From 4808ec76e87652bb3085e8f31a58f831a53033df Mon Sep 17 00:00:00 2001 From: Xiangfu Liu Date: Sun, 16 Aug 2009 13:35:53 +0800 Subject: [PATCH] add-2.6.24-kernel-patch Signed-off-by: Xiangfu Liu --- .../xburst/patches-2.6.24/0001-git-init.patch | 0 .../0002-Ingenic-PATCH-jz-20090226.patch | 185647 +++++++++++++++ .../0003-add-uImage-to-gitignore.patch | 24 + .../0004-first-pi-work-kernel.patch | 3818 + .../0005-fix-timer-callback.patch | 23 + .../xburst/patches-2.6.24/0006-lcd-work.patch | 459 + .../0007-already-init-in-u-boot.patch | 95 + .../0008-add-build-script.patch | 67 + .../0009-format-the-build-shell-script.patch | 42 + .../patches-2.6.24/0010-test-email.patch | 29 + .../0011-add-vmlinux.lds.h-file.patch | 297 + .../0012-fix-make-zImage-erro.path.patch | 39 + .../patches-2.6.24/0013-fix-mmc-driver.patch | 190 + .../0014-add-defautl-command.patch | 2451 + 14 files changed, 193181 insertions(+) create mode 100644 target/linux/xburst/patches-2.6.24/0001-git-init.patch create mode 100644 target/linux/xburst/patches-2.6.24/0002-Ingenic-PATCH-jz-20090226.patch create mode 100644 target/linux/xburst/patches-2.6.24/0003-add-uImage-to-gitignore.patch create mode 100644 target/linux/xburst/patches-2.6.24/0004-first-pi-work-kernel.patch create mode 100644 target/linux/xburst/patches-2.6.24/0005-fix-timer-callback.patch create mode 100644 target/linux/xburst/patches-2.6.24/0006-lcd-work.patch create mode 100644 target/linux/xburst/patches-2.6.24/0007-already-init-in-u-boot.patch create mode 100644 target/linux/xburst/patches-2.6.24/0008-add-build-script.patch create mode 100644 target/linux/xburst/patches-2.6.24/0009-format-the-build-shell-script.patch create mode 100644 target/linux/xburst/patches-2.6.24/0010-test-email.patch create mode 100644 target/linux/xburst/patches-2.6.24/0011-add-vmlinux.lds.h-file.patch create mode 100644 target/linux/xburst/patches-2.6.24/0012-fix-make-zImage-erro.path.patch create mode 100644 target/linux/xburst/patches-2.6.24/0013-fix-mmc-driver.patch create mode 100644 target/linux/xburst/patches-2.6.24/0014-add-defautl-command.patch diff --git a/target/linux/xburst/patches-2.6.24/0001-git-init.patch b/target/linux/xburst/patches-2.6.24/0001-git-init.patch new file mode 100644 index 000000000..e69de29bb diff --git a/target/linux/xburst/patches-2.6.24/0002-Ingenic-PATCH-jz-20090226.patch b/target/linux/xburst/patches-2.6.24/0002-Ingenic-PATCH-jz-20090226.patch new file mode 100644 index 000000000..a4e9cddaa --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0002-Ingenic-PATCH-jz-20090226.patch @@ -0,0 +1,185647 @@ +From 91f38b8f8948f4764a559ab53032bc3ef64eef69 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Tue, 21 Apr 2009 11:30:14 +0800 +Subject: [PATCH] Ingenic PATCH jz-20090226 + +--- + Changelog | 364 ++ + Makefile | 4 +- + README-JZ | 1181 ++++ + arch/mips/Kconfig | 143 + + arch/mips/Makefile | 44 +- + arch/mips/boot/Makefile | 23 +- + arch/mips/boot/compressed/Makefile | 42 + + arch/mips/boot/compressed/dummy.c | 4 + + arch/mips/boot/compressed/head.S | 85 + + arch/mips/boot/compressed/ld.script | 151 + + arch/mips/boot/compressed/misc.c | 242 + + arch/mips/boot/tools/entry | 12 + + arch/mips/boot/tools/filesize | 7 + + arch/mips/configs/apus_defconfig | 1205 ++++ + arch/mips/configs/dipper_defconfig | 1281 ++++ + arch/mips/configs/fuwa_defconfig | 928 +++ + arch/mips/configs/leo_defconfig | 1256 ++++ + arch/mips/configs/lyra_defconfig | 981 +++ + arch/mips/configs/pavo_defconfig | 1293 ++++ + arch/mips/configs/pmp_defconfig | 1212 ++++ + arch/mips/configs/slt50_defconfig | 1036 ++++ + arch/mips/configs/virgo_defconfig | 1281 ++++ + arch/mips/jz4730/Makefile | 22 + + arch/mips/jz4730/board-pmp.c | 109 + + arch/mips/jz4730/cpufreq.c | 596 ++ + arch/mips/jz4730/dma.c | 509 ++ + arch/mips/jz4730/i2c.c | 214 + + arch/mips/jz4730/irq.c | 266 + + arch/mips/jz4730/platform.c | 140 + + arch/mips/jz4730/pm.c | 1098 ++++ + arch/mips/jz4730/proc.c | 292 + + arch/mips/jz4730/prom.c | 198 + + arch/mips/jz4730/reset.c | 40 + + arch/mips/jz4730/setup.c | 182 + + arch/mips/jz4730/sleep.S | 307 + + arch/mips/jz4730/time.c | 129 + + arch/mips/jz4740/Makefile | 26 + + arch/mips/jz4740/board-dipper.c | 117 + + arch/mips/jz4740/board-leo.c | 67 + + arch/mips/jz4740/board-lyra.c | 114 + + arch/mips/jz4740/board-pavo.c | 114 + + arch/mips/jz4740/board-virgo.c | 114 + + arch/mips/jz4740/cpufreq.c | 602 ++ + arch/mips/jz4740/dma.c | 768 +++ + arch/mips/jz4740/i2c.c | 273 + + arch/mips/jz4740/irq.c | 265 + + arch/mips/jz4740/platform.c | 169 + + arch/mips/jz4740/pm.c | 462 ++ + arch/mips/jz4740/proc.c | 887 +++ + arch/mips/jz4740/prom.c | 198 + + arch/mips/jz4740/reset.c | 46 + + arch/mips/jz4740/setup.c | 182 + + arch/mips/jz4740/time.c | 158 + + arch/mips/jz4750/Makefile | 23 + + arch/mips/jz4750/board-apus.c | 64 + + arch/mips/jz4750/board-fuwa.c | 105 + + arch/mips/jz4750/board-slt50.c | 64 + + arch/mips/jz4750/cpufreq.c | 601 ++ + arch/mips/jz4750/dma.c | 836 +++ + arch/mips/jz4750/i2c.c | 273 + + arch/mips/jz4750/irq.c | 299 + + arch/mips/jz4750/platform.c | 141 + + arch/mips/jz4750/pm.c | 461 ++ + arch/mips/jz4750/proc.c | 905 +++ + arch/mips/jz4750/prom.c | 198 + + arch/mips/jz4750/reset.c | 46 + + arch/mips/jz4750/setup.c | 197 + + arch/mips/jz4750/time.c | 156 + + arch/mips/jz4750d/Makefile | 22 + + arch/mips/jz4750d/board-fuwa1.c | 72 + + arch/mips/jz4750d/cpufreq.c | 601 ++ + arch/mips/jz4750d/dma.c | 822 +++ + arch/mips/jz4750d/i2c.c | 273 + + arch/mips/jz4750d/irq.c | 299 + + arch/mips/jz4750d/platform.c | 141 + + arch/mips/jz4750d/pm.c | 461 ++ + arch/mips/jz4750d/proc.c | 877 +++ + arch/mips/jz4750d/prom.c | 198 + + arch/mips/jz4750d/reset.c | 46 + + arch/mips/jz4750d/setup.c | 199 + + arch/mips/jz4750d/time.c | 156 + + arch/mips/kernel/cpu-probe.c | 21 + + arch/mips/mm/c-r4k.c | 30 + + arch/mips/mm/cache.c | 2 + + arch/mips/mm/tlbex.c | 5 + + crypto/Kconfig | 8 + + crypto/Makefile | 1 + + crypto/lzo.c | 106 + + crypto/tcrypt.c | 42 +- + crypto/tcrypt.h | 82 + + drivers/char/Kconfig | 11 + + drivers/char/Makefile | 4 + + drivers/char/jzchar/Kconfig | 70 + + drivers/char/jzchar/Makefile | 24 + + drivers/char/jzchar/ak4182.c | 657 ++ + drivers/char/jzchar/ak4182.h | 16 + + drivers/char/jzchar/ata2508.c | 227 + + drivers/char/jzchar/cim.c | 366 ++ + drivers/char/jzchar/cim.h | 36 + + drivers/char/jzchar/jz_ow.c | 497 ++ + drivers/char/jzchar/jz_ts.c | 443 ++ + drivers/char/jzchar/jz_ts.h | 54 + + drivers/char/jzchar/jz_tssi.c | 457 ++ + drivers/char/jzchar/jz_tssi.h | 76 + + drivers/char/jzchar/jzchars.c | 158 + + drivers/char/jzchar/jzchars.h | 47 + + drivers/char/jzchar/poweroff.c | 383 ++ + drivers/char/jzchar/sadc.c | 580 ++ + drivers/char/jzchar/sensor.c | 182 + + drivers/char/jzchar/tcsm.c | 123 + + drivers/char/jzchar/ucb1400.c | 585 ++ + drivers/char/jzchar/ucb1400.h | 113 + + drivers/char/jzchar/udc_hotplug.c | 451 ++ + drivers/char/jzchar/wm9712.c | 334 + + drivers/char/jzchar/wm9712.h | 58 + + drivers/char/rtc_jz.c | 503 ++ + drivers/char/rtc_jz.h | 74 + + drivers/char/rtc_pcf8563.c | 448 ++ + drivers/i2c/busses/Kconfig | 8 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-jz47xx.c | 330 + + drivers/i2c/busses/i2c-jz47xx.h | 20 + + drivers/i2c/i2c-dev.c | 11 +- + drivers/input/keyboard/Kconfig | 24 +- + drivers/input/keyboard/Makefile | 2 + + drivers/input/keyboard/gpio_keys.c | 175 +- + drivers/input/keyboard/jz_keypad.c | 357 ++ + drivers/input/keyboard/jz_keypad_5x5.c | 329 + + drivers/media/video/Kconfig | 9 + + drivers/media/video/Makefile | 3 + + drivers/media/video/jz_cim.c | 622 ++ + drivers/media/video/jz_cim.h | 36 + + drivers/media/video/jz_sensor.c | 202 + + drivers/mmc/card/block.c | 4 + + drivers/mmc/core/mmc.c | 12 +- + drivers/mmc/core/sd.c | 19 +- + drivers/mmc/host/Kconfig | 98 + + drivers/mmc/host/Makefile | 3 + + drivers/mmc/host/jz4750_mmc.c | 1051 ++++ + drivers/mmc/host/jz4750_mmc.h | 88 + + drivers/mmc/host/jz_mmc.c | 1026 ++++ + drivers/mmc/host/jz_mmc.h | 65 + + drivers/mtd/Makefile | 10 +- + drivers/mtd/mtd-utils | 1 + + drivers/mtd/mtd_blkdevs.c | 2 +- + drivers/mtd/mtdblock-jz.uu | 535 ++ + drivers/mtd/mtdblock.c | 19 +- + drivers/mtd/mtdchar.c | 91 +- + drivers/mtd/mtdcore.c | 6 +- + drivers/mtd/mtdpart.c | 76 +- + drivers/mtd/nand/Kconfig | 125 +- + drivers/mtd/nand/Makefile | 5 +- + drivers/mtd/nand/jz4730_nand.c | 367 ++ + drivers/mtd/nand/jz4740_nand.c | 1037 ++++ + drivers/mtd/nand/jz4750_nand.c | 1746 ++++++ + drivers/mtd/nand/nand_base.c | 402 ++- + drivers/mtd/nand/nand_bbt.c | 50 +- + drivers/mtd/nand/nand_ids.c | 3 + + drivers/mtd/ubi/Kconfig | 14 + + drivers/mtd/ubi/Makefile | 3 + + drivers/mtd/ubi/bdev.c | 432 ++ + drivers/mtd/ubi/build.c | 859 ++- + drivers/mtd/ubi/cdev.c | 310 +- + drivers/mtd/ubi/debug.h | 23 +- + drivers/mtd/ubi/eba.c | 460 +- + drivers/mtd/ubi/gluebi.c | 38 +- + drivers/mtd/ubi/io.c | 44 +- + drivers/mtd/ubi/kapi.c | 315 +- + drivers/mtd/ubi/misc.c | 11 +- + drivers/mtd/ubi/scan.c | 66 +- + drivers/mtd/ubi/scan.h | 2 +- + drivers/mtd/ubi/ubi-media.h | 372 ++ + drivers/mtd/ubi/ubi.h | 183 +- + drivers/mtd/ubi/ubiblk.c | 359 ++ + drivers/mtd/ubi/ubiblk.h | 85 + + drivers/mtd/ubi/upd.c | 208 +- + drivers/mtd/ubi/vmt.c | 210 +- + drivers/mtd/ubi/vtbl.c | 77 +- + drivers/mtd/ubi/wl.c | 338 +- + drivers/mtd/udc_cache.c | 531 ++ + drivers/net/Kconfig | 18 + + drivers/net/Makefile | 2 + + drivers/net/jz_eth.c | 1290 ++++ + drivers/net/jz_eth.h | 403 ++ + drivers/net/jzcs8900a.c | 649 ++ + drivers/net/jzcs8900a.h | 235 + + drivers/pnp/Kconfig | 2 +- + drivers/pnp/core.c | 21 + + drivers/pnp/driver.c | 1 + + drivers/pnp/resource.c | 4 + + drivers/serial/8250.c | 110 +- + drivers/usb/Kconfig | 1 + + drivers/usb/core/hub.c | 26 +- + drivers/usb/gadget/Kconfig | 49 + + drivers/usb/gadget/Makefile | 3 + + drivers/usb/gadget/file_storage.c | 58 +- + drivers/usb/gadget/gadget_chips.h | 16 + + drivers/usb/gadget/jz4730_udc.c | 1403 +++++ + drivers/usb/gadget/jz4730_udc.h | 107 + + drivers/usb/gadget/jz4740_udc.c | 2251 +++++++ + drivers/usb/gadget/jz4740_udc.h | 112 + + drivers/usb/host/ohci-hcd.c | 5 + + drivers/usb/host/ohci-jz.c | 260 + + drivers/video/Kconfig | 216 + + drivers/video/Makefile | 4 + + drivers/video/console/Kconfig | 8 + + drivers/video/console/fbcon.c | 2 + + drivers/video/jz4740_slcd.c | 1334 ++++ + drivers/video/jz4740_slcd.h | 376 ++ + drivers/video/jz4750_lcd.c | 2139 +++++++ + drivers/video/jz4750_lcd.h | 756 +++ + drivers/video/jz4750_tve.c | 104 + + drivers/video/jz4750_tve.h | 45 + + drivers/video/jz_kgm_spfd5420a.h | 382 ++ + drivers/video/jz_toppoly_td043mgeb1.h | 264 + + drivers/video/jzlcd.c | 1571 +++++ + drivers/video/jzlcd.h | 791 +++ + drivers/watchdog/Kconfig | 9 + + drivers/watchdog/Makefile | 3 + + drivers/watchdog/jz_wdt.c | 203 + + fs/Kconfig | 4 + + fs/Makefile | 2 + + fs/fs-writeback.c | 22 +- + fs/ubifs/Kconfig | 71 + + fs/ubifs/Kconfig.debug | 173 + + fs/ubifs/Makefile | 9 + + fs/ubifs/budget.c | 871 +++ + fs/ubifs/build.c | 1423 +++++ + fs/ubifs/commit.c | 718 +++ + fs/ubifs/compat.c | 480 ++ + fs/ubifs/compat.h | 88 + + fs/ubifs/compress.c | 253 + + fs/ubifs/debug.c | 1517 +++++ + fs/ubifs/debug.h | 392 ++ + fs/ubifs/dir.c | 1017 ++++ + fs/ubifs/file.c | 983 +++ + fs/ubifs/find.c | 971 +++ + fs/ubifs/gc.c | 762 +++ + fs/ubifs/io.c | 921 +++ + fs/ubifs/ioctl.c | 204 + + fs/ubifs/journal.c | 1275 ++++ + fs/ubifs/key.h | 566 ++ + fs/ubifs/log.c | 803 +++ + fs/ubifs/lprops.c | 1353 +++++ + fs/ubifs/lpt.c | 2279 +++++++ + fs/ubifs/lpt_commit.c | 1639 +++++ + fs/ubifs/master.c | 387 ++ + fs/ubifs/misc.h | 310 + + fs/ubifs/mkfs.ubifs/.gitignore | 5 + + fs/ubifs/mkfs.ubifs/Makefile | 24 + + fs/ubifs/mkfs.ubifs/compr.c | 154 + + fs/ubifs/mkfs.ubifs/compr.h | 38 + + fs/ubifs/mkfs.ubifs/copying | 340 ++ + fs/ubifs/mkfs.ubifs/crc16.c | 56 + + fs/ubifs/mkfs.ubifs/crc16.h | 27 + + fs/ubifs/mkfs.ubifs/crc32.c | 95 + + fs/ubifs/mkfs.ubifs/crc32.h | 22 + + fs/ubifs/mkfs.ubifs/defs.h | 105 + + fs/ubifs/mkfs.ubifs/devtable.c | 524 ++ + fs/ubifs/mkfs.ubifs/hashtable/hashtable.c | 274 + + fs/ubifs/mkfs.ubifs/hashtable/hashtable.h | 199 + + fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c | 188 + + fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h | 112 + + fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h | 85 + + fs/ubifs/mkfs.ubifs/key.h | 511 ++ + fs/ubifs/mkfs.ubifs/lpt.c | 577 ++ + fs/ubifs/mkfs.ubifs/lpt.h | 28 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h | 92 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h | 92 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h | 156 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h | 156 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h | 104 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h | 173 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h | 141 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h | 146 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h | 88 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h | 135 + + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h | 413 ++ + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h | 1545 +++++ + fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h | 69 + + fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la | 35 + + fs/ubifs/mkfs.ubifs/mkfs.ubifs.c | 2163 +++++++ + fs/ubifs/mkfs.ubifs/mkfs.ubifs.h | 143 + + fs/ubifs/mkfs.ubifs/readme | 28 + + fs/ubifs/mkfs.ubifs/ubifs-media.h | 725 +++ + fs/ubifs/mkfs.ubifs/ubifs.h | 424 ++ + fs/ubifs/orphan.c | 959 +++ + fs/ubifs/recovery.c | 1524 +++++ + fs/ubifs/replay.c | 1049 ++++ + fs/ubifs/sb.c | 617 ++ + fs/ubifs/scan.c | 362 ++ + fs/ubifs/shrinker.c | 322 + + fs/ubifs/super.c | 2016 +++++++ + fs/ubifs/tnc.c | 3300 ++++++++++ + fs/ubifs/tnc_commit.c | 1105 ++++ + fs/ubifs/tnc_misc.c | 259 + + fs/ubifs/ubifs-media.h | 731 +++ + fs/ubifs/ubifs.h | 1589 +++++ + fs/ubifs/xattr.c | 582 ++ + fs/yaffs2/Kconfig | 167 + + fs/yaffs2/Makefile | 10 + + fs/yaffs2/devextras.h | 264 + + fs/yaffs2/moduleconfig.h | 51 + + fs/yaffs2/utils/Makefile | 69 + + fs/yaffs2/utils/README | 11 + + fs/yaffs2/utils/mkyaffs2image.c | 738 +++ + fs/yaffs2/utils/mkyaffsimage.c | 593 ++ + fs/yaffs2/utils/ssfdc_rs_ecc.c | 180 + + fs/yaffs2/utils/ssfdc_rs_ecc.h | 29 + + fs/yaffs2/yaffs_checkptrw.c | 384 ++ + fs/yaffs2/yaffs_checkptrw.h | 33 + + fs/yaffs2/yaffs_ecc.c | 422 ++ + fs/yaffs2/yaffs_ecc.h | 48 + + fs/yaffs2/yaffs_fs.c | 2222 +++++++ + fs/yaffs2/yaffs_guts.c | 6676 +++++++++++++++++++++ + fs/yaffs2/yaffs_guts.h | 889 +++ + fs/yaffs2/yaffs_mtdif.c | 246 + + fs/yaffs2/yaffs_mtdif.h | 27 + + fs/yaffs2/yaffs_mtdif2.c | 369 ++ + fs/yaffs2/yaffs_mtdif2.h | 29 + + fs/yaffs2/yaffs_nand.c | 134 + + fs/yaffs2/yaffs_nand.h | 44 + + fs/yaffs2/yaffs_nandemul2k.h | 39 + + fs/yaffs2/yaffs_packedtags1.c | 52 + + fs/yaffs2/yaffs_packedtags1.h | 37 + + fs/yaffs2/yaffs_packedtags2.c | 184 + + fs/yaffs2/yaffs_packedtags2.h | 38 + + fs/yaffs2/yaffs_qsort.c | 155 + + fs/yaffs2/yaffs_qsort.h | 23 + + fs/yaffs2/yaffs_tagscompat.c | 530 ++ + fs/yaffs2/yaffs_tagscompat.h | 36 + + fs/yaffs2/yaffs_tagsvalidity.c | 28 + + fs/yaffs2/yaffs_tagsvalidity.h | 24 + + fs/yaffs2/yaffsinterface.h | 21 + + fs/yaffs2/yportenv.h | 162 + + include/asm-mips/bootinfo.h | 8 + + include/asm-mips/cpu.h | 12 + + include/asm-mips/jzsoc.h | 49 + + include/asm-mips/mach-generic/irq.h | 2 +- + include/asm-mips/mach-jz4730/board-pmp.h | 83 + + include/asm-mips/mach-jz4730/clock.h | 184 + + include/asm-mips/mach-jz4730/dma.h | 272 + + include/asm-mips/mach-jz4730/jz4730.h | 40 + + include/asm-mips/mach-jz4730/misc.h | 28 + + include/asm-mips/mach-jz4730/ops.h | 2541 ++++++++ + include/asm-mips/mach-jz4730/regs.h | 2550 ++++++++ + include/asm-mips/mach-jz4730/serial.h | 33 + + include/asm-mips/mach-jz4730/war.h | 25 + + include/asm-mips/mach-jz4740/board-dipper.h | 69 + + include/asm-mips/mach-jz4740/board-leo.h | 56 + + include/asm-mips/mach-jz4740/board-lyra.h | 70 + + include/asm-mips/mach-jz4740/board-pavo.h | 70 + + include/asm-mips/mach-jz4740/board-virgo.h | 67 + + include/asm-mips/mach-jz4740/clock.h | 173 + + include/asm-mips/mach-jz4740/dma.h | 265 + + include/asm-mips/mach-jz4740/jz4740.h | 56 + + include/asm-mips/mach-jz4740/misc.h | 43 + + include/asm-mips/mach-jz4740/ops.h | 2224 +++++++ + include/asm-mips/mach-jz4740/regs.h | 2392 ++++++++ + include/asm-mips/mach-jz4740/serial.h | 30 + + include/asm-mips/mach-jz4740/war.h | 25 + + include/asm-mips/mach-jz4750/board-apus.h | 119 + + include/asm-mips/mach-jz4750/board-fuwa.h | 93 + + include/asm-mips/mach-jz4750/clock.h | 204 + + include/asm-mips/mach-jz4750/dma.h | 307 + + include/asm-mips/mach-jz4750/jz4750.h | 44 + + include/asm-mips/mach-jz4750/misc.h | 44 + + include/asm-mips/mach-jz4750/ops.h | 3569 +++++++++++ + include/asm-mips/mach-jz4750/regs.h | 3411 +++++++++++ + include/asm-mips/mach-jz4750/serial.h | 30 + + include/asm-mips/mach-jz4750/war.h | 25 + + include/asm-mips/mach-jz4750d/board-fuwa1.h | 124 + + include/asm-mips/mach-jz4750d/clock.h | 230 + + include/asm-mips/mach-jz4750d/dma.h | 307 + + include/asm-mips/mach-jz4750d/jz4750d.h | 40 + + include/asm-mips/mach-jz4750d/misc.h | 44 + + include/asm-mips/mach-jz4750d/ops.h | 3450 +++++++++++ + include/asm-mips/mach-jz4750d/regs.h | 3398 +++++++++++ + include/asm-mips/mach-jz4750d/serial.h | 30 + + include/asm-mips/mach-jz4750d/war.h | 25 + + include/asm-mips/ptrace.h | 2 +- + include/asm-mips/r4kcache.h | 229 + + include/asm-mips/sizes.h | 56 + + include/linux/fs.h | 2 + + include/linux/i2c-dev.h | 4 +- + include/linux/mmc/host.h | 1 + + include/linux/mtd/mtd.h | 56 +- + include/linux/mtd/nand.h | 28 +- + include/linux/mtd/partitions.h | 5 +- + include/linux/mtd/ubi.h | 18 +- + include/linux/vt.h | 14 +- + include/linux/writeback.h | 1 + + include/mtd/mtd-abi.h | 36 +- + include/mtd/ubi-user.h | 143 +- + include/sound/pcm.h | 26 +- + include/sound/pcm.h.org | 1011 ++++ + init/do_mounts.c | 4 +- + sound/core/pcm_native.c | 15 +- + sound/core/pcm_native.c.org | 3451 +++++++++++ + sound/oss/Kconfig | 148 +- + sound/oss/Makefile | 6 + + sound/oss/ak4642en.c | 712 +++ + sound/oss/jz_ac97.c | 2252 +++++++ + sound/oss/jz_i2s.c | 2894 +++++++++ + sound/oss/jz_i2s_4750.c | 3010 ++++++++++ + sound/oss/jz_i2s_dlv_dma_test.c | 2808 +++++++++ + sound/oss/jz_i2s_for_4750.c | 2981 +++++++++ + sound/oss/jz_pcm_tlv320aic1106_dma.c | 1839 ++++++ + sound/oss/jzcodec.c | 443 ++ + sound/oss/jzdlv.c | 644 ++ + sound/oss/jzdlv.h | 21 + + sound/oss/os.h | 1 - + sound/soc/Kconfig | 1 + + sound/soc/Makefile | 2 +- + sound/soc/codecs/Kconfig | 5 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/jzcodec.c | 725 +++ + sound/soc/codecs/jzcodec.h | 22 + + sound/soc/jz4740/Kconfig | 34 + + sound/soc/jz4740/Makefile | 15 + + sound/soc/jz4740/jz4740-ac97.c | 261 + + sound/soc/jz4740/jz4740-ac97.h | 21 + + sound/soc/jz4740/jz4740-i2s.c | 296 + + sound/soc/jz4740/jz4740-i2s.h | 18 + + sound/soc/jz4740/jz4740-pcm.c | 689 +++ + sound/soc/jz4740/jz4740-pcm.h | 33 + + sound/soc/jz4740/pavo.c | 360 ++ + 427 files changed, 176420 insertions(+), 1242 deletions(-) + create mode 100644 Changelog + create mode 100644 README-JZ + create mode 100644 arch/mips/boot/compressed/Makefile + create mode 100644 arch/mips/boot/compressed/dummy.c + create mode 100644 arch/mips/boot/compressed/head.S + create mode 100644 arch/mips/boot/compressed/ld.script + create mode 100644 arch/mips/boot/compressed/misc.c + create mode 100644 arch/mips/boot/tools/entry + create mode 100644 arch/mips/boot/tools/filesize + create mode 100644 arch/mips/configs/apus_defconfig + create mode 100644 arch/mips/configs/dipper_defconfig + create mode 100644 arch/mips/configs/fuwa_defconfig + create mode 100644 arch/mips/configs/leo_defconfig + create mode 100644 arch/mips/configs/lyra_defconfig + create mode 100644 arch/mips/configs/pavo_defconfig + create mode 100644 arch/mips/configs/pmp_defconfig + create mode 100644 arch/mips/configs/slt50_defconfig + create mode 100644 arch/mips/configs/virgo_defconfig + create mode 100644 arch/mips/jz4730/Makefile + create mode 100644 arch/mips/jz4730/board-pmp.c + create mode 100644 arch/mips/jz4730/cpufreq.c + create mode 100644 arch/mips/jz4730/dma.c + create mode 100644 arch/mips/jz4730/i2c.c + create mode 100644 arch/mips/jz4730/irq.c + create mode 100644 arch/mips/jz4730/platform.c + create mode 100644 arch/mips/jz4730/pm.c + create mode 100644 arch/mips/jz4730/proc.c + create mode 100644 arch/mips/jz4730/prom.c + create mode 100644 arch/mips/jz4730/reset.c + create mode 100644 arch/mips/jz4730/setup.c + create mode 100644 arch/mips/jz4730/sleep.S + create mode 100644 arch/mips/jz4730/time.c + create mode 100644 arch/mips/jz4740/Makefile + create mode 100644 arch/mips/jz4740/board-dipper.c + create mode 100644 arch/mips/jz4740/board-leo.c + create mode 100644 arch/mips/jz4740/board-lyra.c + create mode 100644 arch/mips/jz4740/board-pavo.c + create mode 100644 arch/mips/jz4740/board-virgo.c + create mode 100644 arch/mips/jz4740/cpufreq.c + create mode 100644 arch/mips/jz4740/dma.c + create mode 100644 arch/mips/jz4740/i2c.c + create mode 100644 arch/mips/jz4740/irq.c + create mode 100644 arch/mips/jz4740/platform.c + create mode 100644 arch/mips/jz4740/pm.c + create mode 100644 arch/mips/jz4740/proc.c + create mode 100644 arch/mips/jz4740/prom.c + create mode 100644 arch/mips/jz4740/reset.c + create mode 100644 arch/mips/jz4740/setup.c + create mode 100644 arch/mips/jz4740/time.c + create mode 100644 arch/mips/jz4750/Makefile + create mode 100644 arch/mips/jz4750/board-apus.c + create mode 100644 arch/mips/jz4750/board-fuwa.c + create mode 100644 arch/mips/jz4750/board-slt50.c + create mode 100644 arch/mips/jz4750/cpufreq.c + create mode 100644 arch/mips/jz4750/dma.c + create mode 100644 arch/mips/jz4750/i2c.c + create mode 100644 arch/mips/jz4750/irq.c + create mode 100644 arch/mips/jz4750/platform.c + create mode 100644 arch/mips/jz4750/pm.c + create mode 100644 arch/mips/jz4750/proc.c + create mode 100644 arch/mips/jz4750/prom.c + create mode 100644 arch/mips/jz4750/reset.c + create mode 100644 arch/mips/jz4750/setup.c + create mode 100644 arch/mips/jz4750/time.c + create mode 100644 arch/mips/jz4750d/Makefile + create mode 100644 arch/mips/jz4750d/board-fuwa1.c + create mode 100644 arch/mips/jz4750d/cpufreq.c + create mode 100644 arch/mips/jz4750d/dma.c + create mode 100644 arch/mips/jz4750d/i2c.c + create mode 100644 arch/mips/jz4750d/irq.c + create mode 100644 arch/mips/jz4750d/platform.c + create mode 100644 arch/mips/jz4750d/pm.c + create mode 100644 arch/mips/jz4750d/proc.c + create mode 100644 arch/mips/jz4750d/prom.c + create mode 100644 arch/mips/jz4750d/reset.c + create mode 100644 arch/mips/jz4750d/setup.c + create mode 100644 arch/mips/jz4750d/time.c + create mode 100644 crypto/lzo.c + create mode 100644 drivers/char/jzchar/Kconfig + create mode 100644 drivers/char/jzchar/Makefile + create mode 100644 drivers/char/jzchar/ak4182.c + create mode 100644 drivers/char/jzchar/ak4182.h + create mode 100644 drivers/char/jzchar/ata2508.c + create mode 100644 drivers/char/jzchar/cim.c + create mode 100644 drivers/char/jzchar/cim.h + create mode 100644 drivers/char/jzchar/jz_ow.c + create mode 100644 drivers/char/jzchar/jz_ts.c + create mode 100644 drivers/char/jzchar/jz_ts.h + create mode 100644 drivers/char/jzchar/jz_tssi.c + create mode 100644 drivers/char/jzchar/jz_tssi.h + create mode 100644 drivers/char/jzchar/jzchars.c + create mode 100644 drivers/char/jzchar/jzchars.h + create mode 100644 drivers/char/jzchar/poweroff.c + create mode 100644 drivers/char/jzchar/sadc.c + create mode 100644 drivers/char/jzchar/sensor.c + create mode 100644 drivers/char/jzchar/tcsm.c + create mode 100644 drivers/char/jzchar/ucb1400.c + create mode 100644 drivers/char/jzchar/ucb1400.h + create mode 100644 drivers/char/jzchar/udc_hotplug.c + create mode 100644 drivers/char/jzchar/wm9712.c + create mode 100644 drivers/char/jzchar/wm9712.h + create mode 100644 drivers/char/rtc_jz.c + create mode 100644 drivers/char/rtc_jz.h + create mode 100644 drivers/char/rtc_pcf8563.c + create mode 100644 drivers/i2c/busses/i2c-jz47xx.c + create mode 100644 drivers/i2c/busses/i2c-jz47xx.h + create mode 100644 drivers/input/keyboard/jz_keypad.c + create mode 100644 drivers/input/keyboard/jz_keypad_5x5.c + create mode 100644 drivers/media/video/jz_cim.c + create mode 100644 drivers/media/video/jz_cim.h + create mode 100644 drivers/media/video/jz_sensor.c + create mode 100644 drivers/mmc/host/jz4750_mmc.c + create mode 100644 drivers/mmc/host/jz4750_mmc.h + create mode 100644 drivers/mmc/host/jz_mmc.c + create mode 100644 drivers/mmc/host/jz_mmc.h + create mode 160000 drivers/mtd/mtd-utils + create mode 100644 drivers/mtd/mtdblock-jz.uu + create mode 100644 drivers/mtd/nand/jz4730_nand.c + create mode 100644 drivers/mtd/nand/jz4740_nand.c + create mode 100644 drivers/mtd/nand/jz4750_nand.c + create mode 100644 drivers/mtd/ubi/bdev.c + create mode 100644 drivers/mtd/ubi/ubi-media.h + create mode 100644 drivers/mtd/ubi/ubiblk.c + create mode 100644 drivers/mtd/ubi/ubiblk.h + create mode 100644 drivers/mtd/udc_cache.c + create mode 100644 drivers/net/jz_eth.c + create mode 100644 drivers/net/jz_eth.h + create mode 100644 drivers/net/jzcs8900a.c + create mode 100644 drivers/net/jzcs8900a.h + create mode 100644 drivers/usb/gadget/jz4730_udc.c + create mode 100644 drivers/usb/gadget/jz4730_udc.h + create mode 100644 drivers/usb/gadget/jz4740_udc.c + create mode 100644 drivers/usb/gadget/jz4740_udc.h + create mode 100644 drivers/usb/host/ohci-jz.c + create mode 100644 drivers/video/jz4740_slcd.c + create mode 100644 drivers/video/jz4740_slcd.h + create mode 100644 drivers/video/jz4750_lcd.c + create mode 100644 drivers/video/jz4750_lcd.h + create mode 100644 drivers/video/jz4750_tve.c + create mode 100644 drivers/video/jz4750_tve.h + create mode 100644 drivers/video/jz_kgm_spfd5420a.h + create mode 100644 drivers/video/jz_toppoly_td043mgeb1.h + create mode 100644 drivers/video/jzlcd.c + create mode 100644 drivers/video/jzlcd.h + create mode 100644 drivers/watchdog/jz_wdt.c + create mode 100644 fs/ubifs/Kconfig + create mode 100644 fs/ubifs/Kconfig.debug + create mode 100644 fs/ubifs/Makefile + create mode 100644 fs/ubifs/budget.c + create mode 100644 fs/ubifs/build.c + create mode 100644 fs/ubifs/commit.c + create mode 100644 fs/ubifs/compat.c + create mode 100644 fs/ubifs/compat.h + create mode 100644 fs/ubifs/compress.c + create mode 100644 fs/ubifs/debug.c + create mode 100644 fs/ubifs/debug.h + create mode 100644 fs/ubifs/dir.c + create mode 100644 fs/ubifs/file.c + create mode 100644 fs/ubifs/find.c + create mode 100644 fs/ubifs/gc.c + create mode 100644 fs/ubifs/io.c + create mode 100644 fs/ubifs/ioctl.c + create mode 100644 fs/ubifs/journal.c + create mode 100644 fs/ubifs/key.h + create mode 100644 fs/ubifs/log.c + create mode 100644 fs/ubifs/lprops.c + create mode 100644 fs/ubifs/lpt.c + create mode 100644 fs/ubifs/lpt_commit.c + create mode 100644 fs/ubifs/master.c + create mode 100644 fs/ubifs/misc.h + create mode 100644 fs/ubifs/mkfs.ubifs/.gitignore + create mode 100644 fs/ubifs/mkfs.ubifs/Makefile + create mode 100644 fs/ubifs/mkfs.ubifs/compr.c + create mode 100644 fs/ubifs/mkfs.ubifs/compr.h + create mode 100644 fs/ubifs/mkfs.ubifs/copying + create mode 100644 fs/ubifs/mkfs.ubifs/crc16.c + create mode 100644 fs/ubifs/mkfs.ubifs/crc16.h + create mode 100644 fs/ubifs/mkfs.ubifs/crc32.c + create mode 100644 fs/ubifs/mkfs.ubifs/crc32.h + create mode 100644 fs/ubifs/mkfs.ubifs/defs.h + create mode 100644 fs/ubifs/mkfs.ubifs/devtable.c + create mode 100644 fs/ubifs/mkfs.ubifs/hashtable/hashtable.c + create mode 100644 fs/ubifs/mkfs.ubifs/hashtable/hashtable.h + create mode 100644 fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c + create mode 100644 fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h + create mode 100644 fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h + create mode 100644 fs/ubifs/mkfs.ubifs/key.h + create mode 100644 fs/ubifs/mkfs.ubifs/lpt.c + create mode 100644 fs/ubifs/mkfs.ubifs/lpt.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h + create mode 100644 fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la + create mode 100644 fs/ubifs/mkfs.ubifs/mkfs.ubifs.c + create mode 100644 fs/ubifs/mkfs.ubifs/mkfs.ubifs.h + create mode 100644 fs/ubifs/mkfs.ubifs/readme + create mode 100644 fs/ubifs/mkfs.ubifs/ubifs-media.h + create mode 100644 fs/ubifs/mkfs.ubifs/ubifs.h + create mode 100644 fs/ubifs/orphan.c + create mode 100644 fs/ubifs/recovery.c + create mode 100644 fs/ubifs/replay.c + create mode 100644 fs/ubifs/sb.c + create mode 100644 fs/ubifs/scan.c + create mode 100644 fs/ubifs/shrinker.c + create mode 100644 fs/ubifs/super.c + create mode 100644 fs/ubifs/tnc.c + create mode 100644 fs/ubifs/tnc_commit.c + create mode 100644 fs/ubifs/tnc_misc.c + create mode 100644 fs/ubifs/ubifs-media.h + create mode 100644 fs/ubifs/ubifs.h + create mode 100644 fs/ubifs/xattr.c + create mode 100644 fs/yaffs2/Kconfig + create mode 100644 fs/yaffs2/Makefile + create mode 100644 fs/yaffs2/devextras.h + create mode 100644 fs/yaffs2/moduleconfig.h + create mode 100644 fs/yaffs2/utils/Makefile + create mode 100644 fs/yaffs2/utils/README + create mode 100644 fs/yaffs2/utils/mkyaffs2image.c + create mode 100644 fs/yaffs2/utils/mkyaffsimage.c + create mode 100644 fs/yaffs2/utils/ssfdc_rs_ecc.c + create mode 100644 fs/yaffs2/utils/ssfdc_rs_ecc.h + create mode 100644 fs/yaffs2/yaffs_checkptrw.c + create mode 100644 fs/yaffs2/yaffs_checkptrw.h + create mode 100644 fs/yaffs2/yaffs_ecc.c + create mode 100644 fs/yaffs2/yaffs_ecc.h + create mode 100644 fs/yaffs2/yaffs_fs.c + create mode 100644 fs/yaffs2/yaffs_guts.c + create mode 100644 fs/yaffs2/yaffs_guts.h + create mode 100644 fs/yaffs2/yaffs_mtdif.c + create mode 100644 fs/yaffs2/yaffs_mtdif.h + create mode 100644 fs/yaffs2/yaffs_mtdif2.c + create mode 100644 fs/yaffs2/yaffs_mtdif2.h + create mode 100644 fs/yaffs2/yaffs_nand.c + create mode 100644 fs/yaffs2/yaffs_nand.h + create mode 100644 fs/yaffs2/yaffs_nandemul2k.h + create mode 100644 fs/yaffs2/yaffs_packedtags1.c + create mode 100644 fs/yaffs2/yaffs_packedtags1.h + create mode 100644 fs/yaffs2/yaffs_packedtags2.c + create mode 100644 fs/yaffs2/yaffs_packedtags2.h + create mode 100644 fs/yaffs2/yaffs_qsort.c + create mode 100644 fs/yaffs2/yaffs_qsort.h + create mode 100644 fs/yaffs2/yaffs_tagscompat.c + create mode 100644 fs/yaffs2/yaffs_tagscompat.h + create mode 100644 fs/yaffs2/yaffs_tagsvalidity.c + create mode 100644 fs/yaffs2/yaffs_tagsvalidity.h + create mode 100644 fs/yaffs2/yaffsinterface.h + create mode 100644 fs/yaffs2/yportenv.h + create mode 100644 include/asm-mips/jzsoc.h + create mode 100644 include/asm-mips/mach-jz4730/board-pmp.h + create mode 100644 include/asm-mips/mach-jz4730/clock.h + create mode 100644 include/asm-mips/mach-jz4730/dma.h + create mode 100644 include/asm-mips/mach-jz4730/jz4730.h + create mode 100644 include/asm-mips/mach-jz4730/misc.h + create mode 100644 include/asm-mips/mach-jz4730/ops.h + create mode 100644 include/asm-mips/mach-jz4730/regs.h + create mode 100644 include/asm-mips/mach-jz4730/serial.h + create mode 100644 include/asm-mips/mach-jz4730/war.h + create mode 100644 include/asm-mips/mach-jz4740/board-dipper.h + create mode 100644 include/asm-mips/mach-jz4740/board-leo.h + create mode 100644 include/asm-mips/mach-jz4740/board-lyra.h + create mode 100644 include/asm-mips/mach-jz4740/board-pavo.h + create mode 100644 include/asm-mips/mach-jz4740/board-virgo.h + create mode 100644 include/asm-mips/mach-jz4740/clock.h + create mode 100644 include/asm-mips/mach-jz4740/dma.h + create mode 100644 include/asm-mips/mach-jz4740/jz4740.h + create mode 100644 include/asm-mips/mach-jz4740/misc.h + create mode 100644 include/asm-mips/mach-jz4740/ops.h + create mode 100644 include/asm-mips/mach-jz4740/regs.h + create mode 100644 include/asm-mips/mach-jz4740/serial.h + create mode 100644 include/asm-mips/mach-jz4740/war.h + create mode 100644 include/asm-mips/mach-jz4750/board-apus.h + create mode 100644 include/asm-mips/mach-jz4750/board-fuwa.h + create mode 100644 include/asm-mips/mach-jz4750/clock.h + create mode 100644 include/asm-mips/mach-jz4750/dma.h + create mode 100644 include/asm-mips/mach-jz4750/jz4750.h + create mode 100644 include/asm-mips/mach-jz4750/misc.h + create mode 100644 include/asm-mips/mach-jz4750/ops.h + create mode 100644 include/asm-mips/mach-jz4750/regs.h + create mode 100644 include/asm-mips/mach-jz4750/serial.h + create mode 100644 include/asm-mips/mach-jz4750/war.h + create mode 100644 include/asm-mips/mach-jz4750d/board-fuwa1.h + create mode 100644 include/asm-mips/mach-jz4750d/clock.h + create mode 100644 include/asm-mips/mach-jz4750d/dma.h + create mode 100644 include/asm-mips/mach-jz4750d/jz4750d.h + create mode 100644 include/asm-mips/mach-jz4750d/misc.h + create mode 100644 include/asm-mips/mach-jz4750d/ops.h + create mode 100644 include/asm-mips/mach-jz4750d/regs.h + create mode 100644 include/asm-mips/mach-jz4750d/serial.h + create mode 100644 include/asm-mips/mach-jz4750d/war.h + create mode 100644 include/asm-mips/sizes.h + create mode 100644 include/sound/pcm.h.org + create mode 100644 sound/core/pcm_native.c.org + create mode 100644 sound/oss/ak4642en.c + create mode 100644 sound/oss/jz_ac97.c + create mode 100644 sound/oss/jz_i2s.c + create mode 100644 sound/oss/jz_i2s_4750.c + create mode 100644 sound/oss/jz_i2s_dlv_dma_test.c + create mode 100644 sound/oss/jz_i2s_for_4750.c + create mode 100644 sound/oss/jz_pcm_tlv320aic1106_dma.c + create mode 100644 sound/oss/jzcodec.c + create mode 100644 sound/oss/jzdlv.c + create mode 100644 sound/oss/jzdlv.h + create mode 100644 sound/soc/codecs/jzcodec.c + create mode 100644 sound/soc/codecs/jzcodec.h + create mode 100644 sound/soc/jz4740/Kconfig + create mode 100644 sound/soc/jz4740/Makefile + create mode 100644 sound/soc/jz4740/jz4740-ac97.c + create mode 100644 sound/soc/jz4740/jz4740-ac97.h + create mode 100644 sound/soc/jz4740/jz4740-i2s.c + create mode 100644 sound/soc/jz4740/jz4740-i2s.h + create mode 100644 sound/soc/jz4740/jz4740-pcm.c + create mode 100644 sound/soc/jz4740/jz4740-pcm.h + create mode 100644 sound/soc/jz4740/pavo.c + +diff --git a/Changelog b/Changelog +new file mode 100644 +index 0000000..dea2e77 +--- /dev/null ++++ b/Changelog +@@ -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 --git a/Makefile b/Makefile +index bac9dd0..9f73a2b 100644 +--- a/Makefile ++++ b/Makefile +@@ -190,8 +190,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + # Default value for CROSS_COMPILE is not to prefix executables + # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + +-ARCH ?= $(SUBARCH) +-CROSS_COMPILE ?= ++ARCH ?= mips ++CROSS_COMPILE ?= mipsel-linux- + + # Architecture as present in compile.h + UTS_MACHINE := $(ARCH) +diff --git a/README-JZ b/README-JZ +new file mode 100644 +index 0000000..120e6e1 +--- /dev/null ++++ b/README-JZ +@@ -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 --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index b22c043..3d6eddf 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -16,6 +16,82 @@ choice + 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 @@ source "arch/mips/vr41xx/Kconfig" + + 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 @@ config NR_CPUS + + 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 @@ config BINFMT_ELF32 + + 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 --git a/arch/mips/Makefile b/arch/mips/Makefile +index a1f8d8b..acad988 100644 +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -159,6 +159,37 @@ libs-$(CONFIG_SIBYTE_CFE) += arch/mips/sibyte/cfe/ + # + + # ++# 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/ +@@ -670,6 +701,12 @@ makeboot =$(Q)$(MAKE) $(build)=arch/mips/boot VMLINUX=$(vmlinux-32) $(1) + + all: $(all-y) + ++uImage: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ ++zImage: $(vmlinux-32) ++ +@$(call makeboot,$@) ++ + vmlinux.bin: $(vmlinux-32) + +@$(call makeboot,$@) + +@@ -694,12 +731,13 @@ endif + + 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 --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile +index 2a209d7..1cfce3e 100644 +--- a/arch/mips/boot/Makefile ++++ b/arch/mips/boot/Makefile +@@ -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 @@ strip-flags = $(addprefix --remove-section=,$(drop-sections)) + + 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 @@ vmlinux.srec: $(VMLINUX) + $(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 --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile +new file mode 100644 +index 0000000..9b41329 +--- /dev/null ++++ b/arch/mips/boot/compressed/Makefile +@@ -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 --git a/arch/mips/boot/compressed/dummy.c b/arch/mips/boot/compressed/dummy.c +new file mode 100644 +index 0000000..31dbf45 +--- /dev/null ++++ b/arch/mips/boot/compressed/dummy.c +@@ -0,0 +1,4 @@ ++int main(void) ++{ ++ return 0; ++} +diff --git a/arch/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S +new file mode 100644 +index 0000000..d9700eb +--- /dev/null ++++ b/arch/mips/boot/compressed/head.S +@@ -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 --git a/arch/mips/boot/compressed/ld.script b/arch/mips/boot/compressed/ld.script +new file mode 100644 +index 0000000..fcf8ba0 +--- /dev/null ++++ b/arch/mips/boot/compressed/ld.script +@@ -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 --git a/arch/mips/boot/compressed/misc.c b/arch/mips/boot/compressed/misc.c +new file mode 100644 +index 0000000..2309fee +--- /dev/null ++++ b/arch/mips/boot/compressed/misc.c +@@ -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 --git a/arch/mips/boot/tools/entry b/arch/mips/boot/tools/entry +new file mode 100644 +index 0000000..376e822 +--- /dev/null ++++ b/arch/mips/boot/tools/entry +@@ -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 --git a/arch/mips/boot/tools/filesize b/arch/mips/boot/tools/filesize +new file mode 100644 +index 0000000..2142ad5 +--- /dev/null ++++ b/arch/mips/boot/tools/filesize +@@ -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 --git a/arch/mips/configs/apus_defconfig b/arch/mips/configs/apus_defconfig +new file mode 100644 +index 0000000..2872c36 +--- /dev/null ++++ b/arch/mips/configs/apus_defconfig +@@ -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 --git a/arch/mips/configs/dipper_defconfig b/arch/mips/configs/dipper_defconfig +new file mode 100644 +index 0000000..b046b2c +--- /dev/null ++++ b/arch/mips/configs/dipper_defconfig +@@ -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 --git a/arch/mips/configs/fuwa_defconfig b/arch/mips/configs/fuwa_defconfig +new file mode 100644 +index 0000000..f0bde8f +--- /dev/null ++++ b/arch/mips/configs/fuwa_defconfig +@@ -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 --git a/arch/mips/configs/leo_defconfig b/arch/mips/configs/leo_defconfig +new file mode 100644 +index 0000000..6d5fbdb +--- /dev/null ++++ b/arch/mips/configs/leo_defconfig +@@ -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 --git a/arch/mips/configs/lyra_defconfig b/arch/mips/configs/lyra_defconfig +new file mode 100644 +index 0000000..eeb84d8 +--- /dev/null ++++ b/arch/mips/configs/lyra_defconfig +@@ -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 --git a/arch/mips/configs/pavo_defconfig b/arch/mips/configs/pavo_defconfig +new file mode 100644 +index 0000000..cb13bc0 +--- /dev/null ++++ b/arch/mips/configs/pavo_defconfig +@@ -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 --git a/arch/mips/configs/pmp_defconfig b/arch/mips/configs/pmp_defconfig +new file mode 100644 +index 0000000..85575b4 +--- /dev/null ++++ b/arch/mips/configs/pmp_defconfig +@@ -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 --git a/arch/mips/configs/slt50_defconfig b/arch/mips/configs/slt50_defconfig +new file mode 100644 +index 0000000..53b0521 +--- /dev/null ++++ b/arch/mips/configs/slt50_defconfig +@@ -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 --git a/arch/mips/configs/virgo_defconfig b/arch/mips/configs/virgo_defconfig +new file mode 100644 +index 0000000..a0e02a0 +--- /dev/null ++++ b/arch/mips/configs/virgo_defconfig +@@ -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 --git a/arch/mips/jz4730/Makefile b/arch/mips/jz4730/Makefile +new file mode 100644 +index 0000000..d84b6b6 +--- /dev/null ++++ b/arch/mips/jz4730/Makefile +@@ -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 --git a/arch/mips/jz4730/board-pmp.c b/arch/mips/jz4730/board-pmp.c +new file mode 100644 +index 0000000..00860c1 +--- /dev/null ++++ b/arch/mips/jz4730/board-pmp.c +@@ -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 --git a/arch/mips/jz4730/cpufreq.c b/arch/mips/jz4730/cpufreq.c +new file mode 100644 +index 0000000..1bf4713 +--- /dev/null ++++ b/arch/mips/jz4730/cpufreq.c +@@ -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 --git a/arch/mips/jz4730/dma.c b/arch/mips/jz4730/dma.c +new file mode 100644 +index 0000000..ca7d549 +--- /dev/null ++++ b/arch/mips/jz4730/dma.c +@@ -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 --git a/arch/mips/jz4730/i2c.c b/arch/mips/jz4730/i2c.c +new file mode 100644 +index 0000000..91f55a7 +--- /dev/null ++++ b/arch/mips/jz4730/i2c.c +@@ -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 --git a/arch/mips/jz4730/irq.c b/arch/mips/jz4730/irq.c +new file mode 100644 +index 0000000..658596f +--- /dev/null ++++ b/arch/mips/jz4730/irq.c +@@ -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 --git a/arch/mips/jz4730/pm.c b/arch/mips/jz4730/pm.c +new file mode 100644 +index 0000000..a46e9c3 +--- /dev/null ++++ b/arch/mips/jz4730/pm.c +@@ -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 --git a/arch/mips/jz4730/proc.c b/arch/mips/jz4730/proc.c +new file mode 100644 +index 0000000..49585bc +--- /dev/null ++++ b/arch/mips/jz4730/proc.c +@@ -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 --git a/arch/mips/jz4730/prom.c b/arch/mips/jz4730/prom.c +new file mode 100644 +index 0000000..5a5f6b8 +--- /dev/null ++++ b/arch/mips/jz4730/prom.c +@@ -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 --git a/arch/mips/jz4730/reset.c b/arch/mips/jz4730/reset.c +new file mode 100644 +index 0000000..4c9e20c +--- /dev/null ++++ b/arch/mips/jz4730/reset.c +@@ -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 --git a/arch/mips/jz4730/setup.c b/arch/mips/jz4730/setup.c +new file mode 100644 +index 0000000..4594b56 +--- /dev/null ++++ b/arch/mips/jz4730/setup.c +@@ -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 --git a/arch/mips/jz4730/sleep.S b/arch/mips/jz4730/sleep.S +new file mode 100644 +index 0000000..9ee9e70 +--- /dev/null ++++ b/arch/mips/jz4730/sleep.S +@@ -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 --git a/arch/mips/jz4730/time.c b/arch/mips/jz4730/time.c +new file mode 100644 +index 0000000..806c7fe +--- /dev/null ++++ b/arch/mips/jz4730/time.c +@@ -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 --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile +new file mode 100644 +index 0000000..7592f4e +--- /dev/null ++++ b/arch/mips/jz4740/Makefile +@@ -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 --git a/arch/mips/jz4740/board-dipper.c b/arch/mips/jz4740/board-dipper.c +new file mode 100644 +index 0000000..ca30225 +--- /dev/null ++++ b/arch/mips/jz4740/board-dipper.c +@@ -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 --git a/arch/mips/jz4740/board-leo.c b/arch/mips/jz4740/board-leo.c +new file mode 100644 +index 0000000..912636a +--- /dev/null ++++ b/arch/mips/jz4740/board-leo.c +@@ -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 --git a/arch/mips/jz4740/board-lyra.c b/arch/mips/jz4740/board-lyra.c +new file mode 100644 +index 0000000..ea56626 +--- /dev/null ++++ b/arch/mips/jz4740/board-lyra.c +@@ -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 --git a/arch/mips/jz4740/board-pavo.c b/arch/mips/jz4740/board-pavo.c +new file mode 100644 +index 0000000..e2a5509 +--- /dev/null ++++ b/arch/mips/jz4740/board-pavo.c +@@ -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 --git a/arch/mips/jz4740/board-virgo.c b/arch/mips/jz4740/board-virgo.c +new file mode 100644 +index 0000000..1429877 +--- /dev/null ++++ b/arch/mips/jz4740/board-virgo.c +@@ -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 --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c +new file mode 100644 +index 0000000..d646a1e +--- /dev/null ++++ b/arch/mips/jz4740/cpufreq.c +@@ -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 --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c +new file mode 100644 +index 0000000..dd5055e +--- /dev/null ++++ b/arch/mips/jz4740/dma.c +@@ -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 --git a/arch/mips/jz4740/i2c.c b/arch/mips/jz4740/i2c.c +new file mode 100644 +index 0000000..3080fdf +--- /dev/null ++++ b/arch/mips/jz4740/i2c.c +@@ -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 --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c +new file mode 100644 +index 0000000..98543c2 +--- /dev/null ++++ b/arch/mips/jz4740/irq.c +@@ -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 --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c +new file mode 100644 +index 0000000..2c6cd83 +--- /dev/null ++++ b/arch/mips/jz4740/pm.c +@@ -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 --git a/arch/mips/jz4740/proc.c b/arch/mips/jz4740/proc.c +new file mode 100644 +index 0000000..af48bee +--- /dev/null ++++ b/arch/mips/jz4740/proc.c +@@ -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 --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c +new file mode 100644 +index 0000000..4068939 +--- /dev/null ++++ b/arch/mips/jz4740/prom.c +@@ -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 --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c +new file mode 100644 +index 0000000..83577d8 +--- /dev/null ++++ b/arch/mips/jz4740/reset.c +@@ -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 --git a/arch/mips/jz4740/setup.c b/arch/mips/jz4740/setup.c +new file mode 100644 +index 0000000..e19f767 +--- /dev/null ++++ b/arch/mips/jz4740/setup.c +@@ -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 --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c +new file mode 100644 +index 0000000..a82b17c +--- /dev/null ++++ b/arch/mips/jz4740/time.c +@@ -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 --git a/arch/mips/jz4750/Makefile b/arch/mips/jz4750/Makefile +new file mode 100644 +index 0000000..a9c6c16 +--- /dev/null ++++ b/arch/mips/jz4750/Makefile +@@ -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 --git a/arch/mips/jz4750/board-apus.c b/arch/mips/jz4750/board-apus.c +new file mode 100644 +index 0000000..7347dfa +--- /dev/null ++++ b/arch/mips/jz4750/board-apus.c +@@ -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 --git a/arch/mips/jz4750/board-fuwa.c b/arch/mips/jz4750/board-fuwa.c +new file mode 100644 +index 0000000..c5aae06 +--- /dev/null ++++ b/arch/mips/jz4750/board-fuwa.c +@@ -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 --git a/arch/mips/jz4750/board-slt50.c b/arch/mips/jz4750/board-slt50.c +new file mode 100644 +index 0000000..e8e20b4 +--- /dev/null ++++ b/arch/mips/jz4750/board-slt50.c +@@ -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 --git a/arch/mips/jz4750/cpufreq.c b/arch/mips/jz4750/cpufreq.c +new file mode 100644 +index 0000000..83f98b1 +--- /dev/null ++++ b/arch/mips/jz4750/cpufreq.c +@@ -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 --git a/arch/mips/jz4750/dma.c b/arch/mips/jz4750/dma.c +new file mode 100644 +index 0000000..2508111 +--- /dev/null ++++ b/arch/mips/jz4750/dma.c +@@ -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 --git a/arch/mips/jz4750/i2c.c b/arch/mips/jz4750/i2c.c +new file mode 100644 +index 0000000..cdb1223 +--- /dev/null ++++ b/arch/mips/jz4750/i2c.c +@@ -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 --git a/arch/mips/jz4750/irq.c b/arch/mips/jz4750/irq.c +new file mode 100644 +index 0000000..6a9d867 +--- /dev/null ++++ b/arch/mips/jz4750/irq.c +@@ -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 --git a/arch/mips/jz4750/pm.c b/arch/mips/jz4750/pm.c +new file mode 100644 +index 0000000..74d137f +--- /dev/null ++++ b/arch/mips/jz4750/pm.c +@@ -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 --git a/arch/mips/jz4750/prom.c b/arch/mips/jz4750/prom.c +new file mode 100644 +index 0000000..d04bb3e +--- /dev/null ++++ b/arch/mips/jz4750/prom.c +@@ -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 --git a/arch/mips/jz4750/reset.c b/arch/mips/jz4750/reset.c +new file mode 100644 +index 0000000..90b521e +--- /dev/null ++++ b/arch/mips/jz4750/reset.c +@@ -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 --git a/arch/mips/jz4750/setup.c b/arch/mips/jz4750/setup.c +new file mode 100644 +index 0000000..b9b4240 +--- /dev/null ++++ b/arch/mips/jz4750/setup.c +@@ -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 --git a/arch/mips/jz4750/time.c b/arch/mips/jz4750/time.c +new file mode 100644 +index 0000000..e5d18b5 +--- /dev/null ++++ b/arch/mips/jz4750/time.c +@@ -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 --git a/arch/mips/jz4750d/Makefile b/arch/mips/jz4750d/Makefile +new file mode 100644 +index 0000000..85afce5 +--- /dev/null ++++ b/arch/mips/jz4750d/Makefile +@@ -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 --git a/arch/mips/jz4750d/board-fuwa1.c b/arch/mips/jz4750d/board-fuwa1.c +new file mode 100644 +index 0000000..e9294c3 +--- /dev/null ++++ b/arch/mips/jz4750d/board-fuwa1.c +@@ -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 --git a/arch/mips/jz4750d/cpufreq.c b/arch/mips/jz4750d/cpufreq.c +new file mode 100644 +index 0000000..980c66b +--- /dev/null ++++ b/arch/mips/jz4750d/cpufreq.c +@@ -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 --git a/arch/mips/jz4750d/dma.c b/arch/mips/jz4750d/dma.c +new file mode 100644 +index 0000000..290cc12 +--- /dev/null ++++ b/arch/mips/jz4750d/dma.c +@@ -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 --git a/arch/mips/jz4750d/i2c.c b/arch/mips/jz4750d/i2c.c +new file mode 100644 +index 0000000..c12142f +--- /dev/null ++++ b/arch/mips/jz4750d/i2c.c +@@ -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 --git a/arch/mips/jz4750d/irq.c b/arch/mips/jz4750d/irq.c +new file mode 100644 +index 0000000..aa43d9b +--- /dev/null ++++ b/arch/mips/jz4750d/irq.c +@@ -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 --git a/arch/mips/jz4750d/pm.c b/arch/mips/jz4750d/pm.c +new file mode 100644 +index 0000000..7f58372 +--- /dev/null ++++ b/arch/mips/jz4750d/pm.c +@@ -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 --git a/arch/mips/jz4750d/prom.c b/arch/mips/jz4750d/prom.c +new file mode 100644 +index 0000000..35d39cb +--- /dev/null ++++ b/arch/mips/jz4750d/prom.c +@@ -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 --git a/arch/mips/jz4750d/reset.c b/arch/mips/jz4750d/reset.c +new file mode 100644 +index 0000000..90b521e +--- /dev/null ++++ b/arch/mips/jz4750d/reset.c +@@ -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 --git a/arch/mips/jz4750d/setup.c b/arch/mips/jz4750d/setup.c +new file mode 100644 +index 0000000..ffc50ec +--- /dev/null ++++ b/arch/mips/jz4750d/setup.c +@@ -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 --git a/arch/mips/jz4750d/time.c b/arch/mips/jz4750d/time.c +new file mode 100644 +index 0000000..0c6d647 +--- /dev/null ++++ b/arch/mips/jz4750d/time.c +@@ -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 --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c +index 5c27943..0516923 100644 +--- a/arch/mips/kernel/cpu-probe.c ++++ b/arch/mips/kernel/cpu-probe.c +@@ -160,6 +160,7 @@ static inline void check_wait(void) + 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_broadcom(struct cpuinfo_mips *c) + } + } + ++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 @@ static __init const char *cpu_to_name(struct cpuinfo_mips *c) + 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 @@ __init void cpu_probe(void) + case PRID_COMP_PHILIPS: + cpu_probe_philips(c); + break; ++ case PRID_COMP_INGENIC: ++ cpu_probe_ingenic(c); ++ break; + default: + c->cputype = CPU_UNKNOWN; + } +diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c +index 9355f1c..f99dc49 100644 +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -874,6 +874,36 @@ static void __init probe_pcache(void) + 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 --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c +index 81f30ac..43dde87 100644 +--- a/arch/mips/mm/cache.c ++++ b/arch/mips/mm/cache.c +@@ -47,6 +47,8 @@ void (*_dma_cache_wback)(unsigned long start, unsigned long size); + 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 --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c +index a61246d..a4bc5ce 100644 +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -981,6 +981,11 @@ static __init void build_tlb_write_entry(u32 **p, struct label **l, + 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 --git a/crypto/Kconfig b/crypto/Kconfig +index 083d2e1..d23ea63 100644 +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -502,6 +502,14 @@ config CRYPTO_AUTHENC + 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 --git a/crypto/Makefile b/crypto/Makefile +index 43c2a0d..c861610 100644 +--- a/crypto/Makefile ++++ b/crypto/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_CRYPTO_SEED) += seed.o + 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 --git a/crypto/lzo.c b/crypto/lzo.c +new file mode 100644 +index 0000000..48c3288 +--- /dev/null ++++ b/crypto/lzo.c +@@ -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 --git a/crypto/tcrypt.c b/crypto/tcrypt.c +index 24141fb..bbd6a76 100644 +--- a/crypto/tcrypt.c ++++ b/crypto/tcrypt.c +@@ -78,7 +78,7 @@ static char *check[] = { + "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 @@ out: + 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 @@ static void test_deflate(void) + 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 @@ static void test_deflate(void) + 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 @@ static void do_test(void) + 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 @@ static void do_test(void) + 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 @@ static void do_test(void) + 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 --git a/crypto/tcrypt.h b/crypto/tcrypt.h +index ec86138..a387b19 100644 +--- a/crypto/tcrypt.h ++++ b/crypto/tcrypt.h +@@ -4408,6 +4408,88 @@ static struct comp_testvec deflate_decomp_tv_template[] = { + }; + + /* ++ * 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 --git a/drivers/char/Kconfig b/drivers/char/Kconfig +index 2e3a0d4..6334f86 100644 +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -731,6 +731,16 @@ config RTC + 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 +@@ -1039,6 +1049,7 @@ config DEVPORT + default y + + source "drivers/s390/char/Kconfig" ++source "drivers/char/jzchar/Kconfig" + + endmenu + +diff --git a/drivers/char/Makefile b/drivers/char/Makefile +index 07304d5..8a034e6 100644 +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -97,6 +97,10 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o + 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 --git a/drivers/char/jzchar/Kconfig b/drivers/char/jzchar/Kconfig +new file mode 100644 +index 0000000..4f7ff70 +--- /dev/null ++++ b/drivers/char/jzchar/Kconfig +@@ -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 --git a/drivers/char/jzchar/Makefile b/drivers/char/jzchar/Makefile +new file mode 100644 +index 0000000..b079ffc +--- /dev/null ++++ b/drivers/char/jzchar/Makefile +@@ -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 --git a/drivers/char/jzchar/ak4182.c b/drivers/char/jzchar/ak4182.c +new file mode 100644 +index 0000000..c88aa9a +--- /dev/null ++++ b/drivers/char/jzchar/ak4182.c +@@ -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 --git a/drivers/char/jzchar/ak4182.h b/drivers/char/jzchar/ak4182.h +new file mode 100644 +index 0000000..e8b1bf8 +--- /dev/null ++++ b/drivers/char/jzchar/ak4182.h +@@ -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 --git a/drivers/char/jzchar/ata2508.c b/drivers/char/jzchar/ata2508.c +new file mode 100644 +index 0000000..41a4ff4 +--- /dev/null ++++ b/drivers/char/jzchar/ata2508.c +@@ -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 --git a/drivers/char/jzchar/cim.c b/drivers/char/jzchar/cim.c +new file mode 100644 +index 0000000..c320d67 +--- /dev/null ++++ b/drivers/char/jzchar/cim.c +@@ -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 --git a/drivers/char/jzchar/cim.h b/drivers/char/jzchar/cim.h +new file mode 100644 +index 0000000..c21431b +--- /dev/null ++++ b/drivers/char/jzchar/cim.h +@@ -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 --git a/drivers/char/jzchar/jz_ow.c b/drivers/char/jzchar/jz_ow.c +new file mode 100644 +index 0000000..86b3c00 +--- /dev/null ++++ b/drivers/char/jzchar/jz_ow.c +@@ -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 --git a/drivers/char/jzchar/jz_ts.c b/drivers/char/jzchar/jz_ts.c +new file mode 100644 +index 0000000..16b46a2 +--- /dev/null ++++ b/drivers/char/jzchar/jz_ts.c +@@ -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 --git a/drivers/char/jzchar/jz_ts.h b/drivers/char/jzchar/jz_ts.h +new file mode 100644 +index 0000000..4285797 +--- /dev/null ++++ b/drivers/char/jzchar/jz_ts.h +@@ -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 --git a/drivers/char/jzchar/jz_tssi.c b/drivers/char/jzchar/jz_tssi.c +new file mode 100644 +index 0000000..fe4978a +--- /dev/null ++++ b/drivers/char/jzchar/jz_tssi.c +@@ -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 --git a/drivers/char/jzchar/jz_tssi.h b/drivers/char/jzchar/jz_tssi.h +new file mode 100644 +index 0000000..7ac424d +--- /dev/null ++++ b/drivers/char/jzchar/jz_tssi.h +@@ -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 --git a/drivers/char/jzchar/jzchars.c b/drivers/char/jzchar/jzchars.c +new file mode 100644 +index 0000000..1a1b944 +--- /dev/null ++++ b/drivers/char/jzchar/jzchars.c +@@ -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 --git a/drivers/char/jzchar/jzchars.h b/drivers/char/jzchar/jzchars.h +new file mode 100644 +index 0000000..a76f2ae +--- /dev/null ++++ b/drivers/char/jzchar/jzchars.h +@@ -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 --git a/drivers/char/jzchar/poweroff.c b/drivers/char/jzchar/poweroff.c +new file mode 100644 +index 0000000..66b9f5c +--- /dev/null ++++ b/drivers/char/jzchar/poweroff.c +@@ -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 --git a/drivers/char/jzchar/sadc.c b/drivers/char/jzchar/sadc.c +new file mode 100644 +index 0000000..dc43404 +--- /dev/null ++++ b/drivers/char/jzchar/sadc.c +@@ -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 --git a/drivers/char/jzchar/sensor.c b/drivers/char/jzchar/sensor.c +new file mode 100644 +index 0000000..8123203 +--- /dev/null ++++ b/drivers/char/jzchar/sensor.c +@@ -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 --git a/drivers/char/jzchar/tcsm.c b/drivers/char/jzchar/tcsm.c +new file mode 100644 +index 0000000..d25ddca +--- /dev/null ++++ b/drivers/char/jzchar/tcsm.c +@@ -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 --git a/drivers/char/jzchar/ucb1400.c b/drivers/char/jzchar/ucb1400.c +new file mode 100644 +index 0000000..d5d06f7 +--- /dev/null ++++ b/drivers/char/jzchar/ucb1400.c +@@ -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 --git a/drivers/char/jzchar/ucb1400.h b/drivers/char/jzchar/ucb1400.h +new file mode 100644 +index 0000000..318c004 +--- /dev/null ++++ b/drivers/char/jzchar/ucb1400.h +@@ -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 --git a/drivers/char/jzchar/udc_hotplug.c b/drivers/char/jzchar/udc_hotplug.c +new file mode 100644 +index 0000000..70c38d1 +--- /dev/null ++++ b/drivers/char/jzchar/udc_hotplug.c +@@ -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 --git a/drivers/char/jzchar/wm9712.c b/drivers/char/jzchar/wm9712.c +new file mode 100644 +index 0000000..de75435 +--- /dev/null ++++ b/drivers/char/jzchar/wm9712.c +@@ -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 --git a/drivers/char/jzchar/wm9712.h b/drivers/char/jzchar/wm9712.h +new file mode 100644 +index 0000000..7cd9e5c +--- /dev/null ++++ b/drivers/char/jzchar/wm9712.h +@@ -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 --git a/drivers/char/rtc_jz.c b/drivers/char/rtc_jz.c +new file mode 100644 +index 0000000..55da710 +--- /dev/null ++++ b/drivers/char/rtc_jz.c +@@ -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 --git a/drivers/char/rtc_jz.h b/drivers/char/rtc_jz.h +new file mode 100644 +index 0000000..6c754d6 +--- /dev/null ++++ b/drivers/char/rtc_jz.h +@@ -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 --git a/drivers/char/rtc_pcf8563.c b/drivers/char/rtc_pcf8563.c +new file mode 100644 +index 0000000..08dc30b +--- /dev/null ++++ b/drivers/char/rtc_pcf8563.c +@@ -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 --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index c466c6c..6cd79ca 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -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 --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 81d43c2..98bb392 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -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 --git a/drivers/i2c/busses/i2c-jz47xx.c b/drivers/i2c/busses/i2c-jz47xx.c +new file mode 100644 +index 0000000..16f947e +--- /dev/null ++++ b/drivers/i2c/busses/i2c-jz47xx.c +@@ -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 --git a/drivers/i2c/busses/i2c-jz47xx.h b/drivers/i2c/busses/i2c-jz47xx.h +new file mode 100644 +index 0000000..eb86110 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-jz47xx.h +@@ -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 --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index df540d5..d17f109 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -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 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, + 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 --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +index 086d58c..571453c 100644 +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -259,9 +259,27 @@ config KEYBOARD_AAED2000 + 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 @@ config KEYBOARD_GPIO + 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 --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile +index e97455f..4a47482 100644 +--- a/drivers/input/keyboard/Makefile ++++ b/drivers/input/keyboard/Makefile +@@ -19,6 +19,8 @@ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o + 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 --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c +index 6a9ca4b..741cc89 100644 +--- a/drivers/input/keyboard/gpio_keys.c ++++ b/drivers/input/keyboard/gpio_keys.c +@@ -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 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) + + 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 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) + 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 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) + + 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 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) + 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 @@ static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state) + 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 @@ static int gpio_keys_resume(struct platform_device *pdev) + 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 @@ struct platform_driver gpio_keys_device_driver = { + + 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 --git a/drivers/input/keyboard/jz_keypad.c b/drivers/input/keyboard/jz_keypad.c +new file mode 100644 +index 0000000..3e6e309 +--- /dev/null ++++ b/drivers/input/keyboard/jz_keypad.c +@@ -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 --git a/drivers/input/keyboard/jz_keypad_5x5.c b/drivers/input/keyboard/jz_keypad_5x5.c +new file mode 100644 +index 0000000..eb1502b +--- /dev/null ++++ b/drivers/input/keyboard/jz_keypad_5x5.c +@@ -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 --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index c9f14bf..0d29fe1 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -514,6 +514,15 @@ config VIDEO_VINO + 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 --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index b5a0641..d0c391e 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -15,6 +15,9 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) + 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 --git a/drivers/media/video/jz_cim.c b/drivers/media/video/jz_cim.c +new file mode 100644 +index 0000000..4ea0594 +--- /dev/null ++++ b/drivers/media/video/jz_cim.c +@@ -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 --git a/drivers/media/video/jz_cim.h b/drivers/media/video/jz_cim.h +new file mode 100644 +index 0000000..f72cfa4 +--- /dev/null ++++ b/drivers/media/video/jz_cim.h +@@ -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 --git a/drivers/media/video/jz_sensor.c b/drivers/media/video/jz_sensor.c +new file mode 100644 +index 0000000..2d6005f +--- /dev/null ++++ b/drivers/media/video/jz_sensor.c +@@ -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 --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c +index aeb32a9..a8d0b56 100644 +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -225,7 +225,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) + 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 --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index 68c0e3b..6a0e3da 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -141,8 +141,13 @@ static int mmc_decode_csd(struct mmc_card *card) + + 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 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, + 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 --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c +index d1c1e0f..3c167eb 100644 +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -110,8 +110,13 @@ static int mmc_decode_csd(struct mmc_card *card) + + 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 @@ static int mmc_decode_csd(struct mmc_card *card) + 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 @@ static int mmc_switch_hs(struct mmc_card *card) + 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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + goto free_card; + + mmc_decode_cid(card); ++ ++ /* set 24MHz clock again, why?? */ ++ mmc_set_clock(host, 24000000); + } + + /* +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 5fef678..502ca10 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -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 --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3877c87..b7f52b6 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -6,6 +6,9 @@ ifeq ($(CONFIG_MMC_DEBUG),y) + 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 --git a/drivers/mmc/host/jz4750_mmc.c b/drivers/mmc/host/jz4750_mmc.c +new file mode 100644 +index 0000000..94004fa +--- /dev/null ++++ b/drivers/mmc/host/jz4750_mmc.c +@@ -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 --git a/drivers/mmc/host/jz4750_mmc.h b/drivers/mmc/host/jz4750_mmc.h +new file mode 100644 +index 0000000..8577db6 +--- /dev/null ++++ b/drivers/mmc/host/jz4750_mmc.h +@@ -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 --git a/drivers/mmc/host/jz_mmc.c b/drivers/mmc/host/jz_mmc.c +new file mode 100644 +index 0000000..29ab9bb +--- /dev/null ++++ b/drivers/mmc/host/jz_mmc.c +@@ -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 --git a/drivers/mmc/host/jz_mmc.h b/drivers/mmc/host/jz_mmc.h +new file mode 100644 +index 0000000..c733529 +--- /dev/null ++++ b/drivers/mmc/host/jz_mmc.h +@@ -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 --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index 7f0b04b..94e8509 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -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 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + # '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 @@ inftl-objs := inftlcore.o inftlmount.o + 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 --git a/drivers/mtd/mtd-utils b/drivers/mtd/mtd-utils +new file mode 160000 +index 0000000..d5f5e57 +--- /dev/null ++++ b/drivers/mtd/mtd-utils +@@ -0,0 +1 @@ ++Subproject commit d5f5e57b72289bb5f551a4e17be0132aa8dfb6c4 +diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c +index 74d9d30..f9ffd1a 100644 +--- a/drivers/mtd/mtd_blkdevs.c ++++ b/drivers/mtd/mtd_blkdevs.c +@@ -278,7 +278,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) + + /* 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 --git a/drivers/mtd/mtdblock-jz.uu b/drivers/mtd/mtdblock-jz.uu +new file mode 100644 +index 0000000..9a4838a +--- /dev/null ++++ b/drivers/mtd/mtdblock-jz.uu +@@ -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 @@ static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) + 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 --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c +index 22ed96c..03d9305 100644 +--- a/drivers/mtd/mtdchar.c ++++ b/drivers/mtd/mtdchar.c +@@ -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 +@@ -82,8 +81,6 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) + return -EINVAL; + } + +- +- + static int mtd_open(struct inode *inode, struct file *file) + { + int minor = iminor(inode); +@@ -136,8 +133,7 @@ static int mtd_close(struct inode *inode, struct file *file) + + 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); +@@ -156,7 +152,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t + { + 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; +@@ -250,7 +246,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count + 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; +@@ -535,7 +531,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, + { + struct mtd_oob_buf buf; + struct mtd_oob_ops ops; +- ++ + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) + return -EFAULT; + +@@ -575,6 +571,73 @@ static int mtd_ioctl(struct inode *inode, struct file *file, + 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; +@@ -626,9 +689,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, + + 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; +@@ -639,9 +702,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, + + 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; +@@ -756,9 +819,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, + } + + default: ++ printk("line : %d\n", __LINE__); + ret = -ENOTTY; + } +- + return ret; + } /* memory_ioctl */ + +diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c +index 6c2645e..715e21d 100644 +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -303,10 +303,10 @@ void put_mtd_device(struct mtd_info *mtd) + */ + + 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 @@ static inline int mtd_proc_info (char *buf, int i) + 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 --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c +index 6174a97..ffe98c9 100644 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $ ++ * $Id: mtdpart.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $ + * + * 02-21-2002 Thomas Gleixner + * added support for read_oob, write_oob +@@ -28,7 +28,7 @@ static LIST_HEAD(mtd_partitions); + 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; +@@ -46,8 +46,8 @@ struct mtd_part { + * 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; +@@ -67,8 +67,8 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, + 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) +@@ -79,14 +79,14 @@ static int part_point (struct mtd_info *mtd, loff_t from, size_t len, + 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); +@@ -107,8 +107,8 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, + 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, +@@ -116,14 +116,14 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le + } + + 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, +@@ -131,14 +131,14 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le + } + + 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)) +@@ -151,7 +151,7 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, + 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); +@@ -166,22 +166,22 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, + 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)) +@@ -222,7 +222,7 @@ void mtd_erase_callback(struct erase_info *instr) + } + 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) +@@ -230,7 +230,7 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) + 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) +@@ -256,7 +256,7 @@ static void part_resume(struct mtd_info *mtd) + 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) +@@ -265,7 +265,7 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) + 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; +@@ -320,10 +320,10 @@ int add_mtd_partitions(struct mtd_info *master, + int nbparts) + { + struct mtd_part *slave; +- u_int32_t cur_offset = 0; ++ u_int64_t cur_offset = 0; + int i; + +- printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); ++ //printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + + for (i = 0; i < nbparts; i++) { + +@@ -351,6 +351,7 @@ int add_mtd_partitions(struct mtd_info *master, + + slave->mtd.read = part_read; + slave->mtd.write = part_write; ++ slave->mtd.priv = master->priv; //add by Nancy + + if(master->point && master->unpoint){ + slave->mtd.point = part_point; +@@ -398,11 +399,12 @@ int add_mtd_partitions(struct mtd_info *master, + slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + slave->offset = cur_offset; +- if ((cur_offset % master->erasesize) != 0) { ++ if (((u32)cur_offset % master->erasesize) != 0) { + /* Round up to next erasesize */ +- slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; ++ //slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; ++ slave->offset = ((cur_offset >> (ffs(master->erasesize)-1)) + 1) * master->erasesize; + printk(KERN_NOTICE "Moving partition %d: " +- "0x%08x -> 0x%08x\n", i, ++ "0x%09llx -> 0x%09llx\n", i, + cur_offset, slave->offset); + } + } +@@ -410,7 +412,7 @@ int add_mtd_partitions(struct mtd_info *master, + slave->mtd.size = master->size - slave->offset; + cur_offset = slave->offset + slave->mtd.size; + +- printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, ++ printk (KERN_NOTICE "0x%09llx-0x%09llx : \"%s\"\n", slave->offset, + slave->offset + slave->mtd.size, slave->mtd.name); + + /* let's do some sanity checks */ +@@ -423,7 +425,7 @@ int add_mtd_partitions(struct mtd_info *master, + } + if (slave->offset + slave->mtd.size > master->size) { + slave->mtd.size = master->size - slave->offset; +- printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", ++ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", + parts[i].name, master->name, slave->mtd.size); + } + if (master->numeraseregions>1) { +@@ -446,7 +448,7 @@ int add_mtd_partitions(struct mtd_info *master, + } + + if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->offset % slave->mtd.erasesize)) { ++ ((u32)slave->offset % slave->mtd.erasesize)) { + /* Doesn't start on a boundary of major erase size */ + /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ + slave->mtd.flags &= ~MTD_WRITEABLE; +@@ -454,7 +456,7 @@ int add_mtd_partitions(struct mtd_info *master, + parts[i].name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->mtd.size % slave->mtd.erasesize)) { ++ ((u32)slave->mtd.size % slave->mtd.erasesize)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + parts[i].name); +@@ -462,7 +464,7 @@ int add_mtd_partitions(struct mtd_info *master, + + slave->mtd.ecclayout = master->ecclayout; + if (master->block_isbad) { +- uint32_t offs = 0; ++ uint64_t offs = 0; + + while(offs < slave->mtd.size) { + if (master->block_isbad(master, +@@ -560,3 +562,7 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types, + EXPORT_SYMBOL_GPL(parse_mtd_partitions); + EXPORT_SYMBOL_GPL(register_mtd_parser); + EXPORT_SYMBOL_GPL(deregister_mtd_parser); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nicolas Pitre "); ++MODULE_DESCRIPTION("Generic support for partitioning of MTD devices"); +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 246d451..5369ed9 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -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 @@ config MTD_ALAUDA + 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 --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 3ad6c01..c4100b7 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -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_CM_X270) += cmx270_nand.o + 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 --git a/drivers/mtd/nand/jz4730_nand.c b/drivers/mtd/nand/jz4730_nand.c +new file mode 100644 +index 0000000..f3e57f7 +--- /dev/null ++++ b/drivers/mtd/nand/jz4730_nand.c +@@ -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 --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c +new file mode 100644 +index 0000000..ccd4ed6 +--- /dev/null ++++ b/drivers/mtd/nand/jz4740_nand.c +@@ -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 --git a/drivers/mtd/nand/jz4750_nand.c b/drivers/mtd/nand/jz4750_nand.c +new file mode 100644 +index 0000000..4d6b06e +--- /dev/null ++++ b/drivers/mtd/nand/jz4750_nand.c +@@ -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 --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index e29c1da..39095c3 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -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_16 = { + }; + + 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 @@ static struct nand_ecclayout nand_oob_64 = { + .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 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + 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 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) + 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 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) + * + * 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 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) + 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 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) + * 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 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) + */ + 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 @@ static int nand_check_wp(struct mtd_info *mtd) + * 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 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + + /* 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 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + 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 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) + 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 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + 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 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + 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 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + * @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 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + 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 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + 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 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + * + * 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 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + 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 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + 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 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + * + * 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 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, + * + * 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 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + * + * 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 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + { + 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 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + 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 int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + 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 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + 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 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + 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 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + * + * 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 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + * + * 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 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + * + * 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 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + * @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 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + /* + * 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 @@ static void nand_sync(struct mtd_info *mtd) + * @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 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) + * @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 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + 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 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + + 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 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + 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_ident(struct mtd_info *mtd, int maxchips) + */ + 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 @@ int nand_scan_tail(struct mtd_info *mtd) + 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 @@ int nand_scan_tail(struct mtd_info *mtd) + 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 @@ int nand_scan_tail(struct mtd_info *mtd) + 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 --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c +index 5e121ce..dd78703 100644 +--- a/drivers/mtd/nand/nand_bbt.c ++++ b/drivers/mtd/nand/nand_bbt.c +@@ -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 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + { + 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 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc + /* + * 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 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + /* + * 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 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + * 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 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + * 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 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + 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 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + * 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 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + 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 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + + 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 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr + 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 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + 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 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + + 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 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + 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 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) + * + * 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 @@ int nand_default_bbt(struct mtd_info *mtd) + * @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 --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c +index a3e3ab0..1d19e8c 100644 +--- a/drivers/mtd/nand/nand_ids.c ++++ b/drivers/mtd/nand/nand_ids.c +@@ -111,6 +111,9 @@ struct nand_flash_dev nand_flash_ids[] = { + {"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 --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig +index b9daf15..8a8fc75 100644 +--- a/drivers/mtd/ubi/Kconfig ++++ b/drivers/mtd/ubi/Kconfig +@@ -55,4 +55,18 @@ config MTD_UBI_GLUEBI + 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 --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile +index dd834e0..659bbbb 100644 +--- a/drivers/mtd/ubi/Makefile ++++ b/drivers/mtd/ubi/Makefile +@@ -5,3 +5,6 @@ ubi-y += misc.o + + 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 --git a/drivers/mtd/ubi/bdev.c b/drivers/mtd/ubi/bdev.c +new file mode 100644 +index 0000000..cde4e4d +--- /dev/null ++++ b/drivers/mtd/ubi/bdev.c +@@ -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 --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c +index 0236539..72aa949 100644 +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -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 @@ static int mtd_devs = 0; + /* 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 @@ static struct device_attribute dev_min_io_size = + __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 @@ static int ubi_sysfs_init(struct ubi_device *ubi) + 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 @@ out: + */ + 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 @@ static void kill_volumes(struct ubi_device *ubi) + + 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 @@ static int uif_init(struct ubi_device *ubi) + 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 @@ static int uif_init(struct ubi_device *ubi) + 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 @@ static void uif_close(struct ubi_device *ubi) + 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 @@ static int attach_by_scanning(struct ubi_device *ubi) + 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 @@ out_si: + * 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 @@ static int io_init(struct ubi_device *ubi) + 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 @@ static int io_init(struct ubi_device *ubi) + + /* 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 @@ static int io_init(struct ubi_device *ubi) + } + + /* 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 @@ static int io_init(struct ubi_device *ubi) + 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 @@ static int io_init(struct ubi_device *ubi) + } + + /** +- * 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 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + 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 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + 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 @@ static int __init ubi_init(void) + 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 @@ static int __init bytes_str_to_int(const char *str) + + 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 @@ static int __init bytes_str_to_int(const char *str) + 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 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) + 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 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) + 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 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) + + 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 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) + + 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 --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c +index fe4da1e..5b3f62b 100644 +--- a/drivers/mtd/ubi/cdev.c ++++ b/drivers/mtd/ubi/cdev.c +@@ -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 void revoke_exclusive(struct ubi_volume_desc *desc, int mode) + 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 @@ static int vol_cdev_open(struct inode *inode, struct file *file) + + 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 @@ static int vol_cdev_release(struct inode *inode, struct file *file) + 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 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + 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 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + 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 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + 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 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + 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 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + 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 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + 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 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + 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 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + 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 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + 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 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + 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 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + revoke_exclusive(desc, UBI_READWRITE); + } + +- *offp += count; + return count; + } + +@@ -416,6 +436,47 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, + break; + } + +- if (desc->mode == UBI_READONLY) { ++ if (desc->mode == UBI_READONLY || ++ vol->vol_type == UBI_STATIC_VOLUME) { + err = -EROFS; + break; + } +@@ -477,13 +574,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + 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 --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h +index 467722e..718740a 100644 +--- a/drivers/mtd/ubi/debug.h ++++ b/drivers/mtd/ubi/debug.h +@@ -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 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); + + #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 --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c +index 880fa36..e9d2a8a 100644 +--- a/drivers/mtd/ubi/eba.c ++++ b/drivers/mtd/ubi/eba.c +@@ -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 unsigned long long next_sqnum(struct ubi_device *ubi) + */ + 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 @@ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) + * @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 @@ static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + * 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 @@ static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, + 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 @@ static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, + 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 struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, + */ + 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 int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) + 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 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) + + up_read(&le->mutex); + if (free) +- kmem_cache_free(ltree_slab, le); ++ kfree(le); + } + + /** +@@ -273,7 +252,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) + */ + 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 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) + } + + /** ++ * 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 int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) + 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 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) + + 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 @@ out_unlock: + /** + * 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 @@ out_unlock: + * 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 @@ write_error: + /** + * 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 @@ write_error: + * @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 @@ int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, + 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 @@ retry: + 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 @@ write_error: + /** + * 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 @@ write_error: + * @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 @@ write_error: + * 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 @@ write_error: + /* + * 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 @@ write_error: + * 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 @@ write_error: + } + + /** +- * 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 @@ static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) + * + * 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 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + 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 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + + 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 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + 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 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + 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 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + 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 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + } + } + ++ 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 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ out_free: + continue; + kfree(ubi->volumes[i]->eba_tbl); + } +- if (ubi_devices_cnt == 0) +- kmem_cache_destroy(ltree_slab); + return err; + } + +@@ -1222,6 +1249,141 @@ void ubi_eba_close(const struct ubi_device *ubi) + 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 --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c +index 41ff74c..18c6a02 100644 +--- a/drivers/mtd/ubi/gluebi.c ++++ b/drivers/mtd/ubi/gluebi.c +@@ -103,15 +103,15 @@ static void gluebi_put_device(struct mtd_info *mtd) + * 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 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + 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 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + * 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 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + 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 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + 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 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) + 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 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) + 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 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) + 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 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) + /* + * 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 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) + 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 --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c +index 7c304ee..314d6e7 100644 +--- a/drivers/mtd/ubi/io.c ++++ b/drivers/mtd/ubi/io.c +@@ -135,8 +135,8 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + 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 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + 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 @@ retry: + + 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 ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + 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 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + 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 @@ retry: + 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 @@ int ubi_io_is_bad(const struct ubi_device *ubi, int pnum) + 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 @@ int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum) + 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 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, + + 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 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, + + 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 @@ exit: + 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 --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c +index 03c774f..5265b52 100644 +--- a/drivers/mtd/ubi/kapi.c ++++ b/drivers/mtd/ubi/kapi.c +@@ -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 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, + 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 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) + + 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 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) + 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 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) + } + 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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + 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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + 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 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + } + 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 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + 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 @@ void ubi_close_volume(struct ubi_volume_desc *desc) + 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 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + 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 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + 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 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + 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 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) + { + 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 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) + 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 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) + { + 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 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) + 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 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) + 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 --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c +index 9e2338c..115ace8 100644 +--- a/drivers/mtd/ubi/misc.c ++++ b/drivers/mtd/ubi/misc.c +@@ -67,7 +67,12 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) + 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 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) + 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 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) + } + } + ++#if defined(CONFIG_MTD_NAND_DMA) && !defined(CONFIG_MTD_NAND_DMABUF) ++ kfree(buf); ++#else + vfree(buf); ++#endif + return err; + } + +diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c +index c7b0afc..19f4601 100644 +--- a/drivers/mtd/ubi/scan.c ++++ b/drivers/mtd/ubi/scan.c +@@ -42,6 +42,7 @@ + + #include + #include ++#include + #include "ubi.h" + + #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +@@ -92,27 +93,6 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, + } + + /** +- * 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 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, + * 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 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, + /* 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 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, + 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 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, + 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 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, + */ + 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 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum + } + + 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 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum + + 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 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) + + 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 --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h +index 46d444a..966b9b6 100644 +--- a/drivers/mtd/ubi/scan.h ++++ b/drivers/mtd/ubi/scan.h +@@ -124,7 +124,7 @@ struct ubi_scan_info { + int max_ec; + unsigned long long max_sqnum; + int mean_ec; +- int ec_sum; ++ uint64_t ec_sum; + int ec_count; + }; + +diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h +new file mode 100644 +index 0000000..c3185d9 +--- /dev/null ++++ b/drivers/mtd/ubi/ubi-media.h +@@ -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 --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h +index 5e941a6..8a4a87e 100644 +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -37,10 +37,9 @@ + #include + #include + #include +- +-#include + #include + ++#include "ubi-media.h" + #include "scan.h" + #include "debug.h" + +@@ -94,8 +93,43 @@ enum { + 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 @@ struct ubi_volume_desc; + * @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 @@ struct ubi_volume_desc; + * @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 ubi_volume { + 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 @@ struct ubi_volume { + 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_wl_entry; + + /** + * 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 @@ struct ubi_wl_entry; + * @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 @@ struct ubi_wl_entry; + * @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 ubi_device { + 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 @@ struct ubi_device { + 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_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); + 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 @@ void ubi_gluebi_updated(struct ubi_volume *vol); + #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_flush(struct ubi_device *ubi); + 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 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, + 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 int ubi_io_write_data(struct ubi_device *ubi, const void *buf, + */ + 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 --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c +new file mode 100644 +index 0000000..c9df0f0 +--- /dev/null ++++ b/drivers/mtd/ubi/ubiblk.c +@@ -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 --git a/drivers/mtd/ubi/ubiblk.h b/drivers/mtd/ubi/ubiblk.h +new file mode 100644 +index 0000000..5bf4f99 +--- /dev/null ++++ b/drivers/mtd/ubi/ubiblk.h +@@ -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 --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c +index 0efc586..4b1ff94 100644 +--- a/drivers/mtd/ubi/upd.c ++++ b/drivers/mtd/ubi/upd.c +@@ -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 @@ static int set_update_marker(struct ubi_device *ubi, int vol_id) + /** + * 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 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt + 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 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt + /** + * 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 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) + 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 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) + } + + /** ++ * 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 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) + * 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 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + * 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 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + * @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 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + * 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 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + 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 --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c +index 88629a3..5be58d8 100644 +--- a/drivers/mtd/ubi/vmt.c ++++ b/drivers/mtd/ubi/vmt.c +@@ -63,21 +63,30 @@ static struct device_attribute attr_vol_upd_marker = + * 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 @@ static ssize_t vol_attribute_show(struct device *dev, + 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 ssize_t vol_attribute_show(struct device *dev, + 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 @@ static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) + 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 @@ static void volume_sysfs_close(struct ubi_volume *vol) + * @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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + } + 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + /* 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + + 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 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) + 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 @@ out_acc: + 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 @@ out_sysfs: + * + * 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 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) + 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 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) + 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 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) + * @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 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) + + 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 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) + 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 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) + + 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 @@ out_free: + /** + * 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 @@ int ubi_add_volume(struct ubi_device *ubi, int vol_id) + + 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 @@ out_cdev: + /** + * 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 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) + 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 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) + + 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 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) + 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 @@ static void paranoid_check_volumes(struct ubi_device *ubi) + { + 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 --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c +index 25b3bd6..2d751b3 100644 +--- a/drivers/mtd/ubi/vtbl.c ++++ b/drivers/mtd/ubi/vtbl.c +@@ -86,8 +86,10 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + { + 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 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + 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 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si, + * 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 @@ retry: + } + + 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 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, + + /* 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 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, + } + + /* 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 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, + 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 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, + 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 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, + + 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 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + 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 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + 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 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + 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 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c +index 6330c8c..a471a49 100644 +--- a/drivers/mtd/ubi/wl.c ++++ b/drivers/mtd/ubi/wl.c +@@ -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 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + #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 @@ static int do_work(struct ubi_device *ubi) + 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 @@ static int do_work(struct ubi_device *ubi) + 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 @@ retry: + * 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 @@ static void prot_tree_del(struct ubi_device *ubi, int pnum) + 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 @@ static void prot_tree_del(struct ubi_device *ubi, int pnum) + 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 schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + */ + 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + * 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + * 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 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + */ + 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); + +- yield(); ++ 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); ++ ++ 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 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + 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 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + + 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 @@ out_ro: + } + + /** +- * 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 @@ out_ro: + * 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 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) + 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 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) + * 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 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) + } 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 @@ retry: + 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 @@ retry: + */ + 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 @@ static void tree_destroy(struct rb_root *root) + rb->rb_right = NULL; + } + +- kmem_cache_free(wl_entries_slab, e); ++ kmem_cache_free(ubi_wl_entry_slab, e); + } + } + } +@@ -1303,7 +1355,7 @@ static void tree_destroy(struct rb_root *root) + * 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) + 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 @@ out_free: + 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 @@ static void protection_trees_destroy(struct ubi_device *ubi) + 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 @@ static void protection_trees_destroy(struct ubi_device *ubi) + */ + 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 @@ void ubi_wl_close(struct ubi_device *ubi) + 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 --git a/drivers/mtd/udc_cache.c b/drivers/mtd/udc_cache.c +new file mode 100644 +index 0000000..6cb5eb4 +--- /dev/null ++++ b/drivers/mtd/udc_cache.c +@@ -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 --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index 9af05a2..49bc0a3 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -210,6 +210,24 @@ config MII + 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 --git a/drivers/net/Makefile b/drivers/net/Makefile +index 0e5fde4..a4ee38e 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -84,6 +84,8 @@ obj-$(CONFIG_RIONET) += rionet.o + 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 --git a/drivers/net/jz_eth.c b/drivers/net/jz_eth.c +new file mode 100644 +index 0000000..ade9556 +--- /dev/null ++++ b/drivers/net/jz_eth.c +@@ -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 --git a/drivers/net/jz_eth.h b/drivers/net/jz_eth.h +new file mode 100644 +index 0000000..cb8486e +--- /dev/null ++++ b/drivers/net/jz_eth.h +@@ -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 --git a/drivers/net/jzcs8900a.c b/drivers/net/jzcs8900a.c +new file mode 100644 +index 0000000..db345d0 +--- /dev/null ++++ b/drivers/net/jzcs8900a.c +@@ -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 --git a/drivers/net/jzcs8900a.h b/drivers/net/jzcs8900a.h +new file mode 100644 +index 0000000..68d76bb +--- /dev/null ++++ b/drivers/net/jzcs8900a.h +@@ -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 --git a/drivers/pnp/Kconfig b/drivers/pnp/Kconfig +index 821933f..589f4b2 100644 +--- a/drivers/pnp/Kconfig ++++ b/drivers/pnp/Kconfig +@@ -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 --git a/drivers/pnp/core.c b/drivers/pnp/core.c +index 7d366ca..79600ee 100644 +--- a/drivers/pnp/core.c ++++ b/drivers/pnp/core.c +@@ -74,6 +74,8 @@ int pnp_register_protocol(struct pnp_protocol *protocol) + 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 @@ void pnp_unregister_protocol(struct pnp_protocol *protocol) + 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 @@ int pnp_add_device(struct pnp_dev *dev) + return 0; + } + ++EXPORT_SYMBOL(pnp_add_device); ++ + void __pnp_remove_device(struct pnp_dev *dev) + { + spin_lock(&pnp_lock); +@@ -175,6 +181,21 @@ void __pnp_remove_device(struct pnp_dev *dev) + 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 --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c +index a262762..001a53d 100644 +--- a/drivers/pnp/driver.c ++++ b/drivers/pnp/driver.c +@@ -246,3 +246,4 @@ EXPORT_SYMBOL(pnp_register_driver); + EXPORT_SYMBOL(pnp_unregister_driver); + EXPORT_SYMBOL(pnp_device_attach); + EXPORT_SYMBOL(pnp_device_detach); ++EXPORT_SYMBOL(pnp_add_id); +diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c +index e50ebcf..f23e714 100644 +--- a/drivers/pnp/resource.c ++++ b/drivers/pnp/resource.c +@@ -404,6 +404,10 @@ int pnp_check_irq(struct pnp_dev *dev, int idx) + + 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 --git a/drivers/serial/8250.c b/drivers/serial/8250.c +index f94109c..2e4d882 100644 +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -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 @@ static const struct serial8250_config uart_config[] = { + [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 @@ serial_out(struct uart_8250_port *up, int offset, int value) + 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 @@ static void serial8250_shutdown(struct uart_port *port) + 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 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int + + return quot; + } ++#endif + + static void + serial8250_set_termios(struct uart_port *port, struct ktermios *termios, +@@ -2009,6 +2091,9 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + 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 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + * 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 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + 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 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + 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 @@ static int __init serial8250_init(void) + 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_suspend_port); + 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 --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig +index 7580aa5..0f40ddc 100644 +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -41,6 +41,7 @@ config USB_ARCH_HAS_OHCI + default y if PPC_MPC52xx + # MIPS: + default y if SOC_AU1X00 ++ default y if JZSOC + # more: + default PCI + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 1b17f63..06b3ca4 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -2484,11 +2484,35 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, + 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 --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index f81d08d..e5fda35 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -93,6 +93,47 @@ choice + 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 @@ config USB_FILE_STORAGE_TEST + 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 --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 904e57b..8a08dad 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -17,6 +17,9 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o + 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 --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c +index 1d174dc..6c055d8 100644 +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -274,6 +274,18 @@ MODULE_LICENSE("Dual BSD/GPL"); + + + /*-------------------------------------------------------------------------*/ ++#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 @@ static struct { + } 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 @@ static void put_be32(u8 *buf, u32 val) + #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 @@ static struct usb_string strings[] = { + {STRING_SERIAL, serial}, + {STRING_CONFIG, "Self-powered"}, + {STRING_INTERFACE, "Mass Storage"}, ++ {STRING_MS_OS, "Microsoft"}, + {} + }; + +@@ -1618,9 +1632,14 @@ static int do_read(struct fsg_dev *fsg) + + /* 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 @@ static int do_write(struct fsg_dev *fsg) + + /* 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 @@ static int do_verify(struct fsg_dev *fsg) + + /* 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 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) + { + 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 @@ static int do_scsi_command(struct fsg_dev *fsg) + 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 @@ static int fsg_main_thread(void *fsg_) + + /* 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 @@ static int open_backing_file(struct lun *curlun, const char *filename) + 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 --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h +index f7f159c..3988e7d 100644 +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -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 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) + 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 --git a/drivers/usb/gadget/jz4730_udc.c b/drivers/usb/gadget/jz4730_udc.c +new file mode 100644 +index 0000000..fb182a6 +--- /dev/null ++++ b/drivers/usb/gadget/jz4730_udc.c +@@ -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 --git a/drivers/usb/gadget/jz4730_udc.h b/drivers/usb/gadget/jz4730_udc.h +new file mode 100644 +index 0000000..6ea368b +--- /dev/null ++++ b/drivers/usb/gadget/jz4730_udc.h +@@ -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 --git a/drivers/usb/gadget/jz4740_udc.c b/drivers/usb/gadget/jz4740_udc.c +new file mode 100644 +index 0000000..04320f1 +--- /dev/null ++++ b/drivers/usb/gadget/jz4740_udc.c +@@ -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 --git a/drivers/usb/gadget/jz4740_udc.h b/drivers/usb/gadget/jz4740_udc.h +new file mode 100644 +index 0000000..ee642b4 +--- /dev/null ++++ b/drivers/usb/gadget/jz4740_udc.h +@@ -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 --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c +index ecfe800..41c8f8f 100644 +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -977,6 +977,11 @@ MODULE_LICENSE ("GPL"); + #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 --git a/drivers/usb/host/ohci-jz.c b/drivers/usb/host/ohci-jz.c +new file mode 100644 +index 0000000..9604728 +--- /dev/null ++++ b/drivers/usb/host/ohci-jz.c +@@ -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 --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 5b3dbcf..929044d 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -201,6 +201,222 @@ config FB_TILEBLITTING + 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 --git a/drivers/video/Makefile b/drivers/video/Makefile +index 83e02b3..a5c914d 100644 +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -28,6 +28,10 @@ obj-$(CONFIG_FB_DDC) += fb_ddc.o + 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 --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig +index b87ed37..efbf1e5 100644 +--- a/drivers/video/console/Kconfig ++++ b/drivers/video/console/Kconfig +@@ -134,6 +134,14 @@ config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY + + 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 --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c +index 0f32f4a..481dc20 100644 +--- a/drivers/video/console/fbcon.c ++++ b/drivers/video/console/fbcon.c +@@ -399,6 +399,7 @@ static void fbcon_update_softback(struct vc_data *vc) + + 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 @@ static void fb_flashcursor(struct work_struct *work) + 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 --git a/drivers/video/jz4740_slcd.c b/drivers/video/jz4740_slcd.c +new file mode 100644 +index 0000000..41f0928 +--- /dev/null ++++ b/drivers/video/jz4740_slcd.c +@@ -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 --git a/drivers/video/jz4740_slcd.h b/drivers/video/jz4740_slcd.h +new file mode 100644 +index 0000000..84754f6 +--- /dev/null ++++ b/drivers/video/jz4740_slcd.h +@@ -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 --git a/drivers/video/jz4750_lcd.c b/drivers/video/jz4750_lcd.c +new file mode 100644 +index 0000000..96f8997 +--- /dev/null ++++ b/drivers/video/jz4750_lcd.c +@@ -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 --git a/drivers/video/jz4750_lcd.h b/drivers/video/jz4750_lcd.h +new file mode 100644 +index 0000000..1b0a15c +--- /dev/null ++++ b/drivers/video/jz4750_lcd.h +@@ -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 --git a/drivers/video/jz4750_tve.c b/drivers/video/jz4750_tve.c +new file mode 100644 +index 0000000..3a4ea61 +--- /dev/null ++++ b/drivers/video/jz4750_tve.c +@@ -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 --git a/drivers/video/jz4750_tve.h b/drivers/video/jz4750_tve.h +new file mode 100644 +index 0000000..00eefe9 +--- /dev/null ++++ b/drivers/video/jz4750_tve.h +@@ -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 --git a/drivers/video/jz_kgm_spfd5420a.h b/drivers/video/jz_kgm_spfd5420a.h +new file mode 100644 +index 0000000..bf674b0 +--- /dev/null ++++ b/drivers/video/jz_kgm_spfd5420a.h +@@ -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 --git a/drivers/video/jz_toppoly_td043mgeb1.h b/drivers/video/jz_toppoly_td043mgeb1.h +new file mode 100644 +index 0000000..c66494e +--- /dev/null ++++ b/drivers/video/jz_toppoly_td043mgeb1.h +@@ -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 --git a/drivers/video/jzlcd.c b/drivers/video/jzlcd.c +new file mode 100644 +index 0000000..bb461fd +--- /dev/null ++++ b/drivers/video/jzlcd.c +@@ -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 --git a/drivers/video/jzlcd.h b/drivers/video/jzlcd.h +new file mode 100644 +index 0000000..3676b9b +--- /dev/null ++++ b/drivers/video/jzlcd.h +@@ -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 --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 52dff40..04259f1 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -45,6 +45,15 @@ comment "Watchdog Device Drivers" + + # 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 --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index 87483cc..63dcd8a 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -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 --git a/drivers/watchdog/jz_wdt.c b/drivers/watchdog/jz_wdt.c +new file mode 100644 +index 0000000..593bb87 +--- /dev/null ++++ b/drivers/watchdog/jz_wdt.c +@@ -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 --git a/fs/Kconfig b/fs/Kconfig +index 781b47d..d1e833a 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -1385,6 +1385,9 @@ config JFFS2_CMODE_FAVOURLZO + + endchoice + ++# UBIFS File system configuration ++source "fs/ubifs/Kconfig" ++ + config CRAMFS + tristate "Compressed ROM file system support (cramfs)" + depends on BLOCK +@@ -2128,6 +2131,7 @@ endif + + source "fs/nls/Kconfig" + source "fs/dlm/Kconfig" ++source "fs/yaffs2/Kconfig" + + endmenu + +diff --git a/fs/Makefile b/fs/Makefile +index 500cf15..d15ebcf 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -99,6 +99,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs/ + obj-$(CONFIG_UFS_FS) += ufs/ + obj-$(CONFIG_EFS_FS) += efs/ + obj-$(CONFIG_JFFS2_FS) += jffs2/ ++obj-$(CONFIG_UBIFS_FS) += ubifs/ + obj-$(CONFIG_AFFS_FS) += affs/ + obj-$(CONFIG_ROMFS_FS) += romfs/ + obj-$(CONFIG_QNX4FS_FS) += qnx4/ +@@ -118,3 +119,4 @@ obj-$(CONFIG_HPPFS) += hppfs/ + obj-$(CONFIG_DEBUG_FS) += debugfs/ + obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_GFS2_FS) += gfs2/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c +index 300324b..ceeb9b3 100644 +--- a/fs/fs-writeback.c ++++ b/fs/fs-writeback.c +@@ -386,8 +386,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) + * 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 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) + * 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 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) + 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 @@ restart: + * 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 @@ void sync_inodes_sb(struct super_block *sb, int wait) + (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 --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig +new file mode 100644 +index 0000000..2300e7d +--- /dev/null ++++ b/fs/ubifs/Kconfig +@@ -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 --git a/fs/ubifs/Kconfig.debug b/fs/ubifs/Kconfig.debug +new file mode 100644 +index 0000000..3251032 +--- /dev/null ++++ b/fs/ubifs/Kconfig.debug +@@ -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 --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile +new file mode 100644 +index 0000000..f08c7f7 +--- /dev/null ++++ b/fs/ubifs/Makefile +@@ -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 --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c +new file mode 100644 +index 0000000..f66a6b1 +--- /dev/null ++++ b/fs/ubifs/budget.c +@@ -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 --git a/fs/ubifs/build.c b/fs/ubifs/build.c +new file mode 100644 +index 0000000..f6fb3e1 +--- /dev/null ++++ b/fs/ubifs/build.c +@@ -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 --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c +new file mode 100644 +index 0000000..950396f +--- /dev/null ++++ b/fs/ubifs/commit.c +@@ -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 --git a/fs/ubifs/compat.c b/fs/ubifs/compat.c +new file mode 100644 +index 0000000..509cdc6 +--- /dev/null ++++ b/fs/ubifs/compat.c +@@ -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 --git a/fs/ubifs/compat.h b/fs/ubifs/compat.h +new file mode 100644 +index 0000000..72ca52d +--- /dev/null ++++ b/fs/ubifs/compat.h +@@ -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 --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c +new file mode 100644 +index 0000000..5bb51da +--- /dev/null ++++ b/fs/ubifs/compress.c +@@ -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 --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c +new file mode 100644 +index 0000000..216d652 +--- /dev/null ++++ b/fs/ubifs/debug.c +@@ -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 --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h +new file mode 100644 +index 0000000..7e780fe +--- /dev/null ++++ b/fs/ubifs/debug.h +@@ -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 --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c +new file mode 100644 +index 0000000..65504bd +--- /dev/null ++++ b/fs/ubifs/dir.c +@@ -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 --git a/fs/ubifs/file.c b/fs/ubifs/file.c +new file mode 100644 +index 0000000..42783d3 +--- /dev/null ++++ b/fs/ubifs/file.c +@@ -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 --git a/fs/ubifs/find.c b/fs/ubifs/find.c +new file mode 100644 +index 0000000..b16d8f2 +--- /dev/null ++++ b/fs/ubifs/find.c +@@ -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 --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c +new file mode 100644 +index 0000000..6d238aa +--- /dev/null ++++ b/fs/ubifs/gc.c +@@ -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 --git a/fs/ubifs/io.c b/fs/ubifs/io.c +new file mode 100644 +index 0000000..4a73cde +--- /dev/null ++++ b/fs/ubifs/io.c +@@ -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 --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c +new file mode 100644 +index 0000000..aef2164 +--- /dev/null ++++ b/fs/ubifs/ioctl.c +@@ -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 --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c +new file mode 100644 +index 0000000..d844ac0 +--- /dev/null ++++ b/fs/ubifs/journal.c +@@ -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 --git a/fs/ubifs/key.h b/fs/ubifs/key.h +new file mode 100644 +index 0000000..2dfc32d +--- /dev/null ++++ b/fs/ubifs/key.h +@@ -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 --git a/fs/ubifs/log.c b/fs/ubifs/log.c +new file mode 100644 +index 0000000..4ee6643 +--- /dev/null ++++ b/fs/ubifs/log.c +@@ -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 --git a/fs/ubifs/lprops.c b/fs/ubifs/lprops.c +new file mode 100644 +index 0000000..e874537 +--- /dev/null ++++ b/fs/ubifs/lprops.c +@@ -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 --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c +new file mode 100644 +index 0000000..a9a6185 +--- /dev/null ++++ b/fs/ubifs/lpt.c +@@ -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 --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c +new file mode 100644 +index 0000000..b4f7c71 +--- /dev/null ++++ b/fs/ubifs/lpt_commit.c +@@ -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 --git a/fs/ubifs/master.c b/fs/ubifs/master.c +new file mode 100644 +index 0000000..d9dfc95 +--- /dev/null ++++ b/fs/ubifs/master.c +@@ -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 --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h +new file mode 100644 +index 0000000..ad0b495 +--- /dev/null ++++ b/fs/ubifs/misc.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/.gitignore b/fs/ubifs/mkfs.ubifs/.gitignore +new file mode 100644 +index 0000000..d2a803d +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/.gitignore +@@ -0,0 +1,5 @@ ++*.o ++mkfs.ubifs ++patches ++cscope.out ++.pc +diff --git a/fs/ubifs/mkfs.ubifs/Makefile b/fs/ubifs/mkfs.ubifs/Makefile +new file mode 100644 +index 0000000..0cb1154 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/Makefile +@@ -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 --git a/fs/ubifs/mkfs.ubifs/compr.c b/fs/ubifs/mkfs.ubifs/compr.c +new file mode 100644 +index 0000000..285354e +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/compr.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/compr.h b/fs/ubifs/mkfs.ubifs/compr.h +new file mode 100644 +index 0000000..c76a5b9 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/compr.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/copying b/fs/ubifs/mkfs.ubifs/copying +new file mode 100644 +index 0000000..60549be +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/copying +@@ -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 --git a/fs/ubifs/mkfs.ubifs/crc16.c b/fs/ubifs/mkfs.ubifs/crc16.c +new file mode 100644 +index 0000000..a19512e +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/crc16.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/crc16.h b/fs/ubifs/mkfs.ubifs/crc16.h +new file mode 100644 +index 0000000..539d21a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/crc16.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/crc32.c b/fs/ubifs/mkfs.ubifs/crc32.c +new file mode 100644 +index 0000000..6b1e50c +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/crc32.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/crc32.h b/fs/ubifs/mkfs.ubifs/crc32.h +new file mode 100644 +index 0000000..4b51177 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/crc32.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/defs.h b/fs/ubifs/mkfs.ubifs/defs.h +new file mode 100644 +index 0000000..6151cd0 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/defs.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/devtable.c b/fs/ubifs/mkfs.ubifs/devtable.c +new file mode 100644 +index 0000000..3f38339 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/devtable.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c b/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c +new file mode 100644 +index 0000000..763357e +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/hashtable/hashtable.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h b/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h +new file mode 100644 +index 0000000..b90781a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/hashtable/hashtable.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c +new file mode 100644 +index 0000000..5dced84 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h +new file mode 100644 +index 0000000..eea699a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_itr.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h +new file mode 100644 +index 0000000..3e95f60 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/hashtable/hashtable_private.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/key.h b/fs/ubifs/mkfs.ubifs/key.h +new file mode 100644 +index 0000000..c243592 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/key.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lpt.c b/fs/ubifs/mkfs.ubifs/lpt.c +new file mode 100644 +index 0000000..48ae03f +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lpt.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lpt.h b/fs/ubifs/mkfs.ubifs/lpt.h +new file mode 100644 +index 0000000..4cde59d +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lpt.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h +new file mode 100644 +index 0000000..ff6b5cb +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h +new file mode 100644 +index 0000000..5cf23d1 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1a.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h +new file mode 100644 +index 0000000..8423179 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1b.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h +new file mode 100644 +index 0000000..a5f6782 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1c.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h +new file mode 100644 +index 0000000..836c159 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1f.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h +new file mode 100644 +index 0000000..9118f7b +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1x.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h +new file mode 100644 +index 0000000..27e197a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1y.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h +new file mode 100644 +index 0000000..277464a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo1z.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h +new file mode 100644 +index 0000000..a331d00 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo2a.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h +new file mode 100644 +index 0000000..7c8100e +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzo_asm.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h +new file mode 100644 +index 0000000..7a745ad +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoconf.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h +new file mode 100644 +index 0000000..59d18a7 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzodefs.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h +new file mode 100644 +index 0000000..0babba8 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/include/lzo/lzoutil.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la b/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la +new file mode 100644 +index 0000000..b14de39 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/lzo/lib/liblzo2.la +@@ -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 --git a/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c b/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c +new file mode 100644 +index 0000000..710abb0 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/mkfs.ubifs.c +@@ -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 --git a/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h b/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h +new file mode 100644 +index 0000000..641a734 +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/mkfs.ubifs.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/readme b/fs/ubifs/mkfs.ubifs/readme +new file mode 100644 +index 0000000..acf615b +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/readme +@@ -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 --git a/fs/ubifs/mkfs.ubifs/ubifs-media.h b/fs/ubifs/mkfs.ubifs/ubifs-media.h +new file mode 100644 +index 0000000..fd056dd +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/ubifs-media.h +@@ -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 --git a/fs/ubifs/mkfs.ubifs/ubifs.h b/fs/ubifs/mkfs.ubifs/ubifs.h +new file mode 100644 +index 0000000..140451a +--- /dev/null ++++ b/fs/ubifs/mkfs.ubifs/ubifs.h +@@ -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 --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c +new file mode 100644 +index 0000000..e4c3b08 +--- /dev/null ++++ b/fs/ubifs/orphan.c +@@ -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 --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c +new file mode 100644 +index 0000000..ef342c1 +--- /dev/null ++++ b/fs/ubifs/recovery.c +@@ -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 --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c +new file mode 100644 +index 0000000..b927d43 +--- /dev/null ++++ b/fs/ubifs/replay.c +@@ -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 --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c +new file mode 100644 +index 0000000..ab1110d +--- /dev/null ++++ b/fs/ubifs/sb.c +@@ -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 --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c +new file mode 100644 +index 0000000..acf5c5f +--- /dev/null ++++ b/fs/ubifs/scan.c +@@ -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 --git a/fs/ubifs/shrinker.c b/fs/ubifs/shrinker.c +new file mode 100644 +index 0000000..b100ed9 +--- /dev/null ++++ b/fs/ubifs/shrinker.c +@@ -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 --git a/fs/ubifs/super.c b/fs/ubifs/super.c +new file mode 100644 +index 0000000..10468d0 +--- /dev/null ++++ b/fs/ubifs/super.c +@@ -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 --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c +new file mode 100644 +index 0000000..20be8ba +--- /dev/null ++++ b/fs/ubifs/tnc.c +@@ -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 --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c +new file mode 100644 +index 0000000..9ef90bc +--- /dev/null ++++ b/fs/ubifs/tnc_commit.c +@@ -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 --git a/fs/ubifs/tnc_misc.c b/fs/ubifs/tnc_misc.c +new file mode 100644 +index 0000000..cb61007 +--- /dev/null ++++ b/fs/ubifs/tnc_misc.c +@@ -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 --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h +new file mode 100644 +index 0000000..788bdd8 +--- /dev/null ++++ b/fs/ubifs/ubifs-media.h +@@ -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 --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h +new file mode 100644 +index 0000000..5d38b84 +--- /dev/null ++++ b/fs/ubifs/ubifs.h +@@ -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 --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c +new file mode 100644 +index 0000000..5d1b03d +--- /dev/null ++++ b/fs/ubifs/xattr.c +@@ -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 --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig +new file mode 100644 +index 0000000..8cf5e76 +--- /dev/null ++++ b/fs/yaffs2/Kconfig +@@ -0,0 +1,167 @@ ++# ++# YAFFS file system configurations ++# ++ ++menu "Yaffs2 Filesystems" ++ ++config YAFFS_FS ++ tristate "YAFFS2 file system support" ++ default n ++ depends on MTD ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ 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. ++ ++ If unsure, say N. ++ ++ Further information on YAFFS2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable YAFFS1 support -- yaffs for 512 byte / page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_DOES_ECC ++ bool "Lets Yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 ++ default n ++ help ++ This enables Yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++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 ++ 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. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ 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 ++ default y ++ 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. ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_LAZY_LOAD ++ bool "Disable lazy loading" ++ depends on YAFFS_YAFFS2 ++ default n ++ help ++ "Lazy loading" defers loading file details until they are ++ required. This saves mount time, but makes the first look-up ++ a bit longer. ++ ++ Lazy loading will only happen if enabled by this option being 'n' ++ and if the appropriate tags are available, else yaffs2 will ++ automatically fall back to immediate loading and do the right ++ thing. ++ ++ Lazy laoding will be required by checkpointing. ++ ++ Setting this to 'y' will disable lazy loading. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_WIDE_TNODES ++ bool "Turn off wide tnodes" ++ depends on YAFFS_FS ++ default n ++ help ++ 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 make ++ large arrays slower. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_CHUNK_ERASED_CHECK ++ bool "Turn off debug chunk erase check" ++ depends on YAFFS_FS ++ default y ++ help ++ 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. ++ ++config YAFFS_SHORT_NAMES_IN_RAM ++ bool "Cache short names in RAM" ++ depends on YAFFS_FS ++ default y ++ help ++ If this config is set, then short names are stored with the ++ yaffs_Object. This costs an extra 16 bytes of RAM per object, ++ 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 --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile +new file mode 100644 +index 0000000..bf7f54a +--- /dev/null ++++ b/fs/yaffs2/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the linux YAFFS2 filesystem routines. ++# ++ ++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 --git a/fs/yaffs2/devextras.h b/fs/yaffs2/devextras.h +new file mode 100644 +index 0000000..9635c7a +--- /dev/null ++++ b/fs/yaffs2/devextras.h +@@ -0,0 +1,264 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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. ++ */ ++ ++/* ++ * 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 ++ * applications. ++ * ++ */ ++ ++#ifndef __EXTRAS_H__ ++#define __EXTRAS_H__ ++ ++#if defined WIN32 ++#define __inline__ __inline ++#define new newHack ++#endif ++ ++#if !(defined __KERNEL__) || (defined WIN32) ++ ++/* User space defines */ ++ ++typedef unsigned char __u8; ++typedef unsigned short __u16; ++typedef unsigned __u32; ++ ++/* ++ * Simple doubly linked list implementation. ++ * ++ * Some of the internal functions ("__xxx") are useful when ++ * manipulating whole lists rather than single entries, as ++ * sometimes we already know the next/prev entries and we can ++ * generate better code by using them directly rather than ++ * using the generic single-entry routines. ++ */ ++ ++#define prefetch(x) 1 ++ ++struct list_head { ++ struct list_head *next, *prev; ++}; ++ ++#define LIST_HEAD_INIT(name) { &(name), &(name) } ++ ++#define LIST_HEAD(name) \ ++ struct list_head name = LIST_HEAD_INIT(name) ++ ++#define INIT_LIST_HEAD(ptr) do { \ ++ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ ++} while (0) ++ ++/* ++ * Insert a new entry between two known consecutive entries. ++ * ++ * This is only for internal list manipulation where we know ++ * the prev/next entries already! ++ */ ++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; ++} ++ ++/** ++ * list_add - add a new entry ++ * @new: new entry to be added ++ * @head: list head to add it after ++ * ++ * Insert a new entry after the specified head. ++ * This is good for implementing stacks. ++ */ ++static __inline__ void list_add(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head, head->next); ++} ++ ++/** ++ * list_add_tail - add a new entry ++ * @new: new entry to be added ++ * @head: list head to add it before ++ * ++ * Insert a new entry before the specified head. ++ * This is useful for implementing queues. ++ */ ++static __inline__ void list_add_tail(struct list_head *new, ++ struct list_head *head) ++{ ++ __list_add(new, head->prev, head); ++} ++ ++/* ++ * Delete a list entry by making the prev/next entries ++ * point to each other. ++ * ++ * This is only for internal list manipulation where we know ++ * the prev/next entries already! ++ */ ++static __inline__ void __list_del(struct list_head *prev, ++ struct list_head *next) ++{ ++ next->prev = prev; ++ prev->next = next; ++} ++ ++/** ++ * list_del - deletes entry from list. ++ * @entry: the element to delete from the list. ++ * Note: list_empty on entry does not return true after this, the entry is ++ * in an undefined state. ++ */ ++static __inline__ void list_del(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++} ++ ++/** ++ * list_del_init - deletes entry from list and reinitialize it. ++ * @entry: the element to delete from the list. ++ */ ++static __inline__ void list_del_init(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ INIT_LIST_HEAD(entry); ++} ++ ++/** ++ * list_empty - tests whether a list is empty ++ * @head: the list to test. ++ */ ++static __inline__ int list_empty(struct list_head *head) ++{ ++ return head->next == head; ++} ++ ++/** ++ * list_splice - join two lists ++ * @list: the new list to add. ++ * @head: the place to add it in the first list. ++ */ ++static __inline__ void list_splice(struct list_head *list, ++ struct list_head *head) ++{ ++ struct list_head *first = list->next; ++ ++ if (first != list) { ++ struct list_head *last = list->prev; ++ struct list_head *at = head->next; ++ ++ first->prev = head; ++ head->next = first; ++ ++ last->next = at; ++ at->prev = last; ++ } ++} ++ ++/** ++ * list_entry - get the struct for this entry ++ * @ptr: the &struct list_head pointer. ++ * @type: the type of the struct this is embedded in. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_entry(ptr, type, member) \ ++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) ++ ++/** ++ * list_for_each - iterate over a list ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ */ ++#define list_for_each(pos, head) \ ++ for (pos = (head)->next, prefetch(pos->next); pos != (head); \ ++ pos = pos->next, prefetch(pos->next)) ++ ++/** ++ * list_for_each_safe - iterate over a list safe against removal ++ * of list entry ++ * @pos: the &struct list_head to use as a loop counter. ++ * @n: another &struct list_head to use as temporary storage ++ * @head: the head for your list. ++ */ ++#define list_for_each_safe(pos, n, head) \ ++ for (pos = (head)->next, n = pos->next; pos != (head); \ ++ pos = n, n = pos->next) ++ ++/* ++ * File types ++ */ ++#define DT_UNKNOWN 0 ++#define DT_FIFO 1 ++#define DT_CHR 2 ++#define DT_DIR 4 ++#define DT_BLK 6 ++#define DT_REG 8 ++#define DT_LNK 10 ++#define DT_SOCK 12 ++#define DT_WHT 14 ++ ++#ifndef WIN32 ++#include ++#endif ++ ++/* ++ * Attribute flags. These should be or-ed together to figure out what ++ * has been changed! ++ */ ++#define ATTR_MODE 1 ++#define ATTR_UID 2 ++#define ATTR_GID 4 ++#define ATTR_SIZE 8 ++#define ATTR_ATIME 16 ++#define ATTR_MTIME 32 ++#define ATTR_CTIME 64 ++#define ATTR_ATIME_SET 128 ++#define ATTR_MTIME_SET 256 ++#define ATTR_FORCE 512 /* Not a change, but a change it */ ++#define ATTR_ATTR_FLAG 1024 ++ ++struct iattr { ++ unsigned int ia_valid; ++ unsigned ia_mode; ++ unsigned ia_uid; ++ unsigned ia_gid; ++ unsigned ia_size; ++ unsigned ia_atime; ++ unsigned ia_mtime; ++ unsigned ia_ctime; ++ unsigned int ia_attr_flags; ++}; ++ ++#define KERN_DEBUG ++ ++#else ++ ++#ifndef WIN32 ++#include ++#include ++#include ++#include ++#endif ++ ++#endif ++ ++#if defined WIN32 ++#undef new ++#endif ++ ++#endif +diff --git a/fs/yaffs2/moduleconfig.h b/fs/yaffs2/moduleconfig.h +new file mode 100644 +index 0000000..b51a150 +--- /dev/null ++++ b/fs/yaffs2/moduleconfig.h +@@ -0,0 +1,51 @@ ++/* ++ * 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 ++ * ++ * 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_CONFIG_H__ ++#define __YAFFS_CONFIG_H__ ++ ++#ifdef YAFFS_OUT_OF_TREE ++ ++/* DO NOT UNSET THESE THREE. YAFFS2 will not compile if you do. */ ++#define CONFIG_YAFFS_FS ++#define CONFIG_YAFFS_YAFFS1 ++#define CONFIG_YAFFS_YAFFS2 ++ ++/* These options are independent of each other. Select those that matter. */ ++ ++/* Default: Not selected */ ++/* Meaning: Yaffs does its own ECC, rather than using MTD ECC */ ++//#define CONFIG_YAFFS_DOES_ECC ++ ++/* Default: Not selected */ ++/* Meaning: ECC byte order is 'wrong'. Only meaningful if */ ++/* CONFIG_YAFFS_DOES_ECC is set */ ++//#define CONFIG_YAFFS_ECC_WRONG_ORDER ++ ++/* Default: Selected */ ++/* Meaning: Disables testing whether chunks are erased before writing to them*/ ++#define CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK ++ ++/* Default: Selected */ ++/* Meaning: Cache short names, taking more RAM, but faster look-ups */ ++#define CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++ ++/* Default: 10 */ ++/* Meaning: set the count of blocks to reserve for checkpointing */ ++#define YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 ++ ++#endif /* YAFFS_OUT_OF_TREE */ ++ ++#endif /* __YAFFS_CONFIG_H__ */ +diff --git a/fs/yaffs2/utils/Makefile b/fs/yaffs2/utils/Makefile +new file mode 100644 +index 0000000..1a7fd48 +--- /dev/null ++++ b/fs/yaffs2/utils/Makefile +@@ -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 --git a/fs/yaffs2/utils/README b/fs/yaffs2/utils/README +new file mode 100644 +index 0000000..317452b +--- /dev/null ++++ b/fs/yaffs2/utils/README +@@ -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 --git a/fs/yaffs2/utils/mkyaffs2image.c b/fs/yaffs2/utils/mkyaffs2image.c +new file mode 100644 +index 0000000..0f52b64 +--- /dev/null ++++ b/fs/yaffs2/utils/mkyaffs2image.c +@@ -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 --git a/fs/yaffs2/utils/mkyaffsimage.c b/fs/yaffs2/utils/mkyaffsimage.c +new file mode 100644 +index 0000000..06e2ac1 +--- /dev/null ++++ b/fs/yaffs2/utils/mkyaffsimage.c +@@ -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 --git a/fs/yaffs2/utils/ssfdc_rs_ecc.c b/fs/yaffs2/utils/ssfdc_rs_ecc.c +new file mode 100644 +index 0000000..9b541b4 +--- /dev/null ++++ b/fs/yaffs2/utils/ssfdc_rs_ecc.c +@@ -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 --git a/fs/yaffs2/utils/ssfdc_rs_ecc.h b/fs/yaffs2/utils/ssfdc_rs_ecc.h +new file mode 100644 +index 0000000..db304bd +--- /dev/null ++++ b/fs/yaffs2/utils/ssfdc_rs_ecc.h +@@ -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< ++ * ++ * 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. ++ */ ++ ++const char *yaffs_checkptrw_c_version = ++ "$Id: yaffs_checkptrw.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; ++ ++ ++#include "yaffs_checkptrw.h" ++ ++ ++static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) ++{ ++ ++ int blocksAvailable = dev->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) ++ 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){ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); ++ if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ ++ bi->blockState = YAFFS_BLOCK_STATE_EMPTY; ++ dev->nErasedBlocks++; ++ dev->nFreeChunks += dev->nChunksPerBlock; ++ } ++ else { ++ dev->markNANDBlockBad(dev,i); ++ bi->blockState = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocksInCheckpoint = 0; ++ ++ return 1; ++} ++ ++ ++static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) ++{ ++ int i; ++ int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; ++ 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){ ++ dev->checkpointNextBlock = i + 1; ++ dev->checkpointCurrentBlock = i; ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i)); ++ return; ++ } ++ } ++ } ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); ++ ++ dev->checkpointNextBlock = -1; ++ dev->checkpointCurrentBlock = -1; ++} ++ ++static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) ++{ ++ 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) ++ 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), ++ i, tags.objectId,tags.sequenceNumber,tags.eccResult)); ++ ++ if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ ++ /* Right kind of block */ ++ dev->checkpointNextBlock = tags.objectId; ++ dev->checkpointCurrentBlock = i; ++ dev->checkpointBlockList[dev->blocksInCheckpoint] = i; ++ dev->blocksInCheckpoint++; ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); ++ return; ++ } ++ } ++ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); ++ ++ dev->checkpointNextBlock = -1; ++ dev->checkpointCurrentBlock = -1; ++} ++ ++ ++int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) ++{ ++ ++ /* Got the functions we need? */ ++ if (!dev->writeChunkWithTagsToNAND || ++ !dev->readChunkWithTagsFromNAND || ++ !dev->eraseBlockInNAND || ++ !dev->markNANDBlockBad) ++ return 0; ++ ++ 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->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 */ ++ dev->checkpointByteOffset = dev->nDataBytesPerChunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) ++ * going to be way more than we need */ ++ dev->blocksInCheckpoint = 0; ++ dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2; ++ dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks); ++ for(i = 0; i < dev->checkpointMaxBlocks; i++) ++ dev->checkpointBlockList[i] = -1; ++ } ++ ++ return 1; ++} ++ ++static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) ++{ ++ ++ int chunk; ++ 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; ++ tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.byteCount = dev->nDataBytesPerChunk; ++ if(dev->checkpointCurrentChunk == 0){ ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); ++ 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)); ++ ++ realignedChunk = chunk - dev->chunkOffset; ++ ++ dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); ++ dev->checkpointByteOffset = 0; ++ dev->checkpointPageSequence++; ++ dev->checkpointCurrentChunk++; ++ if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ ++ dev->checkpointCurrentChunk = 0; ++ dev->checkpointCurrentBlock = -1; ++ } ++ memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); ++ ++ return 1; ++} ++ ++ ++int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) ++{ ++ int i=0; ++ int ok = 1; ++ ++ ++ __u8 * dataBytes = (__u8 *)data; ++ ++ ++ ++ if(!dev->checkpointBuffer) ++ return 0; ++ ++ while(i < nBytes && ok) { ++ ++ ++ ++ dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; ++ dev->checkpointByteOffset++; ++ i++; ++ dataBytes++; ++ dev->checkpointByteCount++; ++ ++ ++ if(dev->checkpointByteOffset < 0 || ++ dev->checkpointByteOffset >= dev->nDataBytesPerChunk) ++ ok = yaffs_CheckpointFlushBuffer(dev); ++ ++ } ++ ++ return i; ++} ++ ++int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) ++{ ++ int i=0; ++ int ok = 1; ++ yaffs_ExtendedTags tags; ++ ++ ++ int chunk; ++ int realignedChunk; ++ ++ __u8 *dataBytes = (__u8 *)data; ++ ++ if(!dev->checkpointBuffer) ++ return 0; ++ ++ 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 + ++ dev->checkpointCurrentChunk; ++ ++ realignedChunk = chunk - dev->chunkOffset; ++ ++ /* read in the next chunk */ ++ /* printf("read checkpoint page %d\n",dev->checkpointPage); */ ++ dev->readChunkWithTagsFromNAND(dev, realignedChunk, ++ dev->checkpointBuffer, ++ &tags); ++ ++ if(tags.chunkId != (dev->checkpointPageSequence + 1) || ++ tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ ok = 0; ++ ++ dev->checkpointByteOffset = 0; ++ dev->checkpointPageSequence++; ++ dev->checkpointCurrentChunk++; ++ ++ if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) ++ dev->checkpointCurrentBlock = -1; ++ } ++ } ++ ++ if(ok){ ++ *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; ++ dev->checkpointByteOffset++; ++ i++; ++ dataBytes++; ++ dev->checkpointByteCount++; ++ } ++ } ++ ++ return i; ++} ++ ++int yaffs_CheckpointClose(yaffs_Device *dev) ++{ ++ ++ if(dev->checkpointOpenForWrite){ ++ if(dev->checkpointByteOffset != 0) ++ yaffs_CheckpointFlushBuffer(dev); ++ } else { ++ int i; ++ for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ ++ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); ++ if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) ++ bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; ++ else { ++ // Todo this looks odd... ++ } ++ } ++ YFREE(dev->checkpointBlockList); ++ dev->checkpointBlockList = NULL; ++ } ++ ++ 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 */ ++ YFREE(dev->checkpointBuffer); ++ dev->checkpointBuffer = NULL; ++ return 1; ++ } ++ else ++ return 0; ++ ++} ++ ++int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) ++{ ++ /* Erase the first checksum block */ ++ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); ++ ++ if(!yaffs_CheckpointSpaceOk(dev)) ++ return 0; ++ ++ return yaffs_CheckpointErase(dev); ++} ++ ++ ++ +diff --git a/fs/yaffs2/yaffs_checkptrw.h b/fs/yaffs2/yaffs_checkptrw.h +new file mode 100644 +index 0000000..da78a06 +--- /dev/null ++++ b/fs/yaffs2/yaffs_checkptrw.h +@@ -0,0 +1,33 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting); ++ ++int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes); ++ ++int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes); ++ ++int yaffs_CheckpointClose(yaffs_Device *dev); ++ ++int yaffs_CheckpointInvalidateStream(yaffs_Device *dev); ++ ++ ++#endif ++ +diff --git a/fs/yaffs2/yaffs_ecc.c b/fs/yaffs2/yaffs_ecc.c +new file mode 100644 +index 0000000..4fd6cc6 +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.c +@@ -0,0 +1,422 @@ ++/* ++ * 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 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. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * 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 ++ * blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore ++ * this bytes influence on the line parity. ++ */ ++ ++const char *yaffs_ecc_c_version = ++ "$Id: yaffs_ecc.c,v 1.2 2008-07-17 23:07:00 lhhuang Exp $"; ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++/* Count the bits in an unsigned char or a U32 */ ++ ++static int yaffs_CountBits(unsigned char 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) ++{ ++ unsigned int i; ++ ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) // odd number of bits in the byte ++ { ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER ++ // Swap the bytes into the wrong order ++ t = ecc[0]; ++ ecc[0] = ecc[1]; ++ ecc[1] = t; ++#endif ++} ++ ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER ++ // swap the bytes to correct for the wrong order ++ unsigned char t; ++ ++ t = d0; ++ d0 = d1; ++ d1 = t; ++#endif ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((yaffs_CountBits(d0) + ++ yaffs_CountBits(d1) + ++ yaffs_CountBits(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ 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 ++ */ ++void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * eccOther) ++{ ++ unsigned int i; ++ ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < nBytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ eccOther->colParity = (col_parity >> 2) & 0x3f; ++ eccOther->lineParity = line_parity; ++ eccOther->lineParityPrime = line_parity_prime; ++} ++ ++int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * read_ecc, ++ const yaffs_ECCOther * test_ecc) ++{ ++ unsigned char cDelta; /* column parity delta */ ++ unsigned lDelta; /* line parity delta */ ++ unsigned lDeltaPrime; /* line parity delta */ ++ unsigned bit; ++ ++ cDelta = read_ecc->colParity ^ test_ecc->colParity; ++ lDelta = read_ecc->lineParity ^ test_ecc->lineParity; ++ lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime; ++ ++ if ((cDelta | lDelta | lDeltaPrime) == 0) ++ return 0; /* no error */ ++ ++ if (lDelta == ~lDeltaPrime && ++ (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) ++ { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (cDelta & 0x20) ++ bit |= 0x04; ++ if (cDelta & 0x08) ++ bit |= 0x02; ++ if (cDelta & 0x02) ++ bit |= 0x01; ++ ++ if(lDelta >= nBytes) ++ return -1; ++ ++ data[lDelta] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) + ++ yaffs_CountBits(cDelta)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++#endif /* YAFFS_ECC_RS */ +diff --git a/fs/yaffs2/yaffs_ecc.h b/fs/yaffs2/yaffs_ecc.h +new file mode 100644 +index 0000000..6754663 +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.h +@@ -0,0 +1,48 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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. ++ */ ++ ++ /* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * 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 ++ * blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++typedef struct { ++ unsigned char colParity; ++ unsigned lineParity; ++ 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); ++ ++void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * ecc); ++int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, ++ yaffs_ECCOther * read_ecc, ++ const yaffs_ECCOther * test_ecc); ++#endif +diff --git a/fs/yaffs2/yaffs_fs.c b/fs/yaffs2/yaffs_fs.c +new file mode 100644 +index 0000000..5e7e202 +--- /dev/null ++++ b/fs/yaffs2/yaffs_fs.c +@@ -0,0 +1,2222 @@ ++/* ++ * 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 Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * 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 is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * 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 ++ * superblock ++ * >> inode->u.generic_ip points to the associated yaffs_Object. ++ */ ++ ++const char *yaffs_fs_c_version = ++ "$Id: yaffs_fs.c,v 1.2 2008-07-17 23:59:16 lhhuang Exp $"; ++extern const char *yaffs_guts_c_version; ++ ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ ++#include /* Added NCB 15-8-2003 */ ++#include ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#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_mtdif2.h" ++ ++#if defined(CONFIG_YAFFS_ECC_RS) ++#include ++#include "yaffs_ecc.h" ++#endif ++ ++/*#define T(x) printk x */ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) ++#define yaffs_InodeToObjectLV(iptr) (iptr)->i_private ++#else ++#define yaffs_InodeToObjectLV(iptr) (iptr)->u.generic_ip ++#endif ++ ++#define yaffs_InodeToObject(iptr) ((yaffs_Object *)(yaffs_InodeToObjectLV(iptr))) ++#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->s_fs_info) ++#else ++#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->u.generic_sbp) ++#endif ++ ++static void yaffs_put_super(struct super_block *sb); ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id); ++#else ++static int yaffs_file_flush(struct file *file); ++#endif ++ ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync); ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n); ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n); ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode); ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry); ++#endif ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry); ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry); ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname); ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t dev); ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int dev); ++#endif ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry); ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait); ++static void yaffs_write_super(struct super_block *sb); ++#else ++static int yaffs_sync_fs(struct super_block *sb); ++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)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf); ++#endif ++static void yaffs_read_inode(struct inode *inode); ++ ++static void yaffs_put_inode(struct inode *inode); ++static void yaffs_delete_inode(struct inode *); ++static void yaffs_clear_inode(struct inode *); ++ ++static int yaffs_readpage(struct file *file, struct page *page); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc); ++#else ++static int yaffs_writepage(struct page *page); ++#endif ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to); ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to); ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++}; ++ ++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, ++#else ++ .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)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)) ++ .sendfile = generic_file_sendfile, ++#endif ++ ++}; ++ ++static struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++}; ++ ++static struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++ .setattr = yaffs_setattr, ++}; ++ ++static struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++}; ++ ++static struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++ .readdir = yaffs_readdir, ++ .fsync = yaffs_sync_object, ++}; ++ ++static struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ .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, ++ .write_super = yaffs_write_super, ++}; ++ ++static void yaffs_GrossLock(yaffs_Device * dev) ++{ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs locking\n")); ++ ++ down(&dev->grossLock); ++} ++ ++static void yaffs_GrossUnlock(yaffs_Device * dev) ++{ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs unlocking\n")); ++ up(&dev->grossLock); ++ ++} ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); ++ ++ yaffs_GrossUnlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++ kfree(alias); ++ return ret; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++#endif ++{ ++ unsigned char *alias; ++ int ret; ++ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); ++ ++ yaffs_GrossUnlock(dev); ++ ++ if (!alias) ++ { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) ++ return ERR_PTR (ret); ++#else ++ return ret; ++#endif ++} ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ yaffs_Object * obj); ++ ++/* ++ * Lookup is used to find objects in the fs ++ */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ yaffs_Object *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_lookup for %d:%s\n", ++ yaffs_InodeToObject(dir)->objectId, dentry->d_name.name)); ++ ++ obj = ++ yaffs_FindObjectByName(yaffs_InodeToObject(dir), ++ 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); ++ ++ if (obj) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_lookup found %d\n", obj->objectId)); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ ++ if (inode) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_loookup dentry \n")); ++/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to ++ * d_add even if NULL inode */ ++#if 0 ++ /*dget(dentry); // try to solve directory bug */ ++ d_add(dentry, inode); ++ ++ /* return dentry; */ ++ return NULL; ++#endif ++ } ++ ++ } else { ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_lookup not found\n")); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++ /* return (ERR_PTR(-EIO)); */ ++ ++} ++ ++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. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ T(YAFFS_TRACE_OS, ++ ("yaffs_put_inode: ino %d, count %d\n", (int)inode->i_ino, ++ atomic_read(&inode->i_count))); ++ ++} ++ ++/* clear is called to tell the fs to release any per-inode data it holds */ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ yaffs_Object *obj; ++ yaffs_Device *dev; ++ ++ obj = yaffs_InodeToObject(inode); ++ ++ T(YAFFS_TRACE_OS, ++ ("yaffs_clear_inode: ino %d, count %d %s\n", (int)inode->i_ino, ++ atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object")); ++ ++ if (obj) { ++ dev = obj->myDev; ++ yaffs_GrossLock(dev); ++ ++ /* Clear the association between the inode and ++ * the yaffs_Object. ++ */ ++ obj->myInode = NULL; ++ yaffs_InodeToObjectLV(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ ++ yaffs_HandleDeferedFree(obj); ++ ++ yaffs_GrossUnlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ yaffs_Object *obj = yaffs_InodeToObject(inode); ++ yaffs_Device *dev; ++ ++ T(YAFFS_TRACE_OS, ++ ("yaffs_delete_inode: ino %d, count %d %s\n", (int)inode->i_ino, ++ atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object")); ++ ++ if (obj) { ++ dev = obj->myDev; ++ yaffs_GrossLock(dev); ++ yaffs_DeleteFile(obj); ++ yaffs_GrossUnlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) ++ truncate_inode_pages (&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry); ++ ++ yaffs_Device *dev = obj->myDev; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_file_flush object %d (%s)\n", obj->objectId, ++ obj->dirty ? "dirty" : "clean")); ++ ++ yaffs_GrossLock(dev); ++ ++ yaffs_FlushFile(obj, 1); ++ ++ yaffs_GrossUnlock(dev); ++ ++ return 0; ++} ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ yaffs_Object *obj; ++ unsigned char *pg_buf; ++ int ret; ++ ++ yaffs_Device *dev; ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage at %08x, size %08x\n", ++ (unsigned)(pg->index << PAGE_CACHE_SHIFT), ++ (unsigned)PAGE_CACHE_SIZE)); ++ ++ obj = yaffs_DentryToObject(f->f_dentry); ++ ++ dev = obj->myDev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_GrossLock(dev); ++ ++ ret = ++ yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, ++ PAGE_CACHE_SIZE); ++ ++ yaffs_GrossUnlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage done\n")); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ return yaffs_readpage_unlock(f, pg); ++} ++ ++/* writepage inspired by/stolen from smbfs */ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct address_space *mapping = page->mapping; ++ loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ yaffs_Object *obj; ++ int nWritten = 0; ++ unsigned nBytes; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ ++ if (offset > inode->i_size) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG ++ "yaffs_writepage at %08x, inode size = %08x!!!\n", ++ (unsigned)(page->index << PAGE_CACHE_SHIFT), ++ (unsigned)inode->i_size)); ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG " -> don't care!!\n")); ++ unlock_page(page); ++ return 0; ++ } ++ ++ end_index = inode->i_size >> PAGE_CACHE_SHIFT; ++ ++ /* easy case */ ++ if (page->index < end_index) { ++ nBytes = PAGE_CACHE_SIZE; ++ } else { ++ nBytes = inode->i_size & (PAGE_CACHE_SIZE - 1); ++ } ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_InodeToObject(inode); ++ yaffs_GrossLock(obj->myDev); ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_writepage at %08x, size %08x\n", ++ (unsigned)(page->index << PAGE_CACHE_SHIFT), nBytes)); ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "writepag0: obj = %05x, ino = %05x\n", ++ (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); ++ ++ nWritten = ++ yaffs_WriteDataToFile(obj, buffer, page->index << PAGE_CACHE_SHIFT, ++ nBytes, 0); ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "writepag1: obj = %05x, ino = %05x\n", ++ (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); ++ ++ yaffs_GrossUnlock(obj->myDev); ++ ++ kunmap(page); ++ SetPageUptodate(page); ++ UnlockPage(page); ++ put_page(page); ++ ++ return (nWritten == nBytes) ? 0 : -ENOSPC; ++} ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_prepair_write\n")); ++ if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE)) ++ return yaffs_readpage_nolock(f, pg); ++ ++ return 0; ++ ++} ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ ++ void *addr = page_address(pg) + offset; ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int nBytes = to - offset; ++ int nWritten; ++ ++ unsigned spos = pos; ++ unsigned saddr = (unsigned)addr; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_commit_write addr %x pos %x nBytes %d\n", saddr, ++ spos, nBytes)); ++ ++ nWritten = yaffs_file_write(f, addr, nBytes, &pos); ++ ++ if (nWritten != nBytes) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG ++ "yaffs_commit_write not same size nWritten %d nBytes %d\n", ++ nWritten, nBytes)); ++ SetPageError(pg); ++ ClearPageUptodate(pg); ++ } else { ++ SetPageUptodate(pg); ++ } ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_commit_write returning %d\n", ++ nWritten == nBytes ? 0 : nWritten)); ++ ++ return nWritten == nBytes ? 0 : nWritten; ++ ++} ++ ++static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object * obj) ++{ ++ if (inode && obj) { ++ ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ __u32 mode = obj->yst_mode; ++ switch( obj->variantType ){ ++ case YAFFS_OBJECT_TYPE_FILE : ++ if( ! S_ISREG(mode) ){ ++ 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 : ++ case YAFFS_OBJECT_TYPE_SPECIAL : ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_ino = obj->objectId; ++ inode->i_mode = obj->yst_mode; ++ inode->i_uid = obj->yst_uid; ++ inode->i_gid = obj->yst_gid; ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_GetObjectFileLength(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ inode->i_nlink = yaffs_GetObjectLinkCount(obj); ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG ++ "yaffs_FillInode mode %x uid %d gid %d size %d count %d\n", ++ inode->i_mode, inode->i_uid, inode->i_gid, ++ (int)inode->i_size, atomic_read(&inode->i_count))); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_InodeToObjectLV(inode) = obj; ++ ++ obj->myInode = inode; ++ ++ } else { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_FileInode invalid parameters\n")); ++ } ++ ++} ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ yaffs_Object * obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_get_inode for NULL super_block!!\n")); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_get_inode for NULL object!!\n")); ++ return NULL; ++ ++ } ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId)); ++ ++ inode = iget(sb, obj->objectId); ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding grossLock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ yaffs_Object *obj; ++ int nWritten, ipos; ++ struct inode *inode; ++ yaffs_Device *dev; ++ ++ obj = yaffs_DentryToObject(f->f_dentry); ++ ++ dev = obj->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) { ++ ipos = inode->i_size; ++ } else { ++ ipos = *pos; ++ } ++ ++ if (!obj) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_file_write: hey obj is null!\n")); ++ } else { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG ++ "yaffs_file_write about to write writing %d bytes" ++ "to object %d at %d\n", ++ n, obj->objectId, ipos)); ++ } ++ ++ nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0); ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_file_write writing %d bytes, %d written at %d\n", ++ n, nWritten, ipos)); ++ if (nWritten > 0) { ++ ipos += nWritten; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG ++ "yaffs_file_write size updated to %d bytes, " ++ "%d blocks\n", ++ ipos, (int)(inode->i_blocks))); ++ } ++ ++ } ++ yaffs_GrossUnlock(dev); ++ return nWritten == 0 ? -ENOSPC : nWritten; ++} ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ yaffs_Object *obj; ++ yaffs_Device *dev; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct list_head *i; ++ yaffs_Object *l; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_DentryToObject(f->f_dentry); ++ dev = obj->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ offset = f->f_pos; ++ ++ T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset)); ++ ++ if (offset == 0) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_readdir: entry . ino %d \n", ++ (int)inode->i_ino)); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) ++ < 0) { ++ goto out; ++ } ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_readdir: entry .. ino %d \n", ++ (int)f->f_dentry->d_parent->d_inode->i_ino)); ++ if (filldir ++ (dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { ++ goto out; ++ } ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ list_for_each(i, &obj->variant.directoryVariant.children) { ++ curoffs++; ++ if (curoffs >= offset) { ++ l = list_entry(i, yaffs_Object, siblings); ++ ++ yaffs_GetObjectName(l, name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_readdir: %s inode %d\n", name, ++ yaffs_GetObjectInode(l))); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, ++ yaffs_GetObjectInode(l), ++ yaffs_GetObjectType(l)) ++ < 0) { ++ goto up_and_out; ++ } ++ ++ offset++; ++ f->f_pos++; ++ } ++ } ++ ++ up_and_out: ++ out: ++ ++ yaffs_GrossUnlock(dev); ++ ++ return 0; ++} ++ ++/* ++ * File creation. Allocate an inode, and we're done.. ++ */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ yaffs_Object *obj = NULL; ++ yaffs_Device *dev; ++ ++ yaffs_Object *parent = yaffs_InodeToObject(dir); ++ ++ 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; ++ ++ if (parent) { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_mknod: parent object %d type %d\n", ++ parent->objectId, parent->variantType)); ++ } else { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_mknod: could not get parent object\n")); ++ return -EPERM; ++ } ++ ++ T(YAFFS_TRACE_OS, ("yaffs_mknod: making oject for %s, " ++ "mode %x dev %x\n", ++ dentry->d_name.name, mode, rdev)); ++ ++ dev = parent->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG ++ "yaffs_mknod: making special\n")); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ obj = ++ yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); ++ obj = ++ yaffs_MknodFile(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_mknod: making directory\n")); ++ obj = ++ yaffs_MknodDirectory(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_GrossUnlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_mknod created object %d count = %d\n", ++ obj->objectId, atomic_read(&inode->i_count))); ++ error = 0; ++ } else { ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_mknod failed making object\n")); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ int retVal; ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mkdir\n")); ++ retVal = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++#if 0 ++ /* attempt to fix dir bug - didn't work */ ++ if (!retVal) { ++ dget(dentry); ++ } ++#endif ++ return retVal; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_create\n")); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int retVal; ++ ++ yaffs_Device *dev; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_unlink %d:%s\n", (int)(dir->i_ino), ++ dentry->d_name.name)); ++ ++ dev = yaffs_InodeToObject(dir)->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name); ++ ++ if (retVal == YAFFS_OK) { ++ dentry->d_inode->i_nlink--; ++ dir->i_version++; ++ yaffs_GrossUnlock(dev); ++ mark_inode_dirty(dentry->d_inode); ++ return 0; ++ } ++ yaffs_GrossUnlock(dev); ++ return -ENOTEMPTY; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ yaffs_Object *obj = NULL; ++ yaffs_Object *link = NULL; ++ yaffs_Device *dev; ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_link\n")); ++ ++ obj = yaffs_InodeToObject(inode); ++ dev = obj->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ { ++ link = ++ yaffs_Link(yaffs_InodeToObject(dir), dentry->d_name.name, ++ obj); ++ } ++ ++ if (link) { ++ old_dentry->d_inode->i_nlink = yaffs_GetObjectLinkCount(obj); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_link link count %d i_count %d\n", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count))); ++ ++ } ++ ++ yaffs_GrossUnlock(dev); ++ ++ if (link) { ++ ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ yaffs_Object *obj; ++ yaffs_Device *dev; ++ uid_t uid = current->fsuid; ++ gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_symlink\n")); ++ ++ dev = yaffs_InodeToObject(dir)->myDev; ++ yaffs_GrossLock(dev); ++ obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_GrossUnlock(dev); ++ ++ if (obj) { ++ ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink created OK\n")); ++ return 0; ++ } else { ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink not created\n")); ++ ++ } ++ ++ return -ENOMEM; ++} ++ ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++{ ++ ++ yaffs_Object *obj; ++ yaffs_Device *dev; ++ ++ obj = yaffs_DentryToObject(dentry); ++ ++ dev = obj->myDev; ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_object\n")); ++ yaffs_GrossLock(dev); ++ yaffs_FlushFile(obj, 1); ++ yaffs_GrossUnlock(dev); ++ return 0; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ yaffs_Device *dev; ++ int retVal = YAFFS_FAIL; ++ yaffs_Object *target; ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_rename\n")); ++ dev = yaffs_InodeToObject(old_dir)->myDev; ++ ++ yaffs_GrossLock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ 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; ++ } else { ++ ++ /* 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, ++ yaffs_InodeToObject(new_dir), ++ new_dentry->d_name.name); ++ ++ } ++ yaffs_GrossUnlock(dev); ++ ++ if (retVal == YAFFS_OK) { ++ if(target) { ++ new_dentry->d_inode->i_nlink--; ++ mark_inode_dirty(new_dentry->d_inode); ++ } ++ ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error; ++ yaffs_Device *dev; ++ ++ T(YAFFS_TRACE_OS, ++ (KERN_DEBUG "yaffs_setattr of object %d\n", ++ yaffs_InodeToObject(inode)->objectId)); ++ ++ if ((error = inode_change_ok(inode, attr)) == 0) { ++ ++ dev = yaffs_InodeToObject(inode)->myDev; ++ yaffs_GrossLock(dev); ++ if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) == ++ YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_GrossUnlock(dev); ++ if (!error) ++ error = inode_setattr(inode, attr); ++ } ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ yaffs_Device *dev = yaffs_SuperToDevice(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ yaffs_Device *dev = yaffs_SuperToDevice(sb); ++#endif ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_statfs\n")); ++ ++ yaffs_GrossLock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ if (sb->s_blocksize > dev->nDataBytesPerChunk) { ++ ++ buf->f_blocks = ++ (dev->endBlock - dev->startBlock + ++ 1) * dev->nChunksPerBlock / (sb->s_blocksize / ++ dev->nDataBytesPerChunk); ++ buf->f_bfree = ++ yaffs_GetNumberOfFreeChunks(dev) / (sb->s_blocksize / ++ dev->nDataBytesPerChunk); ++ } else { ++ ++ buf->f_blocks = ++ (dev->endBlock - dev->startBlock + ++ 1) * dev->nChunksPerBlock * (dev->nDataBytesPerChunk / ++ sb->s_blocksize); ++ buf->f_bfree = ++ yaffs_GetNumberOfFreeChunks(dev) * (dev->nDataBytesPerChunk / ++ sb->s_blocksize); ++ } ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_GrossUnlock(dev); ++ return 0; ++} ++ ++ ++/** ++static int yaffs_do_sync_fs(struct super_block *sb) ++{ ++ ++ yaffs_Device *dev = yaffs_SuperToDevice(sb); ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_do_sync_fs\n")); ++ ++ if(sb->s_dirt) { ++ yaffs_GrossLock(dev); ++ ++ if(dev) ++ yaffs_CheckpointSave(dev); ++ ++ yaffs_GrossUnlock(dev); ++ ++ sb->s_dirt = 0; ++ } ++ return 0; ++} ++**/ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_write_super\n")); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) ++ return 0; /* yaffs_do_sync_fs(sb);*/ ++#endif ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ ++ 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 ++ * need to lock again. ++ */ ++ ++ yaffs_Object *obj; ++ yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb); ++ ++ T(YAFFS_TRACE_OS, ++ (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); ++ ++ yaffs_GrossUnlock(dev); ++} ++ ++static LIST_HEAD(yaffs_dev_list); ++ ++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, ++ (KERN_DEBUG "yaffs_remount_fs: %s: RW\n", dev->name )); ++ } ++ ++ return 0; ++} ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ yaffs_Device *dev = yaffs_SuperToDevice(sb); ++ ++ T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_put_super\n")); ++ ++ yaffs_GrossLock(dev); ++ ++ yaffs_FlushEntireDeviceCache(dev); ++ ++ yaffs_CheckpointSave(dev); ++ ++ if (dev->putSuperFunc) { ++ dev->putSuperFunc(sb); ++ } ++ ++ 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; ++ } ++ ++ kfree(dev); ++} ++ ++ ++static void yaffs_MTDPutSuper(struct super_block *sb) ++{ ++ ++ struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; ++ ++ if (mtd->sync) { ++ mtd->sync(mtd); ++ } ++ ++ put_mtd_device(mtd); ++} ++ ++ ++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; ++} ++ ++static struct super_block *yaffs_internal_read_super(int yaffsVersion, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int nBlocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ yaffs_Device *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ ++ if (!sb) ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ else if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf)); ++ ++ 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)); ++ T(YAFFS_TRACE_OS, ++ ("yaffs_read_super: block size %d\n", (int)(sb->s_blocksize))); ++ ++#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY ++ T(YAFFS_TRACE_OS, ++ ("yaffs: Write verification disabled. All guarantees " ++ "null and void\n")); ++#endif ++ ++ T(YAFFS_TRACE_ALWAYS, ("yaffs: Attempting MTD mount on %u.%u, " ++ "\"%s\"\n", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf))); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { ++ return NULL; /* This isn't an mtd device */ ++ } ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (!mtd) { ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs: MTD device #%u doesn't appear to exist\n", ++ MINOR(sb->s_dev))); ++ return NULL; ++ } ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs: MTD device is not NAND it's type %d\n", mtd->type)); ++ return NULL; ++ } ++ ++ T(YAFFS_TRACE_OS, (" erase %p\n", mtd->erase)); ++ T(YAFFS_TRACE_OS, (" read %p\n", mtd->read)); ++ T(YAFFS_TRACE_OS, (" write %p\n", mtd->write)); ++ T(YAFFS_TRACE_OS, (" readoob %p\n", mtd->read_oob)); ++ 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)); ++#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 %lld\n", mtd->size)); ++ ++#ifdef CONFIG_YAFFS_AUTO_YAFFS2 ++ ++ if (yaffsVersion == 1 && ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ mtd->writesize >= 2048) { ++#else ++ mtd->oobblock >= 2048) { ++#endif ++ T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); ++ yaffsVersion = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffsVersion == 2 && ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ mtd->writesize == 512) { ++#else ++ mtd->oobblock == 512) { ++#endif ++ T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n")); ++ yaffsVersion = 1; ++ } ++ ++#endif ++ ++ if (yaffsVersion == 2) { ++ /* Check for version 2 style functions */ ++ if (!mtd->erase || ++ !mtd->block_isbad || ++ !mtd->block_markbad || ++ !mtd->read || ++ !mtd->write || ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ !mtd->read_oob || !mtd->write_oob) { ++#else ++ !mtd->write_ecc || ++ !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { ++#endif ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs: MTD device does not support required " ++ "functions\n"));; ++ return NULL; ++ } ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ if (mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++#else ++ if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++#endif ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) { ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs: MTD device does not have the " ++ "right page sizes\n")); ++ return NULL; ++ } ++ } else { ++ /* Check for V1 style functions */ ++ if (!mtd->erase || ++ !mtd->read || ++ !mtd->write || ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ !mtd->read_oob || !mtd->write_oob) { ++#else ++ !mtd->write_ecc || ++ !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { ++#endif ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs: MTD device does not support required " ++ "functions\n"));; ++ return NULL; ++ } ++ ++#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 " ++ "right page sizes\n")); ++ return NULL; ++ } ++ } ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the yaffs_Device up for mtd ++ */ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++ sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); ++#else ++ sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); ++#endif ++ if (!dev) { ++ /* Deep shit could not allocate device structure */ ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs_read_super: Failed trying to allocate " ++ "yaffs_Device. \n")); ++ return NULL; ++ } ++ ++ memset(dev, 0, sizeof(yaffs_Device)); ++ dev->genericDevice = mtd; ++ dev->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK; ++ dev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK; ++ dev->nReservedBlocks = 5; ++ dev->nShortOpCaches = 10; /* Enable short op caching */ ++ ++ /* ... and the functions. */ ++ if (yaffsVersion == 2) { ++ int block_shift; ++ dev->writeChunkWithTagsToNAND = ++ nandmtd2_WriteChunkWithTagsToNAND; ++ dev->readChunkWithTagsFromNAND = ++ nandmtd2_ReadChunkWithTagsFromNAND; ++ dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; ++ dev->queryNANDBlock = nandmtd2_QueryNANDBlock; ++ dev->spareBuffer = YMALLOC(mtd->oobsize); ++ dev->isYaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ dev->nDataBytesPerChunk = mtd->writesize; ++ dev->nChunksPerBlock = mtd->erasesize / mtd->writesize; ++#else ++ dev->nDataBytesPerChunk = mtd->oobblock; ++ dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; ++#endif ++ 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 { ++ 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; ++ dev->isYaffs2 = 0; ++ } ++ /* ... and common functions */ ++ dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND; ++ dev->initialiseNAND = nandmtd_InitialiseNAND; ++ ++ dev->putSuperFunc = yaffs_MTDPutSuper; ++ ++ dev->superBlock = (void *)sb; ++ dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty; ++ ++ ++#ifndef CONFIG_YAFFS_DOES_ECC ++ dev->useNANDECC = 1; ++#endif ++ ++#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES ++ dev->wideTnodesDisabled = 1; ++#endif ++ ++ /* we assume this is protected by lock_kernel() in mount/umount */ ++ list_add_tail(&dev->devList, &yaffs_dev_list); ++ ++ init_MUTEX(&dev->grossLock); ++ ++ yaffs_GrossLock(dev); ++ ++ err = yaffs_GutsInitialise(dev); ++ ++ 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); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, ++ yaffs_Root(dev)); ++ ++ if (!inode) ++ return NULL; ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ T(YAFFS_TRACE_OS, ("yaffs_read_super: got root inode\n")); ++ ++ root = d_alloc_root(inode); ++ ++ T(YAFFS_TRACE_OS, ("yaffs_read_super: d_alloc_root done\n")); ++ ++ if (!root) { ++ iput(inode); ++ return NULL; ++ } ++ sb->s_root = root; ++ T(YAFFS_TRACE_OS, ("yaffs_read_super: done\n")); ++ return sb; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++ .get_sb = yaffs_read_super, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++ .get_sb = yaffs2_read_super, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++#endif /* CONFIG_YAFFS_YAFFS2 */ ++ ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev(char *buf, yaffs_Device * dev) ++{ ++ buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock); ++ buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock); ++ 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, "nTnodesCreated..... %d\n", dev->nTnodesCreated); ++ buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes); ++ buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated); ++ buf += sprintf(buf, "nFreeObjects....... %d\n", dev->nFreeObjects); ++ buf += sprintf(buf, "nFreeChunks........ %d\n", dev->nFreeChunks); ++ buf += sprintf(buf, "nPageWrites........ %d\n", dev->nPageWrites); ++ buf += sprintf(buf, "nPageReads......... %d\n", dev->nPageReads); ++ buf += sprintf(buf, "nBlockErasures..... %d\n", dev->nBlockErasures); ++ buf += sprintf(buf, "nGCCopies.......... %d\n", dev->nGCCopies); ++ buf += ++ sprintf(buf, "garbageCollections. %d\n", dev->garbageCollections); ++ buf += ++ sprintf(buf, "passiveGCs......... %d\n", ++ dev->passiveGarbageCollections); ++ buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites); ++ buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks); ++ buf += sprintf(buf, "eccFixed........... %d\n", dev->eccFixed); ++ buf += sprintf(buf, "eccUnfixed......... %d\n", dev->eccUnfixed); ++ buf += sprintf(buf, "tagsEccFixed....... %d\n", dev->tagsEccFixed); ++ buf += sprintf(buf, "tagsEccUnfixed..... %d\n", dev->tagsEccUnfixed); ++ buf += sprintf(buf, "cacheHits.......... %d\n", dev->cacheHits); ++ buf += sprintf(buf, "nDeletedFiles...... %d\n", dev->nDeletedFiles); ++ buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); ++ buf += ++ sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions); ++ buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC); ++ buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in devList. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) { ++ buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ ++ "\n%s\n%s\n", yaffs_fs_c_version, ++ yaffs_guts_c_version); ++ } ++ ++ /* hold lock_kernel while traversing yaffs_dev_list */ ++ lock_kernel(); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_dev_list) { ++ yaffs_Device *dev = list_entry(item, yaffs_Device, devList); ++ if (n < step) { ++ n++; ++ continue; ++ } ++ buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name); ++ buf = yaffs_dump_dev(buf, dev); ++ break; ++ } ++ unlock_kernel(); ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end, *mask_name; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_traceMask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) { ++ pos++; ++ } ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ done = 0; ++ } else { ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ len = strlen(mask_flags[i].mask_name); ++ if (strncmp(buf + pos, mask_flags[i].mask_name, len) == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ pos += len; ++ done = 0; ++ switch(add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_traceMask = rg | YAFFS_TRACE_ALWAYS; ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk("%c%s\n", flag, mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++//#ifdef CONFIG_YAFFS_YAFFS1 ++ {&yaffs_fs_type, 0}, ++//#endif ++//#ifdef CONFIG_YAFFS_YAFFS2 ++ {&yaffs2_fs_type, 0}, ++//#endif ++ {NULL, 0} ++}; ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ T(YAFFS_TRACE_ALWAYS, ++ ("yaffs " __DATE__ " " __TIME__ " Installing. \n")); ++ ++ /* Install the proc_fs entry */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, ++ &proc_root); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ } else { ++ return -ENOMEM; ++ } ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) { ++ fsinst->installed = 1; ++ } ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++#if defined(CONFIG_YAFFS_ECC_RS) ++ /* 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 ++ */ ++ rs_decoder = init_rs (5, 0x25, 1, 1, 4); ++#endif ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ T(YAFFS_TRACE_ALWAYS, ("yaffs " __DATE__ " " __TIME__ ++ " removing. \n")); ++ ++ remove_proc_entry("yaffs", &proc_root); ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ ++#if defined(CONFIG_YAFFS_ECC_RS) ++ free_rs(rs_decoder); ++#endif ++} ++ ++module_init(init_yaffs_fs) ++module_exit(exit_yaffs_fs) ++ ++MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006"); ++MODULE_LICENSE("GPL"); +diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c +new file mode 100644 +index 0000000..d00022a +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.c +@@ -0,0 +1,6676 @@ ++/* ++ * 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 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. ++ */ ++ ++const char *yaffs_guts_c_version = ++ "$Id: yaffs_guts.c,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $"; ++ ++#include "yportenv.h" ++ ++#include "yaffsinterface.h" ++#include "yaffs_guts.h" ++#include "yaffs_tagsvalidity.h" ++ ++#include "yaffs_tagscompat.h" ++#ifndef CONFIG_YAFFS_OWN_SORT ++#include "yaffs_qsort.h" ++#endif ++#include "yaffs_nand.h" ++ ++#include "yaffs_checkptrw.h" ++ ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++ ++ ++#ifdef CONFIG_YAFFS_WINCE ++void yfsd_LockYAFFS(BOOL fsLockOnly); ++void yfsd_UnlockYAFFS(BOOL fsLockOnly); ++#endif ++ ++#define YAFFS_PASSIVE_GC_CHUNKS 2 ++ ++#include "yaffs_ecc.h" ++ ++ ++/* Robustification (if it ever comes about...) */ ++static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND); ++static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk); ++static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, ++ const yaffs_ExtendedTags * tags); ++static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, ++ const yaffs_ExtendedTags * tags); ++ ++/* Other local prototypes */ ++static int yaffs_UnlinkObject( yaffs_Object *obj); ++static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); ++ ++static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); ++ ++static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev, ++ const __u8 * buffer, ++ yaffs_ExtendedTags * tags, ++ int useReserve); ++static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, ++ int chunkInNAND, int inScan); ++ ++static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, ++ yaffs_ObjectType type); ++static void yaffs_AddObjectToDirectory(yaffs_Object * directory, ++ yaffs_Object * obj); ++static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, ++ int force, int isShrink, int shadows); ++static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); ++static int yaffs_CheckStructures(void); ++static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, ++ int chunkOffset, int *limit); ++static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); ++ ++static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); ++ ++static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo); ++static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, ++ int lineNo); ++ ++static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND); ++ ++static int yaffs_UnlinkWorker(yaffs_Object * obj); ++static void yaffs_DestroyObject(yaffs_Object * obj); ++ ++static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, ++ int chunkInObject); ++ ++loff_t yaffs_GetFileSize(yaffs_Object * obj); ++ ++static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr); ++ ++static void yaffs_VerifyFreeChunks(yaffs_Device * dev); ++ ++#ifdef YAFFS_PARANOID ++static int yaffs_CheckFileSanity(yaffs_Object * in); ++#else ++#define yaffs_CheckFileSanity(in) ++#endif ++ ++static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in); ++static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId); ++ ++static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); ++ ++ ++ ++/* Function to calculate chunk and offset */ ++ ++static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset) ++{ ++ if(dev->chunkShift){ ++ /* Easy-peasy power of 2 case */ ++ *chunk = (__u32)(addr >> dev->chunkShift); ++ *offset = (__u32)(addr & dev->chunkMask); ++ } ++ else if(dev->crumbsPerChunk) ++ { ++ /* Case where we're using "crumbs" */ ++ *offset = (__u32)(addr & dev->crumbMask); ++ addr >>= dev->crumbShift; ++ *chunk = ((__u32)addr)/dev->crumbsPerChunk; ++ *offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift); ++ } ++ else ++ YBUG(); ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or equal ++ * to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static __u32 ShiftsGE(__u32 x) ++{ ++ int extraBits; ++ int nShifts; ++ ++ nShifts = extraBits = 0; ++ ++ while(x>1){ ++ if(x & 1) extraBits++; ++ x>>=1; ++ nShifts++; ++ } ++ ++ 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 __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) ++{ ++ int i, j; ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->tempBuffer[i].line == 0) { ++ dev->tempBuffer[i].line = lineNo; ++ if ((i + 1) > dev->maxTemp) { ++ dev->maxTemp = i + 1; ++ for (j = 0; j <= i; j++) ++ dev->tempBuffer[j].maxLine = ++ dev->tempBuffer[j].line; ++ } ++ ++ return dev->tempBuffer[i].buffer; ++ } ++ } ++ ++ T(YAFFS_TRACE_BUFFERS, ++ (TSTR("Out of temp buffers at line %d, other held by lines:"), ++ lineNo)); ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line)); ++ } ++ T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); ++ ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanagedTempAllocations++; ++ return YMALLOC(dev->nDataBytesPerChunk); ++ ++} ++ ++static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, ++ int lineNo) ++{ ++ int i; ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->tempBuffer[i].buffer == buffer) { ++ dev->tempBuffer[i].line = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ T(YAFFS_TRACE_BUFFERS, ++ (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), ++ lineNo)); ++ YFREE(buffer); ++ dev->unmanagedTempDeallocations++; ++ } ++ ++} ++ ++/* ++ * Determine if we have a managed buffer. ++ */ ++int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer) ++{ ++ int i; ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->tempBuffer[i].buffer == buffer) ++ return 1; ++ ++ } ++ ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if( dev->srCache[i].data == buffer ) ++ return 1; ++ ++ } ++ ++ if (buffer == dev->checkpointBuffer) ++ return 1; ++ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); ++ return 0; ++} ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk) ++{ ++ if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), ++ blk)); ++ YBUG(); ++ } ++ return dev->chunkBits + ++ (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); ++} ++ ++static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) ++{ ++ __u8 *blkBits = yaffs_BlockBits(dev, blk); ++ ++ memset(blkBits, 0, dev->chunkBitmapStride); ++} ++ ++static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk) ++{ ++ __u8 *blkBits = yaffs_BlockBits(dev, blk); ++ ++ blkBits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk) ++{ ++ __u8 *blkBits = yaffs_BlockBits(dev, blk); ++ ++ 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); ++ return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk) ++{ ++ __u8 *blkBits = yaffs_BlockBits(dev, blk); ++ int i; ++ for (i = 0; i < dev->chunkBitmapStride; i++) { ++ if (*blkBits) ++ return 1; ++ blkBits++; ++ } ++ return 0; ++} ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static Y_INLINE int yaffs_HashFunction(int n) ++{ ++ n = abs(n); ++ return (n % YAFFS_NOBJECT_BUCKETS); ++} ++ ++/* ++ * Access functions to useful fake objects ++ */ ++ ++yaffs_Object *yaffs_Root(yaffs_Device * dev) ++{ ++ return dev->rootDir; ++} ++ ++yaffs_Object *yaffs_LostNFound(yaffs_Device * dev) ++{ ++ return dev->lostNFoundDir; ++} ++ ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_CheckFF(__u8 * buffer, int nBytes) ++{ ++ /* Horrible, slow implementation */ ++ while (nBytes--) { ++ if (*buffer != 0xFF) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND) ++{ ++ ++ int retval = YAFFS_OK; ++ __u8 *data = yaffs_GetTempBuffer(dev, __LINE__); ++ yaffs_ExtendedTags tags; ++ 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, ++ (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_ReleaseTempBuffer(dev, data, __LINE__); ++ ++ return retval; ++ ++} ++ ++static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, ++ const __u8 * data, ++ yaffs_ExtendedTags * tags, ++ int useReserve) ++{ ++ int chunk; ++ ++ int writeOk = 0; ++ int erasedOk = 1; ++ int attempts = 0; ++ yaffs_BlockInfo *bi; ++ ++ yaffs_InvalidateCheckpoint(dev); ++ ++ do { ++ chunk = yaffs_AllocateChunk(dev, useReserve,&bi); ++ ++ 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 ++ ++ bi->skipErasedCheck = 0; ++ ++#endif ++ if(!bi->skipErasedCheck){ ++ erasedOk = yaffs_CheckChunkErased(dev, chunk); ++ if(erasedOk && !bi->gcPrioritise) ++ bi->skipErasedCheck = 1; ++ } ++ ++ 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); ++ } ++ } ++ } ++ ++ } while (chunk >= 0 && !writeOk); ++ ++ if (attempts > 1) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("**>> yaffs write required %d attempts" TENDSTR), ++ attempts)); ++ dev->nRetriedWrites += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * 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; ++ bi->gcPrioritise = 0; ++ bi->needsRetiring = 0; ++ ++ dev->nRetiredBlocks++; ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, ++ const yaffs_ExtendedTags * tags) ++{ ++} ++ ++static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, ++ const yaffs_ExtendedTags * tags) ++{ ++} ++ ++void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) ++{ ++ if(!bi->gcPrioritise){ ++ 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" : "")); ++ ++ } ++} ++ ++static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk) ++{ ++ ++ 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 ------------*/ ++ ++static __u16 yaffs_CalcNameSum(const YCHAR * name) ++{ ++ __u16 sum = 0; ++ __u16 i = 1; ++ ++ YUCHAR *bname = (YUCHAR *) name; ++ if (bname) { ++ while ((*bname) && (i <= YAFFS_MAX_NAME_LENGTH)) { ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ sum += yaffs_toupper(*bname) * i; ++#else ++ sum += (*bname) * i; ++#endif ++ i++; ++ bname++; ++ } ++ } ++ return sum; ++} ++ ++static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name) ++{ ++#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++ if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) { ++ yaffs_strcpy(obj->shortName, name); ++ } else { ++ obj->shortName[0] = _Y('\0'); ++ } ++#endif ++ obj->sum = yaffs_CalcNameSum(name); ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * 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 ++ */ ++ ++static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes) ++{ ++ int i; ++ int tnodeSize; ++ yaffs_Tnode *newTnodes; ++ __u8 *mem; ++ yaffs_Tnode *curr; ++ yaffs_Tnode *next; ++ yaffs_TnodeList *tnl; ++ ++ 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; ++ ++ /* make these things */ ++ ++ newTnodes = YMALLOC(nTnodes * tnodeSize); ++ mem = (__u8 *)newTnodes; ++ ++ if (!newTnodes) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++#if 0 ++ for (i = 0; i < nTnodes - 1; i++) { ++ newTnodes[i].internal[0] = &newTnodes[i + 1]; ++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; ++#endif ++ } ++ ++ newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; ++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; ++#endif ++ dev->freeTnodes = newTnodes; ++#else ++ /* New hookup for wide tnodes */ ++ for(i = 0; i < nTnodes -1; i++) { ++ curr = (yaffs_Tnode *) &mem[i * tnodeSize]; ++ 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; ++ ++#endif ++ ++ ++ dev->nFreeTnodes += nTnodes; ++ dev->nTnodesCreated += nTnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * 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))); ++ ++ } else { ++ tnl->tnodes = newTnodes; ++ tnl->next = dev->allocatedTnodeList; ++ dev->allocatedTnodeList = tnl; ++ } ++ ++ T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); ++ ++ return YAFFS_OK; ++} ++ ++/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */ ++ ++static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev) ++{ ++ yaffs_Tnode *tn = NULL; ++ ++ /* If there are none left make more */ ++ if (!dev->freeTnodes) { ++ yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ } ++ ++ if (dev->freeTnodes) { ++ tn = dev->freeTnodes; ++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) { ++ /* Hoosterman, this thing looks like it isn't in the list */ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: Tnode list bug 1" TENDSTR))); ++ } ++#endif ++ dev->freeTnodes = dev->freeTnodes->internal[0]; ++ dev->nFreeTnodes--; ++ } ++ ++ return tn; ++} ++ ++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; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn) ++{ ++ if (tn) { ++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) { ++ /* Hoosterman, this thing looks like it is already in the list */ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: Tnode list bug 2" TENDSTR))); ++ } ++ tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1; ++#endif ++ tn->internal[0] = dev->freeTnodes; ++ dev->freeTnodes = tn; ++ dev->nFreeTnodes++; ++ } ++} ++ ++static void yaffs_DeinitialiseTnodes(yaffs_Device * dev) ++{ ++ /* Free the list of allocated tnodes */ ++ yaffs_TnodeList *tmp; ++ ++ while (dev->allocatedTnodeList) { ++ tmp = dev->allocatedTnodeList->next; ++ ++ YFREE(dev->allocatedTnodeList->tnodes); ++ YFREE(dev->allocatedTnodeList); ++ dev->allocatedTnodeList = tmp; ++ ++ } ++ ++ dev->freeTnodes = NULL; ++ dev->nFreeTnodes = 0; ++} ++ ++static void yaffs_InitialiseTnodes(yaffs_Device * dev) ++{ ++ dev->allocatedTnodeList = NULL; ++ dev->freeTnodes = NULL; ++ dev->nFreeTnodes = 0; ++ dev->nTnodesCreated = 0; ++ ++} ++ ++ ++void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val) ++{ ++ __u32 *map = (__u32 *)tn; ++ __u32 bitInMap; ++ __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++;; ++ mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord); ++ map[wordInMap] &= ~mask; ++ map[wordInMap] |= (mask & (val >> bitInWord)); ++ } ++} ++ ++__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; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of topLevel ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, ++ yaffs_FileStructure * fStruct, ++ __u32 chunkId) ++{ ++ ++ yaffs_Tnode *tn = fStruct->top; ++ __u32 i; ++ int requiredTallness; ++ int level = fStruct->topLevel; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) { ++ return NULL; ++ } ++ ++ if (chunkId > YAFFS_MAX_CHUNK_ID) { ++ return NULL; ++ } ++ ++ /* First check we're tall enough (ie enough topLevel) */ ++ ++ i = chunkId >> YAFFS_TNODES_LEVEL0_BITS; ++ requiredTallness = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ requiredTallness++; ++ } ++ ++ if (requiredTallness > fStruct->topLevel) { ++ /* Not tall enough, so we can't find it, return NULL. */ ++ return NULL; ++ } ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn-> ++ internal[(chunkId >> ++ ( YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS) ++ ) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ ++ } ++ ++ return tn; ++} ++ ++/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * 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, ++ yaffs_Tnode *passedTn) ++{ ++ ++ int requiredTallness; ++ int i; ++ int l; ++ yaffs_Tnode *tn; ++ ++ __u32 x; ++ ++ ++ /* Check sane level and page Id */ ++ if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) { ++ return NULL; ++ } ++ ++ if (chunkId > YAFFS_MAX_CHUNK_ID) { ++ return NULL; ++ } ++ ++ /* First check we're tall enough (ie enough topLevel) */ ++ ++ x = chunkId >> YAFFS_TNODES_LEVEL0_BITS; ++ requiredTallness = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ requiredTallness++; ++ } ++ ++ ++ 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) { ++ tn->internal[0] = fStruct->top; ++ fStruct->top = tn; ++ } else { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("yaffs: no more tnodes" TENDSTR))); ++ } ++ } ++ ++ fStruct->topLevel = requiredTallness; ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = fStruct->topLevel; ++ tn = fStruct->top; ++ ++ if(l > 0) { ++ while (l > 0 && tn) { ++ x = (chunkId >> ++ ( YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ ++ if((l>1) && !tn->internal[x]){ ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_GetTnode(dev); ++ ++ } else if(l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passedTn) { ++ /* If we already have one, then release it.*/ ++ 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--; ++ } ++ } else { ++ /* top is level 0 */ ++ if(passedTn) { ++ memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); ++ yaffs_FreeTnode(dev,passedTn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, ++ yaffs_ExtendedTags * tags, int objectId, ++ int chunkInInode) ++{ ++ int j; ++ ++ for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { ++ if (yaffs_CheckChunkBit ++ (dev, theChunk / dev->nChunksPerBlock, ++ theChunk % dev->nChunksPerBlock)) { ++ yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, ++ tags); ++ if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { ++ /* found it; */ ++ return theChunk; ++ ++ } ++ } ++ theChunk++; ++ } ++ return -1; ++} ++ ++ ++/* 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 0 if it stopped early due to hitting the limit and the delete is incomplete. ++ */ ++ ++static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, ++ int chunkOffset, int *limit) ++{ ++ int i; ++ int chunkInInode; ++ int theChunk; ++ yaffs_ExtendedTags tags; ++ int foundChunk; ++ yaffs_Device *dev = in->myDev; ++ ++ int allDone = 1; ++ ++ if (tn) { ++ if (level > 0) { ++ ++ for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ if (limit && (*limit) < 0) { ++ allDone = 0; ++ } else { ++ allDone = ++ yaffs_DeleteWorker(in, ++ tn-> ++ internal ++ [i], ++ level - ++ 1, ++ (chunkOffset ++ << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i, ++ limit); ++ } ++ if (allDone) { ++ yaffs_FreeTnode(dev, ++ tn-> ++ internal[i]); ++ tn->internal[i] = NULL; ++ } ++ } ++ ++ } ++ return (allDone) ? 1 : 0; ++ } else if (level == 0) { ++ int hitLimit = 0; ++ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit; ++ i--) { ++ theChunk = yaffs_GetChunkGroupBase(dev,tn,i); ++ if (theChunk) { ++ ++ chunkInInode = ++ (chunkOffset << ++ YAFFS_TNODES_LEVEL0_BITS) + i; ++ ++ foundChunk = ++ yaffs_FindChunkInGroup(dev, ++ theChunk, ++ &tags, ++ in->objectId, ++ chunkInInode); ++ ++ if (foundChunk > 0) { ++ yaffs_DeleteChunk(dev, ++ foundChunk, 1, ++ __LINE__); ++ in->nDataChunks--; ++ if (limit) { ++ *limit = *limit - 1; ++ if (*limit <= 0) { ++ hitLimit = 1; ++ } ++ } ++ ++ } ++ ++ yaffs_PutLevel0Tnode(dev,tn,i,0); ++ } ++ ++ } ++ return (i < 0) ? 1 : 0; ++ ++ } ++ ++ } ++ ++ return 1; ++ ++} ++ ++static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) ++{ ++ ++ yaffs_BlockInfo *theBlock; ++ ++ T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk)); ++ ++ theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock); ++ if (theBlock) { ++ theBlock->softDeletions++; ++ dev->nFreeChunks++; ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls the chunk out ++ * 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) ++{ ++ int i; ++ int theChunk; ++ int allDone = 1; ++ yaffs_Device *dev = in->myDev; ++ ++ if (tn) { ++ if (level > 0) { ++ ++ for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ allDone = ++ yaffs_SoftDeleteWorker(in, ++ tn-> ++ internal[i], ++ level - 1, ++ (chunkOffset ++ << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (allDone) { ++ yaffs_FreeTnode(dev, ++ tn-> ++ internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Hoosterman... how could this happen? */ ++ } ++ } ++ } ++ return (allDone) ? 1 : 0; ++ } else if (level == 0) { ++ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ 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 ++ * a block. ++ */ ++ yaffs_SoftDeleteChunk(dev, theChunk); ++ yaffs_PutLevel0Tnode(dev,tn,i,0); ++ } ++ ++ } ++ return 1; ++ ++ } ++ ++ } ++ ++ return 1; ++ ++} ++ ++static void yaffs_SoftDeleteFile(yaffs_Object * obj) ++{ ++ if (obj->deleted && ++ obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) { ++ if (obj->nDataChunks <= 0) { ++ /* Empty file with no duplicate object headers, just delete it immediately */ ++ yaffs_FreeTnode(obj->myDev, ++ obj->variant.fileVariant.top); ++ obj->variant.fileVariant.top = NULL; ++ T(YAFFS_TRACE_TRACING, ++ (TSTR("yaffs: Deleting empty file %d" TENDSTR), ++ obj->objectId)); ++ yaffs_DoGenericObjectDeletion(obj); ++ } else { ++ yaffs_SoftDeleteWorker(obj, ++ obj->variant.fileVariant.top, ++ obj->variant.fileVariant. ++ topLevel, 0); ++ obj->softDeleted = 1; ++ } ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ */ ++ ++static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn, ++ __u32 level, int del0) ++{ ++ int i; ++ int hasData; ++ ++ if (tn) { ++ hasData = 0; ++ ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i] && level > 0) { ++ tn->internal[i] = ++ yaffs_PruneWorker(dev, tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) { ++ hasData++; ++ } ++ } ++ ++ if (hasData == 0 && del0) { ++ /* Free and return NULL */ ++ ++ yaffs_FreeTnode(dev, tn); ++ tn = NULL; ++ } ++ ++ } ++ ++ return tn; ++ ++} ++ ++static int yaffs_PruneFileStructure(yaffs_Device * dev, ++ yaffs_FileStructure * fStruct) ++{ ++ int i; ++ int hasData; ++ int done = 0; ++ yaffs_Tnode *tn; ++ ++ if (fStruct->topLevel > 0) { ++ fStruct->top = ++ yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0); ++ ++ /* 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 ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (fStruct->topLevel && !done) { ++ tn = fStruct->top; ++ ++ hasData = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ hasData++; ++ } ++ } ++ ++ if (!hasData) { ++ fStruct->top = tn->internal[0]; ++ fStruct->topLevel--; ++ yaffs_FreeTnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* yaffs_CreateFreeObjects creates a bunch more objects and ++ * adds them to the object free list. ++ */ ++static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) ++{ ++ int i; ++ yaffs_Object *newObjects; ++ yaffs_ObjectList *list; ++ ++ if (nObjects < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); ++ ++ 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 = ++ (struct list_head *)(&newObjects[i + 1]); ++ } ++ ++ newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; ++ dev->freeObjects = newObjects; ++ dev->nFreeObjects += nObjects; ++ dev->nObjectsCreated += nObjects; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ 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; ++} ++ ++ ++/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ ++static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev) ++{ ++ yaffs_Object *tn = NULL; ++ ++ /* If there are none left make more */ ++ if (!dev->freeObjects) { ++ yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); ++ } ++ ++ if (dev->freeObjects) { ++ tn = dev->freeObjects; ++ dev->freeObjects = ++ (yaffs_Object *) (dev->freeObjects->siblings.next); ++ dev->nFreeObjects--; ++ ++ /* Now sweeten it up... */ ++ ++ memset(tn, 0, sizeof(yaffs_Object)); ++ tn->myDev = dev; ++ tn->chunkId = -1; ++ tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(tn->hardLinks)); ++ INIT_LIST_HEAD(&(tn->hashLink)); ++ INIT_LIST_HEAD(&tn->siblings); ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lostNFound in lostNFound so ++ * check if lostNFound exists first ++ */ ++ if (dev->lostNFoundDir) { ++ yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number, ++ __u32 mode) ++{ ++ ++ yaffs_Object *obj = ++ yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (obj) { ++ obj->fake = 1; /* it is fake so it has no NAND presence... */ ++ obj->renameAllowed = 0; /* ... and we're not allowed to rename it... */ ++ obj->unlinkAllowed = 0; /* ... or unlink it */ ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->myDev = dev; ++ obj->chunkId = 0; /* Not a valid chunk. */ ++ } ++ ++ return obj; ++ ++} ++ ++static void yaffs_UnhashObject(yaffs_Object * tn) ++{ ++ int bucket; ++ yaffs_Device *dev = tn->myDev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&tn->hashLink)) { ++ list_del_init(&tn->hashLink); ++ bucket = yaffs_HashFunction(tn->objectId); ++ dev->objectBucket[bucket].count--; ++ } ++ ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_FreeObject(yaffs_Object * tn) ++{ ++ ++ yaffs_Device *dev = tn->myDev; ++ ++#ifdef __KERNEL__ ++ if (tn->myInode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ tn->deferedFree = 1; ++ return; ++ } ++#endif ++ ++ yaffs_UnhashObject(tn); ++ ++ /* Link into the free list. */ ++ tn->siblings.next = (struct list_head *)(dev->freeObjects); ++ dev->freeObjects = tn; ++ dev->nFreeObjects++; ++} ++ ++#ifdef __KERNEL__ ++ ++void yaffs_HandleDeferedFree(yaffs_Object * obj) ++{ ++ if (obj->deferedFree) { ++ yaffs_FreeObject(obj); ++ } ++} ++ ++#endif ++ ++static void yaffs_DeinitialiseObjects(yaffs_Device * dev) ++{ ++ /* Free the list of allocated Objects */ ++ ++ yaffs_ObjectList *tmp; ++ ++ while (dev->allocatedObjectList) { ++ tmp = dev->allocatedObjectList->next; ++ YFREE(dev->allocatedObjectList->objects); ++ YFREE(dev->allocatedObjectList); ++ ++ dev->allocatedObjectList = tmp; ++ } ++ ++ dev->freeObjects = NULL; ++ dev->nFreeObjects = 0; ++} ++ ++static void yaffs_InitialiseObjects(yaffs_Device * dev) ++{ ++ int i; ++ ++ dev->allocatedObjectList = NULL; ++ dev->freeObjects = NULL; ++ dev->nFreeObjects = 0; ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->objectBucket[i].list); ++ dev->objectBucket[i].count = 0; ++ } ++ ++} ++ ++static int yaffs_FindNiceObjectBucket(yaffs_Device * dev) ++{ ++ static int x = 0; ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* First let's see if we can find one that's empty. */ ++ ++ for (i = 0; i < 10 && lowest > 0; i++) { ++ x++; ++ x %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->objectBucket[x].count < lowest) { ++ lowest = dev->objectBucket[x].count; ++ l = x; ++ } ++ ++ } ++ ++ /* If we didn't find an empty list, then try ++ * looking a bit further for a short one ++ */ ++ ++ for (i = 0; i < 10 && lowest > 3; i++) { ++ x++; ++ x %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->objectBucket[x].count < lowest) { ++ lowest = dev->objectBucket[x].count; ++ l = x; ++ } ++ ++ } ++ ++ return l; ++} ++ ++static int yaffs_CreateNewObjectNumber(yaffs_Device * dev) ++{ ++ int bucket = yaffs_FindNiceObjectBucket(dev); ++ ++ /* Now find an object value that has not already been taken ++ * by scanning the list. ++ */ ++ ++ int found = 0; ++ struct list_head *i; ++ ++ __u32 n = (__u32) bucket; ++ ++ /* yaffs_CheckObjectHashSanity(); */ ++ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ if (1 || dev->objectBucket[bucket].count > 0) { ++ list_for_each(i, &dev->objectBucket[bucket].list) { ++ /* If there is already one in the list */ ++ if (i ++ && list_entry(i, yaffs_Object, ++ hashLink)->objectId == n) { ++ found = 0; ++ } ++ } ++ } ++ } ++ ++ ++ return n; ++} ++ ++static void yaffs_HashObject(yaffs_Object * in) ++{ ++ int bucket = yaffs_HashFunction(in->objectId); ++ yaffs_Device *dev = in->myDev; ++ ++ list_add(&in->hashLink, &dev->objectBucket[bucket].list); ++ dev->objectBucket[bucket].count++; ++ ++} ++ ++yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number) ++{ ++ int bucket = yaffs_HashFunction(number); ++ struct list_head *i; ++ yaffs_Object *in; ++ ++ list_for_each(i, &dev->objectBucket[bucket].list) { ++ /* Look if it is in the list */ ++ if (i) { ++ in = list_entry(i, yaffs_Object, hashLink); ++ if (in->objectId == number) { ++#ifdef __KERNEL__ ++ /* Don't tell the VFS about this one if it is defered free */ ++ if (in->deferedFree) ++ return NULL; ++#endif ++ ++ return in; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, ++ yaffs_ObjectType type) ++{ ++ ++ yaffs_Object *theObject; ++ ++ if (number < 0) { ++ number = yaffs_CreateNewObjectNumber(dev); ++ } ++ ++ theObject = yaffs_AllocateEmptyObject(dev); ++ ++ if (theObject) { ++ theObject->fake = 0; ++ theObject->renameAllowed = 1; ++ theObject->unlinkAllowed = 1; ++ theObject->objectId = number; ++ yaffs_HashObject(theObject); ++ theObject->variantType = type; ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_WinFileTimeNow(theObject->win_atime); ++ theObject->win_ctime[0] = theObject->win_mtime[0] = ++ theObject->win_atime[0]; ++ theObject->win_ctime[1] = theObject->win_mtime[1] = ++ theObject->win_atime[1]; ++ ++#else ++ ++ theObject->yst_atime = theObject->yst_mtime = ++ theObject->yst_ctime = Y_CURRENT_TIME; ++#endif ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ theObject->variant.fileVariant.fileSize = 0; ++ theObject->variant.fileVariant.scannedFileSize = 0; ++ theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */ ++ theObject->variant.fileVariant.topLevel = 0; ++ theObject->variant.fileVariant.top = ++ yaffs_GetTnode(dev); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&theObject->variant.directoryVariant. ++ children); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ } ++ ++ return theObject; ++} ++ ++static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev, ++ int number, ++ yaffs_ObjectType type) ++{ ++ yaffs_Object *theObject = NULL; ++ ++ if (number > 0) { ++ theObject = yaffs_FindObjectByNumber(dev, number); ++ } ++ ++ if (!theObject) { ++ theObject = yaffs_CreateNewObject(dev, number, type); ++ } ++ ++ return theObject; ++ ++} ++ ++ ++static YCHAR *yaffs_CloneString(const YCHAR * str) ++{ ++ YCHAR *newStr = NULL; ++ ++ if (str && *str) { ++ newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); ++ yaffs_strcpy(newStr, str); ++ } ++ ++ return newStr; ++ ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equivalentObject only has meaning for a hard link; ++ * 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, ++ __u32 mode, ++ __u32 uid, ++ __u32 gid, ++ yaffs_Object * equivalentObject, ++ const YCHAR * aliasString, __u32 rdev) ++{ ++ yaffs_Object *in; ++ ++ yaffs_Device *dev = parent->myDev; ++ ++ /* Check if the entry exists. If it does then fail the call since we don't want a dup.*/ ++ if (yaffs_FindObjectByName(parent, name)) { ++ return NULL; ++ } ++ ++ in = yaffs_CreateNewObject(dev, -1, type); ++ ++ if (in) { ++ in->chunkId = -1; ++ in->valid = 1; ++ in->variantType = type; ++ ++ in->yst_mode = mode; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_WinFileTimeNow(in->win_atime); ++ in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0]; ++ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1]; ++ ++#else ++ in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME; ++ ++ in->yst_rdev = rdev; ++ in->yst_uid = uid; ++ in->yst_gid = gid; ++#endif ++ in->nDataChunks = 0; ++ ++ yaffs_SetObjectName(in, name); ++ in->dirty = 1; ++ ++ yaffs_AddObjectToDirectory(parent, in); ++ ++ in->myDev = parent->myDev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symLinkVariant.alias = ++ yaffs_CloneString(aliasString); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardLinkVariant.equivalentObject = ++ equivalentObject; ++ in->variant.hardLinkVariant.equivalentObjectId = ++ equivalentObject->objectId; ++ list_add(&in->hardLinks, &equivalentObject->hardLinks); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) { ++ /* Could not create the object header, fail the creation */ ++ yaffs_DestroyObject(in); ++ in = NULL; ++ } ++ ++ } ++ ++ return in; ++} ++ ++yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid) ++{ ++ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid) ++{ ++ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid, __u32 rdev) ++{ ++ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid, ++ const YCHAR * alias) ++{ ++ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_Link returns the object id of the equivalent object.*/ ++yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, ++ yaffs_Object * equivalentObject) ++{ ++ /* Get the real object in case we were fed a hard link as an equivalent object */ ++ equivalentObject = yaffs_GetEquivalentObject(equivalentObject); ++ ++ if (yaffs_MknodObject ++ (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, ++ equivalentObject, NULL, 0)) { ++ return equivalentObject; ++ } else { ++ return NULL; ++ } ++ ++} ++ ++static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir, ++ const YCHAR * newName, int force, int shadows) ++{ ++ int unlinkOp; ++ int deleteOp; ++ ++ yaffs_Object *existingTarget; ++ ++ if (newDir == NULL) { ++ newDir = obj->parent; /* use the old directory */ ++ } ++ ++ if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragendy: yaffs_ChangeObjectName: newDir is not a directory" ++ TENDSTR))); ++ YBUG(); ++ } ++ ++ /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ ++ if (obj->myDev->isYaffs2) { ++ unlinkOp = (newDir == obj->myDev->unlinkedDir); ++ } else { ++ unlinkOp = (newDir == obj->myDev->unlinkedDir ++ && obj->variantType == YAFFS_OBJECT_TYPE_FILE); ++ } ++ ++ deleteOp = (newDir == obj->myDev->deletedDir); ++ ++ existingTarget = yaffs_FindObjectByName(newDir, newName); ++ ++ /* 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 ++ * it into a directory. ++ */ ++ if ((unlinkOp || ++ deleteOp || ++ force || ++ (shadows > 0) || ++ !existingTarget) && ++ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_SetObjectName(obj, newName); ++ obj->dirty = 1; ++ ++ yaffs_AddObjectToDirectory(newDir, obj); ++ ++ if (unlinkOp) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc purposes. */ ++ if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0) ++ return YAFFS_OK; ++ } ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, ++ yaffs_Object * newDir, const YCHAR * newName) ++{ ++ yaffs_Object *obj; ++ yaffs_Object *existingTarget; ++ int force = 0; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems (eg. WinCE). ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) { ++ force = 1; ++ } ++#endif ++ ++ obj = yaffs_FindObjectByName(oldDir, oldName); ++ /* Check new name to long. */ ++ if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK && ++ yaffs_strlen(newName) > YAFFS_MAX_ALIAS_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ else if (obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK && ++ yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (obj && obj->renameAllowed) { ++ ++ /* Now do the handling for an existing target, if there is one */ ++ ++ existingTarget = yaffs_FindObjectByName(newDir, newName); ++ if (existingTarget && ++ existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&existingTarget->variant.directoryVariant.children)) { ++ /* 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, ++ * but only if it isn't the same object ++ */ ++ yaffs_ChangeObjectName(obj, newDir, newName, force, ++ existingTarget->objectId); ++ yaffs_UnlinkObject(existingTarget); ++ } ++ ++ return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); ++ } ++ return YAFFS_FAIL; ++} ++ ++/*------------------------- Block Management and Page Allocation ----------------*/ ++ ++static int yaffs_InitialiseBlocks(yaffs_Device * dev) ++{ ++ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; ++ ++ dev->allocationBlock = -1; /* force it to get a new 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)); ++ dev->blockInfoAlt = 1; ++ } ++ else ++ dev->blockInfoAlt = 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); ++ return YAFFS_OK; ++ } ++ ++ return YAFFS_FAIL; ++ ++} ++ ++static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) ++{ ++ if(dev->blockInfoAlt) ++ YFREE_ALT(dev->blockInfo); ++ else ++ YFREE(dev->blockInfo); ++ dev->blockInfoAlt = 0; ++ ++ dev->blockInfo = NULL; ++ ++ if(dev->chunkBitsAlt) ++ YFREE_ALT(dev->chunkBits); ++ else ++ YFREE(dev->chunkBits); ++ dev->chunkBitsAlt = 0; ++ dev->chunkBits = NULL; ++} ++ ++static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev, ++ yaffs_BlockInfo * bi) ++{ ++ int i; ++ __u32 seq; ++ yaffs_BlockInfo *b; ++ ++ if (!dev->isYaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->hasShrinkHeader) ++ return 1; /* can gc */ ++ ++ /* Find the oldest dirty sequence number if we don't know it and save it ++ * so we don't have to keep recomputing it. ++ */ ++ if (!dev->oldestDirtySequence) { ++ seq = dev->sequenceNumber; ++ ++ for (i = dev->internalStartBlock; i <= dev->internalEndBlock; ++ i++) { ++ b = yaffs_GetBlockInfo(dev, i); ++ if (b->blockState == YAFFS_BLOCK_STATE_FULL && ++ (b->pagesInUse - b->softDeletions) < ++ dev->nChunksPerBlock && b->sequenceNumber < seq) { ++ seq = b->sequenceNumber; ++ } ++ } ++ dev->oldestDirtySequence = seq; ++ } ++ ++ /* Can't do gc of this block if there are any blocks older than this one that have ++ * discarded pages. ++ */ ++ return (bi->sequenceNumber <= dev->oldestDirtySequence); ++ ++} ++ ++/* FindDiretiestBlock is used to select the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, ++ int aggressive) ++{ ++ ++ int b = dev->currentDirtyChecker; ++ ++ int i; ++ int iterations; ++ int dirtiest = -1; ++ 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); ++ if(bi->gcPrioritise) { ++ pendingPrioritisedExist = 1; ++ if(bi->blockState == YAFFS_BLOCK_STATE_FULL && ++ yaffs_BlockNotDisqualifiedFromGC(dev, bi)){ ++ pagesInUse = (bi->pagesInUse - bi->softDeletions); ++ dirtiest = i; ++ prioritised = 1; ++ aggressive = 1; /* Fool the non-aggressive skip logiv below */ ++ } ++ } ++ } ++ ++ if(!pendingPrioritisedExist) /* None found, so we can clear this */ ++ dev->hasPendingPrioritisedGCs = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty block, and ++ * search harder. ++ * else (we're doing a leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ nonAggressiveSkip--; ++ ++ if (!aggressive && (nonAggressiveSkip > 0)) { ++ return -1; ++ } ++ ++ if(!prioritised) ++ pagesInUse = ++ (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; ++ ++ if (aggressive) { ++ iterations = ++ dev->internalEndBlock - dev->internalStartBlock + 1; ++ } else { ++ iterations = ++ dev->internalEndBlock - dev->internalStartBlock + 1; ++ iterations = iterations / 16; ++ if (iterations > 200) { ++ iterations = 200; ++ } ++ } ++ ++ for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { ++ b++; ++ if (b < dev->internalStartBlock || b > dev->internalEndBlock) { ++ b = dev->internalStartBlock; ++ } ++ ++ if (b < dev->internalStartBlock || b > dev->internalEndBlock) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("**>> Block %d is not valid" TENDSTR), b)); ++ YBUG(); ++ } ++ ++ bi = yaffs_GetBlockInfo(dev, b); ++ ++#if 0 ++ if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dirtiest = b; ++ pagesInUse = 0; ++ } ++ else ++#endif ++ ++ if (bi->blockState == YAFFS_BLOCK_STATE_FULL && ++ (bi->pagesInUse - bi->softDeletions) < pagesInUse && ++ yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { ++ dirtiest = b; ++ pagesInUse = (bi->pagesInUse - bi->softDeletions); ++ } ++ } ++ ++ dev->currentDirtyChecker = b; ++ ++ if (dirtiest > 0) { ++ T(YAFFS_TRACE_GC, ++ (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest, ++ dev->nChunksPerBlock - pagesInUse,prioritised)); ++ } ++ ++ dev->oldestDirtySequence = 0; ++ ++ if (dirtiest > 0) { ++ nonAggressiveSkip = 4; ++ } ++ ++ return dirtiest; ++} ++ ++static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) ++{ ++ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo); ++ ++ int erasedOk = 0; ++ ++ /* 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) { ++ yaffs_InvalidateCheckpoint(dev); ++ erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); ++ if (!erasedOk) { ++ dev->nErasureFailures++; ++ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ (TSTR("**>> Erasure failed %d" TENDSTR), blockNo)); ++ } ++ } ++ ++ if (erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE)) { ++ int i; ++ for (i = 0; i < dev->nChunksPerBlock; i++) { ++ if (!yaffs_CheckChunkErased ++ (dev, blockNo * dev->nChunksPerBlock + i)) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ (">>Block %d erasure supposedly OK, but chunk %d not erased" ++ TENDSTR), blockNo, i)); ++ } ++ } ++ } ++ ++ if (erasedOk) { ++ /* Clean it up... */ ++ bi->blockState = YAFFS_BLOCK_STATE_EMPTY; ++ dev->nErasedBlocks++; ++ bi->pagesInUse = 0; ++ bi->softDeletions = 0; ++ bi->hasShrinkHeader = 0; ++ bi->skipErasedCheck = 1; /* This is clean, so no need to check */ ++ bi->gcPrioritise = 0; ++ yaffs_ClearChunkBits(dev, blockNo); ++ ++ T(YAFFS_TRACE_ERASE, ++ (TSTR("Erased block %d" TENDSTR), blockNo)); ++ } else { ++ dev->nFreeChunks -= dev->nChunksPerBlock; /* We lost a block of free space */ ++ ++ yaffs_RetireBlock(dev, blockNo); ++ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ (TSTR("**>> Block %d retired" TENDSTR), blockNo)); ++ } ++} ++ ++static int yaffs_FindBlockForAllocation(yaffs_Device * dev) ++{ ++ int i; ++ ++ yaffs_BlockInfo *bi; ++ ++ if (dev->nErasedBlocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR))); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { ++ dev->allocationBlockFinder++; ++ if (dev->allocationBlockFinder < dev->internalStartBlock ++ || dev->allocationBlockFinder > dev->internalEndBlock) { ++ dev->allocationBlockFinder = dev->internalStartBlock; ++ } ++ ++ bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder); ++ ++ if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->sequenceNumber++; ++ bi->sequenceNumber = dev->sequenceNumber; ++ dev->nErasedBlocks--; ++ T(YAFFS_TRACE_ALLOCATE, ++ (TSTR("Allocated block %d, seq %d, %d left" TENDSTR), ++ dev->allocationBlockFinder, dev->sequenceNumber, ++ dev->nErasedBlocks)); ++ return dev->allocationBlockFinder; ++ } ++ } ++ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("yaffs tragedy: no more eraased blocks, but there should have been %d" ++ TENDSTR), dev->nErasedBlocks)); ++ ++ return -1; ++} ++ ++ ++// Check if there's space to allocate... ++// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()? ++static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev) ++{ ++ 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); ++} ++ ++static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) ++{ ++ int retVal; ++ yaffs_BlockInfo *bi; ++ ++ if (dev->allocationBlock < 0) { ++ /* Get next block to allocate off */ ++ dev->allocationBlock = yaffs_FindBlockForAllocation(dev); ++ dev->allocationPage = 0; ++ } ++ ++ if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) { ++ /* Not enough space to allocate unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->nErasedBlocks < dev->nReservedBlocks ++ && dev->allocationPage == 0) { ++ T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR))); ++ } ++ ++ /* Next page please.... */ ++ if (dev->allocationBlock >= 0) { ++ bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); ++ ++ retVal = (dev->allocationBlock * dev->nChunksPerBlock) + ++ dev->allocationPage; ++ bi->pagesInUse++; ++ yaffs_SetChunkBit(dev, dev->allocationBlock, ++ dev->allocationPage); ++ ++ dev->allocationPage++; ++ ++ dev->nFreeChunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->allocationPage >= dev->nChunksPerBlock) { ++ bi->blockState = YAFFS_BLOCK_STATE_FULL; ++ dev->allocationBlock = -1; ++ } ++ ++ if(blockUsedPtr) ++ *blockUsedPtr = bi; ++ ++ return retVal; ++ } ++ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); ++ ++ return -1; ++} ++ ++static int yaffs_GetErasedChunks(yaffs_Device * dev) ++{ ++ int n; ++ ++ n = dev->nErasedBlocks * dev->nChunksPerBlock; ++ ++ if (dev->allocationBlock > 0) { ++ n += (dev->nChunksPerBlock - dev->allocationPage); ++ } ++ ++ return n; ++ ++} ++ ++static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) ++{ ++ int oldChunk; ++ int newChunk; ++ int chunkInBlock; ++ int markNAND; ++ int retVal = YAFFS_OK; ++ int cleanups = 0; ++ int i; ++ int isCheckpointBlock; ++ ++ int chunksBefore = yaffs_GetErasedChunks(dev); ++ int chunksAfter; ++ ++ yaffs_ExtendedTags tags; ++ ++ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block); ++ ++ yaffs_Object *object; ++ ++ isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ T(YAFFS_TRACE_TRACING, ++ (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block, ++ bi->pagesInUse, bi->hasShrinkHeader)); ++ ++ /*yaffs_VerifyFreeChunks(dev); */ ++ ++ bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ ++ ++ /* Take off the number of soft deleted entries because ++ * they're going to get really deleted during GC. ++ */ ++ dev->nFreeChunks -= bi->softDeletions; ++ ++ dev->isDoingGC = 1; ++ ++ if (isCheckpointBlock || ++ !yaffs_StillSomeChunkBits(dev, block)) { ++ T(YAFFS_TRACE_TRACING, ++ (TSTR ++ ("Collecting block %d that has no chunks in use" TENDSTR), ++ block)); ++ yaffs_BlockBecameDirty(dev, block); ++ } else { ++ ++ __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); ++ ++ for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; ++ chunkInBlock < dev->nChunksPerBlock ++ && yaffs_StillSomeChunkBits(dev, block); ++ chunkInBlock++, oldChunk++) { ++ if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) { ++ ++ /* This page is in use and might need to be copied off */ ++ ++ markNAND = 1; ++ ++ yaffs_InitialiseTags(&tags); ++ ++ yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk, ++ buffer, &tags); ++ ++ object = ++ yaffs_FindObjectByNumber(dev, ++ tags.objectId); ++ ++ T(YAFFS_TRACE_GC_DETAIL, ++ (TSTR ++ ("Collecting page %d, %d %d %d " TENDSTR), ++ chunkInBlock, tags.objectId, tags.chunkId, ++ tags.byteCount)); ++ ++ if (!object) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("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 ++ * fix up the object. ++ */ ++ ++ object->nDataChunks--; ++ ++ if (object->nDataChunks <= 0) { ++ /* remeber to clean up the object */ ++ dev->gcCleanupList[cleanups] = ++ tags.objectId; ++ cleanups++; ++ } ++ markNAND = 0; ++ } else if (0 ++ /* Todo object && object->deleted && object->nDataChunks == 0 */ ++ ) { ++ /* Deleted object header with no data chunks. ++ * Can be discarded and the file deleted. ++ */ ++ object->chunkId = 0; ++ yaffs_FreeTnode(object->myDev, ++ object->variant. ++ fileVariant.top); ++ object->variant.fileVariant.top = NULL; ++ yaffs_DoGenericObjectDeletion(object); ++ ++ } else if (object) { ++ /* It's either a data chunk in a live file or ++ * an ObjectHeader, so we're interested in it. ++ * NB Need to keep the ObjectHeaders of deleted files ++ * until the whole file has been deleted off ++ */ ++ tags.serialNumber++; ++ ++ dev->nGCCopies++; ++ ++ if (tags.chunkId == 0) { ++ /* It is an object Id, ++ * We need to nuke the shrinkheader flags first ++ * We no longer want the shrinkHeader flag since its work is done ++ * and if it is left in place it will mess up scanning. ++ * Also, clear out any shadowing stuff ++ */ ++ ++ yaffs_ObjectHeader *oh; ++ oh = (yaffs_ObjectHeader *)buffer; ++ oh->isShrink = 0; ++ oh->shadowsObject = -1; ++ tags.extraShadows = 0; ++ tags.extraIsShrinkHeader = 0; ++ } ++ ++ newChunk = ++ yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); ++ ++ if (newChunk < 0) { ++ retVal = YAFFS_FAIL; ++ } else { ++ ++ /* Ok, now fix up the Tnodes etc. */ ++ ++ if (tags.chunkId == 0) { ++ /* It's a header */ ++ object->chunkId = newChunk; ++ object->serial = tags.serialNumber; ++ } else { ++ /* It's a data chunk */ ++ yaffs_PutChunkIntoFile ++ (object, ++ tags.chunkId, ++ newChunk, 0); ++ } ++ } ++ } ++ ++ yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__); ++ ++ } ++ } ++ ++ yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); ++ ++ ++ /* Do any required cleanups */ ++ for (i = 0; i < cleanups; i++) { ++ /* Time to delete the file too */ ++ object = ++ yaffs_FindObjectByNumber(dev, ++ dev->gcCleanupList[i]); ++ if (object) { ++ yaffs_FreeTnode(dev, ++ object->variant.fileVariant. ++ top); ++ object->variant.fileVariant.top = NULL; ++ T(YAFFS_TRACE_GC, ++ (TSTR ++ ("yaffs: About to finally delete object %d" ++ TENDSTR), object->objectId)); ++ yaffs_DoGenericObjectDeletion(object); ++ object->myDev->nDeletedFiles--; ++ } ++ ++ } ++ ++ } ++ ++ if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { ++ T(YAFFS_TRACE_GC, ++ (TSTR ++ ("gc did not increase free chunks before %d after %d" ++ TENDSTR), chunksBefore, chunksAfter)); ++ } ++ ++ dev->isDoingGC = 0; ++ ++ return YAFFS_OK; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and will only accept more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_CheckGarbageCollection(yaffs_Device * dev) ++{ ++ int block; ++ 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)) { ++ /* We need a block soon...*/ ++ aggressive = 1; ++ } else { ++ /* We're in no hurry */ ++ aggressive = 0; ++ } ++ ++ block = yaffs_FindBlockForGarbageCollection(dev, aggressive); ++ ++ if (block > 0) { ++ dev->garbageCollections++; ++ if (!aggressive) { ++ dev->passiveGarbageCollections++; ++ } ++ ++ T(YAFFS_TRACE_GC, ++ (TSTR ++ ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR), ++ dev->nErasedBlocks, aggressive)); ++ ++ gcOk = yaffs_GarbageCollectBlock(dev, block); ++ } ++ ++ if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) { ++ T(YAFFS_TRACE_GC, ++ (TSTR ++ ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" ++ TENDSTR), dev->nErasedBlocks, maxTries, block)); ++ } ++ } while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) ++ && (maxTries < 2)); ++ ++ return aggressive ? gcOk : YAFFS_OK; ++} ++ ++/*------------------------- TAGS --------------------------------*/ ++ ++static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, ++ int chunkInObject) ++{ ++ return (tags->chunkId == chunkInObject && ++ tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0; ++ ++} ++ ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, ++ yaffs_ExtendedTags * tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ yaffs_Tnode *tn; ++ int theChunk = -1; ++ yaffs_ExtendedTags localTags; ++ int retVal = -1; ++ ++ yaffs_Device *dev = in->myDev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &localTags; ++ } ++ ++ tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); ++ ++ if (tn) { ++ theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); ++ ++ retVal = ++ yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, ++ chunkInInode); ++ } ++ return retVal; ++} ++ ++static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode, ++ yaffs_ExtendedTags * tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ yaffs_Tnode *tn; ++ int theChunk = -1; ++ yaffs_ExtendedTags localTags; ++ ++ yaffs_Device *dev = in->myDev; ++ int retVal = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &localTags; ++ } ++ ++ tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); ++ ++ if (tn) { ++ ++ theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); ++ ++ retVal = ++ yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, ++ chunkInInode); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (retVal != -1) { ++ yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0); ++ } ++ } else { ++ /*T(("No level 0 found for %d\n", chunkInInode)); */ ++ } ++ ++ if (retVal == -1) { ++ /* T(("Could not find %d to delete\n",chunkInInode)); */ ++ } ++ return retVal; ++} ++ ++#ifdef YAFFS_PARANOID ++ ++static int yaffs_CheckFileSanity(yaffs_Object * in) ++{ ++ int chunk; ++ int nChunks; ++ int fSize; ++ int failed = 0; ++ int objId; ++ yaffs_Tnode *tn; ++ yaffs_Tags localTags; ++ yaffs_Tags *tags = &localTags; ++ int theChunk; ++ int chunkDeleted; ++ ++ if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { ++ /* T(("Object not a file\n")); */ ++ return YAFFS_FAIL; ++ } ++ ++ objId = in->objectId; ++ fSize = in->variant.fileVariant.fileSize; ++ nChunks = ++ (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; ++ ++ for (chunk = 1; chunk <= nChunks; chunk++) { ++ tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, ++ chunk); ++ ++ if (tn) { ++ ++ theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk); ++ ++ if (yaffs_CheckChunkBits ++ (dev, theChunk / dev->nChunksPerBlock, ++ theChunk % dev->nChunksPerBlock)) { ++ ++ yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, ++ tags, ++ &chunkDeleted); ++ if (yaffs_TagsMatch ++ (tags, in->objectId, chunk, chunkDeleted)) { ++ /* found it; */ ++ ++ } ++ } else { ++ ++ failed = 1; ++ } ++ ++ } else { ++ /* T(("No level 0 found for %d\n", chunk)); */ ++ } ++ } ++ ++ return failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++#endif ++ ++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; ++ * for backward scanning inScan is < 0 ++ */ ++ ++ yaffs_Tnode *tn; ++ yaffs_Device *dev = in->myDev; ++ int existingChunk; ++ yaffs_ExtendedTags existingTags; ++ yaffs_ExtendedTags newTags; ++ unsigned existingSerial, newSerial; ++ ++ if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file during scanning ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!inScan) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("yaffs tragedy:attempt to put data chunk into a non-file" ++ TENDSTR))); ++ YBUG(); ++ } ++ ++ yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_AddOrFindLevel0Tnode(dev, ++ &in->variant.fileVariant, ++ chunkInInode, ++ NULL); ++ if (!tn) { ++ return YAFFS_FAIL; ++ } ++ ++ existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); ++ ++ 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 ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags so this is quite cheap. ++ */ ++ ++ if (existingChunk != 0) { ++ /* NB Right now existing chunk will not be real chunkId if the device >= 32MB ++ * thus we have to do a FindChunkInFile to get the real chunk id. ++ * ++ * We have a duplicate now we need to decide which one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what we use, dump the new one. ++ * Forward scanning YAFFS2: The new one is what we use, dump the old one. ++ * YAFFS1: Get both sets of tags and compare serial numbers. ++ */ ++ ++ if (inScan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_ReadChunkWithTagsFromNAND(dev, ++ chunkInNAND, ++ NULL, &newTags); ++ ++ /* Do a proper find */ ++ existingChunk = ++ yaffs_FindChunkInFile(in, chunkInInode, ++ &existingTags); ++ } ++ ++ if (existingChunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("yaffs tragedy: existing chunk < 0 in scan" ++ TENDSTR))); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise the chunks will ++ * not be loaded during a scan ++ */ ++ ++ newSerial = newTags.serialNumber; ++ existingSerial = existingTags.serialNumber; ++ ++ if ((inScan > 0) && ++ (in->myDev->isYaffs2 || ++ existingChunk <= 0 || ++ ((existingSerial + 1) & 3) == newSerial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to update the tnode ++ */ ++ yaffs_DeleteChunk(dev, existingChunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the existing one ++ * Use existing. ++ * Delete the new one and return early so that the tnode isn't changed ++ */ ++ yaffs_DeleteChunk(dev, chunkInNAND, 1, ++ __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existingChunk == 0) { ++ in->nDataChunks++; ++ } ++ ++ yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode, ++ __u8 * buffer) ++{ ++ int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL); ++ ++ if (chunkInNAND >= 0) { ++ return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND, ++ buffer,NULL); ++ } else { ++ T(YAFFS_TRACE_NANDACCESS, ++ (TSTR("Chunk %d not found zero instead" TENDSTR), ++ chunkInNAND)); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->myDev->nDataBytesPerChunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) ++{ ++ int block; ++ int page; ++ yaffs_ExtendedTags tags; ++ yaffs_BlockInfo *bi; ++ ++ if (chunkId <= 0) ++ return; ++ ++ dev->nDeletions++; ++ block = chunkId / dev->nChunksPerBlock; ++ page = chunkId % dev->nChunksPerBlock; ++ ++ bi = yaffs_GetBlockInfo(dev, block); ++ ++ T(YAFFS_TRACE_DELETION, ++ (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); ++ ++ if (markNAND && ++ bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) { ++ ++ yaffs_InitialiseTags(&tags); ++ ++ tags.chunkDeleted = 1; ++ ++ yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags); ++ yaffs_HandleUpdateChunk(dev, chunkId, &tags); ++ } else { ++ dev->nUnmarkedDeletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->blockState == YAFFS_BLOCK_STATE_FULL || ++ bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING || ++ bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->nFreeChunks++; ++ ++ yaffs_ClearChunkBit(dev, block, page); ++ ++ bi->pagesInUse--; ++ ++ if (bi->pagesInUse == 0 && ++ !bi->hasShrinkHeader && ++ bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ yaffs_BlockBecameDirty(dev, block); ++ } ++ ++ } else { ++ /* T(("Bad news deleting chunk %d\n",chunkId)); */ ++ } ++ ++} ++ ++static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode, ++ const __u8 * buffer, int nBytes, ++ int useReserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prevChunkId; ++ yaffs_ExtendedTags prevTags; ++ ++ int newChunkId; ++ yaffs_ExtendedTags newTags; ++ ++ yaffs_Device *dev = in->myDev; ++ ++ yaffs_CheckGarbageCollection(dev); ++ ++ /* Get the previous chunk at this location in the file if it exists */ ++ prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags); ++ ++ /* Set up new tags */ ++ yaffs_InitialiseTags(&newTags); ++ ++ newTags.chunkId = chunkInInode; ++ newTags.objectId = in->objectId; ++ newTags.serialNumber = ++ (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; ++ newTags.byteCount = nBytes; ++ ++ newChunkId = ++ yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, ++ useReserve); ++ ++ if (newChunkId >= 0) { ++ yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0); ++ ++ if (prevChunkId >= 0) { ++ yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); ++ ++ } ++ ++ yaffs_CheckFileSanity(in); ++ } ++ return newChunkId; ++ ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, ++ int isShrink, int shadows) ++{ ++ ++ yaffs_BlockInfo *bi; ++ ++ yaffs_Device *dev = in->myDev; ++ ++ int prevChunkId; ++ int retVal = 0; ++ int result = 0; ++ ++ int newChunkId; ++ yaffs_ExtendedTags newTags; ++ ++ __u8 *buffer = NULL; ++ YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ yaffs_ObjectHeader *oh = NULL; ++ ++ if (!in->fake || force) { ++ ++ yaffs_CheckGarbageCollection(dev); ++ ++ buffer = yaffs_GetTempBuffer(in->myDev, __LINE__); ++ oh = (yaffs_ObjectHeader *) buffer; ++ ++ prevChunkId = in->chunkId; ++ ++ if (prevChunkId >= 0) { ++ result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, ++ buffer, NULL); ++ memcpy(oldName, oh->name, sizeof(oh->name)); ++ } ++ ++ memset(buffer, 0xFF, dev->nDataBytesPerChunk); ++ ++ oh->type = in->variantType; ++ oh->yst_mode = in->yst_mode; ++ oh->shadowsObject = shadows; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ oh->win_atime[0] = in->win_atime[0]; ++ oh->win_ctime[0] = in->win_ctime[0]; ++ oh->win_mtime[0] = in->win_mtime[0]; ++ oh->win_atime[1] = in->win_atime[1]; ++ oh->win_ctime[1] = in->win_ctime[1]; ++ oh->win_mtime[1] = in->win_mtime[1]; ++#else ++ oh->yst_uid = in->yst_uid; ++ oh->yst_gid = in->yst_gid; ++ oh->yst_atime = in->yst_atime; ++ oh->yst_mtime = in->yst_mtime; ++ oh->yst_ctime = in->yst_ctime; ++ oh->yst_rdev = in->yst_rdev; ++#endif ++ if (in->parent) { ++ oh->parentObjectId = in->parent->objectId; ++ } else { ++ oh->parentObjectId = 0; ++ } ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); ++ } else if (prevChunkId) { ++ memcpy(oh->name, oldName, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->isShrink = isShrink; ++ ++ switch (in->variantType) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ oh->fileSize = ++ (oh->parentObjectId == YAFFS_OBJECTID_DELETED ++ || oh->parentObjectId == ++ YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant. ++ fileVariant.fileSize; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equivalentObjectId = ++ in->variant.hardLinkVariant.equivalentObjectId; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_strncpy(oh->alias, ++ in->variant.symLinkVariant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* Tags */ ++ yaffs_InitialiseTags(&newTags); ++ in->serial++; ++ newTags.chunkId = 0; ++ newTags.objectId = in->objectId; ++ newTags.serialNumber = in->serial; ++ ++ /* Add extra info for file header */ ++ ++ newTags.extraHeaderInfoAvailable = 1; ++ newTags.extraParentObjectId = oh->parentObjectId; ++ newTags.extraFileLength = oh->fileSize; ++ newTags.extraIsShrinkHeader = oh->isShrink; ++ newTags.extraEquivalentObjectId = oh->equivalentObjectId; ++ newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; ++ newTags.extraObjectType = in->variantType; ++ ++ /* Create new chunk in NAND */ ++ newChunkId = ++ yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, ++ (prevChunkId >= 0) ? 1 : 0); ++ ++ if (newChunkId >= 0) { ++ ++ in->chunkId = newChunkId; ++ ++ if (prevChunkId >= 0) { ++ yaffs_DeleteChunk(dev, prevChunkId, 1, ++ __LINE__); ++ } ++ ++ if(!yaffs_ObjectHasCachedWriteData(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block that the chunk lives on */ ++ if (isShrink) { ++ bi = yaffs_GetBlockInfo(in->myDev, ++ newChunkId /in->myDev-> nChunksPerBlock); ++ bi->hasShrinkHeader = 1; ++ } ++ ++ } ++ ++ retVal = newChunkId; ++ ++ } ++ ++ if (buffer) ++ yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); ++ ++ return retVal; ++} ++ ++/*------------------------ 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 ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * 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 ++ * buffering. ++ * ++ * There are a limited number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj) ++{ ++ yaffs_Device *dev = obj->myDev; ++ 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; ++} ++ ++ ++static void yaffs_FlushFilesChunkCache(yaffs_Object * obj) ++{ ++ yaffs_Device *dev = obj->myDev; ++ int lowest = -99; /* Stop compiler whining. */ ++ int i; ++ yaffs_ChunkCache *cache; ++ int chunkWritten = 0; ++ int nCaches = obj->myDev->nShortOpCaches; ++ ++ if (nCaches > 0) { ++ do { ++ cache = NULL; ++ ++ /* Find the dirty cache for this object with the lowest chunk id. */ ++ for (i = 0; i < nCaches; i++) { ++ if (dev->srCache[i].object == obj && ++ dev->srCache[i].dirty) { ++ if (!cache ++ || dev->srCache[i].chunkId < ++ lowest) { ++ cache = &dev->srCache[i]; ++ lowest = cache->chunkId; ++ } ++ } ++ } ++ ++ if (cache && !cache->locked) { ++ /* Write it out and free it up */ ++ ++ chunkWritten = ++ yaffs_WriteChunkDataToObject(cache->object, ++ cache->chunkId, ++ cache->data, ++ cache->nBytes, ++ 1); ++ cache->dirty = 0; ++ cache->object = NULL; ++ } ++ ++ } while (cache && chunkWritten > 0); ++ ++ if (cache) { ++ /* Hoosterman, disk full while writing cache out. */ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("yaffs tragedy: no space during cache write" TENDSTR))); ++ ++ } ++ } ++ ++} ++ ++/*yaffs_FlushEntireDeviceCache(dev) ++ * ++ * ++ */ ++ ++void yaffs_FlushEntireDeviceCache(yaffs_Device *dev) ++{ ++ 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. ++ */ ++ do { ++ obj = NULL; ++ for( i = 0; i < nCaches && !obj; i++) { ++ 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. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev) ++{ ++ int i; ++ int usage; ++ int theOne; ++ ++ if (dev->nShortOpCaches > 0) { ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if (!dev->srCache[i].object) ++ return &dev->srCache[i]; ++ } ++ ++ return NULL; ++ ++ theOne = -1; ++ usage = 0; /* just to stop the compiler grizzling */ ++ ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if (!dev->srCache[i].dirty && ++ ((dev->srCache[i].lastUse < usage && theOne >= 0) || ++ theOne < 0)) { ++ usage = dev->srCache[i].lastUse; ++ theOne = i; ++ } ++ } ++ ++ ++ return theOne >= 0 ? &dev->srCache[theOne] : NULL; ++ } else { ++ return NULL; ++ } ++ ++} ++ ++static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev) ++{ ++ yaffs_ChunkCache *cache; ++ yaffs_Object *theObj; ++ int usage; ++ int i; ++ int pushout; ++ ++ if (dev->nShortOpCaches > 0) { ++ /* Try find a non-dirty one... */ ++ ++ cache = yaffs_GrabChunkCacheWorker(dev); ++ ++ if (!cache) { ++ /* They were all dirty, find the last recently used object and flush ++ * its cache, then find again. ++ * NB what's here is not very accurate, we actually flush the object ++ * the last recently used page. ++ */ ++ ++ /* With locking we can't assume we can use entry zero */ ++ ++ theObj = NULL; ++ usage = -1; ++ cache = NULL; ++ pushout = -1; ++ ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if (dev->srCache[i].object && ++ !dev->srCache[i].locked && ++ (dev->srCache[i].lastUse < usage || !cache)) ++ { ++ usage = dev->srCache[i].lastUse; ++ theObj = dev->srCache[i].object; ++ cache = &dev->srCache[i]; ++ pushout = i; ++ } ++ } ++ ++ if (!cache || cache->dirty) { ++ /* Flush and try again */ ++ yaffs_FlushFilesChunkCache(theObj); ++ cache = yaffs_GrabChunkCacheWorker(dev); ++ } ++ ++ } ++ return cache; ++ } else ++ return NULL; ++ ++} ++ ++/* Find a cached chunk */ ++static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj, ++ int chunkId) ++{ ++ yaffs_Device *dev = obj->myDev; ++ int i; ++ if (dev->nShortOpCaches > 0) { ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if (dev->srCache[i].object == obj && ++ dev->srCache[i].chunkId == chunkId) { ++ dev->cacheHits++; ++ ++ return &dev->srCache[i]; ++ } ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache, ++ int isAWrite) ++{ ++ ++ if (dev->nShortOpCaches > 0) { ++ if (dev->srLastUse < 0 || dev->srLastUse > 100000000) { ++ /* Reset the cache usages */ ++ int i; ++ for (i = 1; i < dev->nShortOpCaches; i++) { ++ dev->srCache[i].lastUse = 0; ++ } ++ dev->srLastUse = 0; ++ } ++ ++ dev->srLastUse++; ++ ++ cache->lastUse = dev->srLastUse; ++ ++ if (isAWrite) { ++ cache->dirty = 1; ++ } ++ } ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId) ++{ ++ if (object->myDev->nShortOpCaches > 0) { ++ yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId); ++ ++ if (cache) { ++ cache->object = NULL; ++ } ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in) ++{ ++ int i; ++ yaffs_Device *dev = in->myDev; ++ ++ if (dev->nShortOpCaches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ if (dev->srCache[i].object == in) { ++ dev->srCache[i].object = NULL; ++ } ++ } ++ } ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++ ++static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) ++{ ++ yaffs_CheckpointValidity 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; ++} ++ ++static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) ++{ ++ 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) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++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, ++ yaffs_CheckpointDevice *cp) ++{ ++ dev->nErasedBlocks = cp->nErasedBlocks; ++ dev->allocationBlock = cp->allocationBlock; ++ dev->allocationPage = cp->allocationPage; ++ dev->nFreeChunks = cp->nFreeChunks; ++ ++ dev->nDeletedFiles = cp->nDeletedFiles; ++ dev->nUnlinkedFiles = cp->nUnlinkedFiles; ++ dev->nBackgroundDeletions = cp->nBackgroundDeletions; ++ dev->sequenceNumber = cp->sequenceNumber; ++ dev->oldestDirtySequence = cp->oldestDirtySequence; ++} ++ ++ ++static int yaffs_WriteCheckpointDevice(yaffs_Device *dev) ++{ ++ yaffs_CheckpointDevice cp; ++ __u32 nBytes; ++ __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 */ ++ if(ok) { ++ nBytes = nBlocks * dev->chunkBitmapStride; ++ ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes); ++ } ++ return ok ? 1 : 0; ++ ++} ++ ++static int yaffs_ReadCheckpointDevice(yaffs_Device *dev) ++{ ++ yaffs_CheckpointDevice cp; ++ __u32 nBytes; ++ __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); ++ ++ 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; ++} ++ ++static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, ++ yaffs_Object *obj) ++{ ++ ++ cp->objectId = obj->objectId; ++ cp->parentId = (obj->parent) ? obj->parent->objectId : 0; ++ cp->chunkId = obj->chunkId; ++ cp->variantType = obj->variantType; ++ cp->deleted = obj->deleted; ++ cp->softDeleted = obj->softDeleted; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->renameAllowed = obj->renameAllowed; ++ 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) ++ cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; ++} ++ ++static void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp) ++{ ++ ++ yaffs_Object *parent; ++ ++ obj->objectId = cp->objectId; ++ ++ if(cp->parentId) ++ parent = yaffs_FindOrCreateObjectByNumber( ++ obj->myDev, ++ cp->parentId, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if(parent) ++ yaffs_AddObjectToDirectory(parent, obj); ++ ++ obj->chunkId = cp->chunkId; ++ obj->variantType = cp->variantType; ++ obj->deleted = cp->deleted; ++ obj->softDeleted = cp->softDeleted; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->renameAllowed = cp->renameAllowed; ++ 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; ++} ++ ++ ++ ++static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn, ++ __u32 level, int chunkOffset) ++{ ++ int i; ++ yaffs_Device *dev = in->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_CheckpointTnodeWorker(in, ++ tn->internal[i], ++ level - 1, ++ (chunkOffset<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)) == ++ sizeof(endMarker)); ++ } ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) ++{ ++ __u32 baseChunk; ++ int ok = 1; ++ yaffs_Device *dev = obj->myDev; ++ yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; ++ yaffs_Tnode *tn; ++ ++ ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); ++ ++ while(ok && (~baseChunk)){ ++ /* Read level 0 tnode */ ++ ++ /* printf("read tnode at %d\n",baseChunk); */ ++ tn = yaffs_GetTnodeRaw(dev); ++ if(tn) ++ ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) == ++ (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)); ++ ++ } ++ ++ return ok ? 1 : 0; ++} ++ ++ ++static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) ++{ ++ yaffs_Object *obj; ++ yaffs_CheckpointObject cp; ++ int i; ++ 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) { ++ obj = list_entry(lh, yaffs_Object, hashLink); ++ if (!obj->deferedFree) { ++ yaffs_ObjectToCheckpointObject(&cp,obj); ++ cp.structType = sizeof(cp); ++ ++ 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); ++ } ++ } ++ } ++ } ++ } ++ ++ /* 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; ++} ++ ++static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) ++{ ++ yaffs_Object *obj; ++ yaffs_CheckpointObject cp; ++ 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)) { ++ /* printf("structure parsing failed\n"); */ ++ ok = 0; ++ } ++ ++ 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) { ++ ok = yaffs_ReadCheckpointTnodes(obj); ++ } else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj->hardLinks.next = ++ (struct list_head *) ++ hardList; ++ hardList = obj; ++ } ++ ++ } ++ } ++ } ++ ++ if(ok) ++ yaffs_HardlinkFixup(dev,hardList); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs_WriteCheckpointData(yaffs_Device *dev) ++{ ++ ++ int ok; ++ ++ ok = yaffs_CheckpointOpen(dev,1); ++ ++ if(ok) ++ ok = yaffs_WriteCheckpointValidityMarker(dev,1); ++ if(ok) ++ ok = yaffs_WriteCheckpointDevice(dev); ++ if(ok) ++ ok = yaffs_WriteCheckpointObjects(dev); ++ if(ok) ++ ok = yaffs_WriteCheckpointValidityMarker(dev,0); ++ ++ if(!yaffs_CheckpointClose(dev)) ++ ok = 0; ++ ++ if(ok) ++ dev->isCheckpointed = 1; ++ else ++ dev->isCheckpointed = 0; ++ ++ return dev->isCheckpointed; ++} ++ ++static int yaffs_ReadCheckpointData(yaffs_Device *dev) ++{ ++ int ok; ++ ++ ok = yaffs_CheckpointOpen(dev,0); /* open for read */ ++ ++ if(ok) ++ ok = yaffs_ReadCheckpointValidityMarker(dev,1); ++ if(ok) ++ ok = yaffs_ReadCheckpointDevice(dev); ++ if(ok) ++ ok = yaffs_ReadCheckpointObjects(dev); ++ if(ok) ++ ok = yaffs_ReadCheckpointValidityMarker(dev,0); ++ ++ ++ ++ if(!yaffs_CheckpointClose(dev)) ++ ok = 0; ++ ++ if(ok) ++ dev->isCheckpointed = 1; ++ else ++ dev->isCheckpointed = 0; ++ ++ return ok ? 1 : 0; ++ ++} ++ ++static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) ++{ ++ if(dev->isCheckpointed || ++ dev->blocksInCheckpoint > 0){ ++ dev->isCheckpointed = 0; ++ yaffs_CheckpointInvalidateStream(dev); ++ if(dev->superBlock && dev->markSuperBlockDirty) ++ dev->markSuperBlockDirty(dev->superBlock); ++ } ++} ++ ++ ++int yaffs_CheckpointSave(yaffs_Device *dev) ++{ ++ yaffs_ReportOddballBlocks(dev); ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); ++ ++ if(!dev->isCheckpointed) ++ yaffs_WriteCheckpointData(dev); ++ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); ++ ++ return dev->isCheckpointed; ++} ++ ++int yaffs_CheckpointRestore(yaffs_Device *dev) ++{ ++ int retval; ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); ++ ++ retval = yaffs_ReadCheckpointData(dev); ++ ++ T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); ++ ++ yaffs_ReportOddballBlocks(dev); ++ ++ return retval; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, ++ int nBytes) ++{ ++ ++ int chunk; ++ int start; ++ int nToCopy; ++ int n = nBytes; ++ int nDone = 0; ++ yaffs_ChunkCache *cache; ++ ++ yaffs_Device *dev; ++ ++ dev = in->myDev; ++ ++ while (n > 0) { ++ //chunk = offset / dev->nDataBytesPerChunk + 1; ++ //start = offset % dev->nDataBytesPerChunk; ++ yaffs_AddrToChunk(dev,offset,&chunk,&start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->nDataBytesPerChunk) { ++ nToCopy = n; ++ } else { ++ nToCopy = dev->nDataBytesPerChunk - start; ++ } ++ ++ cache = yaffs_FindChunkCache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than a whole chunk ++ * then use the cache (if there is caching) ++ * else bypass the cache. ++ */ ++ if (cache || nToCopy != dev->nDataBytesPerChunk) { ++ if (dev->nShortOpCaches > 0) { ++ ++ /* If we can't find the data in the cache, then load it up. */ ++ ++ if (!cache) { ++ cache = yaffs_GrabChunkCache(in->myDev); ++ cache->object = in; ++ cache->chunkId = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_ReadChunkDataFromObject(in, chunk, ++ cache-> ++ data); ++ cache->nBytes = 0; ++ } ++ ++ yaffs_UseChunkCache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ memcpy(buffer, &cache->data[start], nToCopy); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++#endif ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy..*/ ++ ++ __u8 *localBuffer = ++ yaffs_GetTempBuffer(dev, __LINE__); ++ yaffs_ReadChunkDataFromObject(in, chunk, ++ localBuffer); ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ memcpy(buffer, &localBuffer[start], nToCopy); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++#endif ++ yaffs_ReleaseTempBuffer(dev, localBuffer, ++ __LINE__); ++ } ++ ++ } else { ++#ifdef CONFIG_YAFFS_WINCE ++ __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); ++ ++ /* Under WinCE can't do direct transfer. Need to use a local buffer. ++ * This is because we otherwise screw up WinCE's memory mapper ++ */ ++ yaffs_ReadChunkDataFromObject(in, chunk, localBuffer); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ memcpy(buffer, localBuffer, dev->nDataBytesPerChunk); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++ yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); ++#endif ++ ++#else ++ /* A full chunk. Read directly into the supplied buffer. */ ++ yaffs_ReadChunkDataFromObject(in, chunk, buffer); ++#endif ++ } ++ ++ n -= nToCopy; ++ offset += nToCopy; ++ buffer += nToCopy; ++ nDone += nToCopy; ++ ++ } ++ ++ return nDone; ++} ++ ++int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, ++ int nBytes, int writeThrough) ++{ ++ ++ int chunk; ++ int start; ++ int nToCopy; ++ int n = nBytes; ++ int nDone = 0; ++ int nToWriteBack; ++ int startOfWrite = offset; ++ int chunkWritten = 0; ++ int nBytesRead; ++ ++ yaffs_Device *dev; ++ ++ dev = in->myDev; ++ ++ while (n > 0 && chunkWritten >= 0) { ++ //chunk = offset / dev->nDataBytesPerChunk + 1; ++ //start = offset % dev->nDataBytesPerChunk; ++ yaffs_AddrToChunk(dev,offset,&chunk,&start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->nDataBytesPerChunk) { ++ nToCopy = n; ++ ++ /* Now folks, to calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of file then ++ * we need to write back as much as was there before. ++ */ ++ ++ nBytesRead = ++ in->variant.fileVariant.fileSize - ++ ((chunk - 1) * dev->nDataBytesPerChunk); ++ ++ if (nBytesRead > dev->nDataBytesPerChunk) { ++ nBytesRead = dev->nDataBytesPerChunk; ++ } ++ ++ nToWriteBack = ++ (nBytesRead > ++ (start + n)) ? nBytesRead : (start + n); ++ ++ } else { ++ nToCopy = dev->nDataBytesPerChunk - start; ++ nToWriteBack = dev->nDataBytesPerChunk; ++ } ++ ++ if (nToCopy != dev->nDataBytesPerChunk) { ++ /* An incomplete start or end chunk (or maybe both start and end chunk) */ ++ if (dev->nShortOpCaches > 0) { ++ 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)) { ++ cache = yaffs_GrabChunkCache(in->myDev); ++ cache->object = in; ++ cache->chunkId = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_ReadChunkDataFromObject(in, chunk, ++ cache-> ++ data); ++ } ++ 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; ++ } ++ ++ if (cache) { ++ yaffs_UseChunkCache(dev, cache, 1); ++ cache->locked = 1; ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ ++ memcpy(&cache->data[start], buffer, ++ nToCopy); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++#endif ++ cache->locked = 0; ++ cache->nBytes = nToWriteBack; ++ ++ if (writeThrough) { ++ chunkWritten = ++ yaffs_WriteChunkDataToObject ++ (cache->object, ++ cache->chunkId, ++ cache->data, cache->nBytes, ++ 1); ++ cache->dirty = 0; ++ } ++ ++ } else { ++ chunkWritten = -1; /* fail the write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe both start and end chunk) ++ * Read into the local buffer then copy, then copy over and write back. ++ */ ++ ++ __u8 *localBuffer = ++ yaffs_GetTempBuffer(dev, __LINE__); ++ ++ yaffs_ReadChunkDataFromObject(in, chunk, ++ localBuffer); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ ++ memcpy(&localBuffer[start], buffer, nToCopy); ++ ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++#endif ++ chunkWritten = ++ yaffs_WriteChunkDataToObject(in, chunk, ++ localBuffer, ++ nToWriteBack, ++ 0); ++ ++ yaffs_ReleaseTempBuffer(dev, localBuffer, ++ __LINE__); ++ ++ } ++ ++ } else { ++ ++#ifdef CONFIG_YAFFS_WINCE ++ /* Under WinCE can't do direct transfer. Need to use a local buffer. ++ * This is because we otherwise screw up WinCE's memory mapper ++ */ ++ __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_UnlockYAFFS(TRUE); ++#endif ++ memcpy(localBuffer, buffer, dev->nDataBytesPerChunk); ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_LockYAFFS(TRUE); ++#endif ++ chunkWritten = ++ yaffs_WriteChunkDataToObject(in, chunk, localBuffer, ++ dev->nDataBytesPerChunk, ++ 0); ++ yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); ++#else ++ /* A full chunk. Write directly from the supplied buffer. */ ++ chunkWritten = ++ yaffs_WriteChunkDataToObject(in, chunk, buffer, ++ dev->nDataBytesPerChunk, ++ 0); ++#endif ++ /* Since we've overwritten the cached data, we better invalidate it. */ ++ yaffs_InvalidateChunkCache(in, chunk); ++ } ++ ++ if (chunkWritten >= 0) { ++ n -= nToCopy; ++ offset += nToCopy; ++ buffer += nToCopy; ++ nDone += nToCopy; ++ } ++ ++ } ++ ++ /* Update file object */ ++ ++ if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) { ++ in->variant.fileVariant.fileSize = (startOfWrite + nDone); ++ } ++ ++ in->dirty = 1; ++ ++ return nDone; ++} ++ ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) ++{ ++ ++ yaffs_Device *dev = in->myDev; ++ int oldFileSize = in->variant.fileVariant.fileSize; ++ ++ int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk; ++ ++ int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) / ++ dev->nDataBytesPerChunk; ++ int i; ++ int chunkId; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = lastDel; i >= startDel; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_DeleteChunk ++ */ ++ ++ chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL); ++ if (chunkId > 0) { ++ if (chunkId < ++ (dev->internalStartBlock * dev->nChunksPerBlock) ++ || chunkId >= ++ ((dev->internalEndBlock + ++ 1) * dev->nChunksPerBlock)) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("Found daft chunkId %d for %d" TENDSTR), ++ chunkId, i)); ++ } else { ++ in->nDataChunks--; ++ yaffs_DeleteChunk(dev, chunkId, 1, __LINE__); ++ } ++ } ++ } ++ ++} ++ ++int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) ++{ ++ ++ int oldFileSize = in->variant.fileVariant.fileSize; ++ int newSizeOfPartialChunk; ++ int newFullChunks; ++ ++ yaffs_Device *dev = in->myDev; ++ ++ yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); ++ ++ yaffs_FlushFilesChunkCache(in); ++ yaffs_InvalidateWholeChunkCache(in); ++ ++ yaffs_CheckGarbageCollection(dev); ++ ++ if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { ++ return yaffs_GetFileSize(in); ++ } ++ ++ if (newSize == oldFileSize) { ++ return oldFileSize; ++ } ++ ++ if (newSize < oldFileSize) { ++ ++ yaffs_PruneResizedChunks(in, newSize); ++ ++ 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 */ ++ yaffs_ReadChunkDataFromObject(in, lastChunk, ++ localBuffer); ++ ++ memset(localBuffer + newSizeOfPartialChunk, 0, ++ dev->nDataBytesPerChunk - newSizeOfPartialChunk); ++ ++ yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, ++ newSizeOfPartialChunk, 1); ++ ++ yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); ++ } ++ ++ in->variant.fileVariant.fileSize = newSize; ++ ++ yaffs_PruneFileStructure(dev, &in->variant.fileVariant); ++ } else { ++ /* newsSize > oldFileSize */ ++ 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. ++ */ ++ if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED && ++ in->parent->objectId != YAFFS_OBJECTID_DELETED) { ++ yaffs_UpdateObjectHeader(in, NULL, 0, ++ (newSize < oldFileSize) ? 1 : 0, 0); ++ } ++ ++ return newSize; ++} ++ ++loff_t yaffs_GetFileSize(yaffs_Object * obj) ++{ ++ obj = yaffs_GetEquivalentObject(obj); ++ ++ switch (obj->variantType) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.fileVariant.fileSize; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_strlen(obj->variant.symLinkVariant.alias); ++ default: ++ return 0; ++ } ++} ++ ++ ++ ++int yaffs_FlushFile(yaffs_Object * in, int updateTime) ++{ ++ int retVal; ++ if (in->dirty) { ++ yaffs_FlushFilesChunkCache(in); ++ if (updateTime) { ++#ifdef CONFIG_YAFFS_WINCE ++ yfsd_WinFileTimeNow(in->win_mtime); ++#else ++ ++ in->yst_mtime = Y_CURRENT_TIME; ++ ++#endif ++ } ++ ++ retVal = ++ (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= ++ 0) ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ retVal = YAFFS_OK; ++ } ++ ++ return retVal; ++ ++} ++ ++static int yaffs_DoGenericObjectDeletion(yaffs_Object * in) ++{ ++ ++ /* First off, invalidate the file's data in the cache, without flushing. */ ++ yaffs_InvalidateWholeChunkCache(in); ++ ++ 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, NULL, 0, 0); ++ ++ } ++ ++ yaffs_RemoveObjectFromDirectory(in); ++ yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__); ++ in->chunkId = -1; ++ ++ yaffs_FreeObject(in); ++ return YAFFS_OK; ++ ++} ++ ++/* yaffs_DeleteFile deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_UnlinkFile(yaffs_Object * in) ++{ ++ ++ int retVal; ++ int immediateDeletion = 0; ++ ++ if (1) { ++#ifdef __KERNEL__ ++ if (!in->myInode) { ++ immediateDeletion = 1; ++ ++ } ++#else ++ if (in->inUse <= 0) { ++ immediateDeletion = 1; ++ ++ } ++#endif ++ if (immediateDeletion) { ++ retVal = ++ yaffs_ChangeObjectName(in, in->myDev->deletedDir, ++ NULL, 0, 0); ++ T(YAFFS_TRACE_TRACING, ++ (TSTR("yaffs: immediate deletion of file %d" TENDSTR), ++ in->objectId)); ++ in->deleted = 1; ++ in->myDev->nDeletedFiles++; ++ if (0 && in->myDev->isYaffs2) { ++ yaffs_ResizeFile(in, 0); ++ } ++ yaffs_SoftDeleteFile(in); ++ } else { ++ retVal = ++ yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, ++ NULL, 0, 0); ++ } ++ ++ } ++ return retVal; ++} ++ ++int yaffs_DeleteFile(yaffs_Object * in) ++{ ++ int retVal = YAFFS_OK; ++ ++ if (in->nDataChunks > 0) { ++ /* Use soft deletion if there is data in the file */ ++ if (!in->unlinked) { ++ retVal = yaffs_UnlinkFile(in); ++ } ++ if (retVal == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ in->myDev->nDeletedFiles++; ++ yaffs_SoftDeleteFile(in); ++ } ++ return in->deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top); ++ in->variant.fileVariant.top = NULL; ++ yaffs_DoGenericObjectDeletion(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++static int yaffs_DeleteDirectory(yaffs_Object * in) ++{ ++ /* First check that the directory is empty. */ ++ if (list_empty(&in->variant.directoryVariant.children)) { ++ return yaffs_DoGenericObjectDeletion(in); ++ } ++ ++ return YAFFS_FAIL; ++ ++} ++ ++static int yaffs_DeleteSymLink(yaffs_Object * in) ++{ ++ YFREE(in->variant.symLinkVariant.alias); ++ ++ return yaffs_DoGenericObjectDeletion(in); ++} ++ ++static int yaffs_DeleteHardLink(yaffs_Object * in) ++{ ++ /* remove this hardlink from the list assocaited with the equivalent ++ * object ++ */ ++ list_del(&in->hardLinks); ++ return yaffs_DoGenericObjectDeletion(in); ++} ++ ++static void yaffs_DestroyObject(yaffs_Object * obj) ++{ ++ switch (obj->variantType) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_DeleteFile(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_DeleteDirectory(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_DeleteSymLink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_DeleteHardLink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_DoGenericObjectDeletion(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ break; /* should not happen. */ ++ } ++} ++ ++static int yaffs_UnlinkWorker(yaffs_Object * obj) ++{ ++ ++ if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_DeleteHardLink(obj); ++ } else if (!list_empty(&obj->hardLinks)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Unhook it from its parent directory (so that the rename can work) ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ yaffs_Object *hl; ++ int retVal; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks); ++ ++ list_del_init(&hl->hardLinks); ++ list_del_init(&hl->siblings); ++ ++ yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ ++ retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0); ++ ++ if (retVal == YAFFS_OK) { ++ retVal = yaffs_DoGenericObjectDeletion(hl); ++ } ++ return retVal; ++ ++ } else { ++ switch (obj->variantType) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_UnlinkFile(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return yaffs_DeleteDirectory(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_DeleteSymLink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_DoGenericObjectDeletion(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } ++} ++ ++ ++static int yaffs_UnlinkObject( yaffs_Object *obj) ++{ ++ ++ if (obj && obj->unlinkAllowed) { ++ return yaffs_UnlinkWorker(obj); ++ } ++ ++ return YAFFS_FAIL; ++ ++} ++int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name) ++{ ++ yaffs_Object *obj; ++ ++ obj = yaffs_FindObjectByName(dir, name); ++ return yaffs_UnlinkObject(obj); ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId, ++ int backwardScanning) ++{ ++ yaffs_Object *obj; ++ ++ if (!backwardScanning) { ++ /* Handle YAFFS1 forward scanning case ++ * For YAFFS1 we always do the deletion ++ */ ++ ++ } else { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ if (yaffs_FindObjectByNumber(dev, objId)) { ++ return; ++ } ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_FindOrCreateObjectByNumber(dev, objId, ++ YAFFS_OBJECT_TYPE_FILE); ++ yaffs_AddObjectToDirectory(dev->unlinkedDir, obj); ++ obj->variant.fileVariant.shrinkSize = 0; ++ obj->valid = 1; /* So that we don't read any other info for this file */ ++ ++} ++ ++typedef struct { ++ int seq; ++ int block; ++} yaffs_BlockIndex; ++ ++ ++static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) ++{ ++ yaffs_Object *hl; ++ yaffs_Object *in; ++ ++ while (hardList) { ++ hl = hardList; ++ hardList = (yaffs_Object *) (hardList->hardLinks.next); ++ ++ in = yaffs_FindObjectByNumber(dev, ++ hl->variant.hardLinkVariant. ++ equivalentObjectId); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardLinkVariant.equivalentObject = in; ++ list_add(&hl->hardLinks, &in->hardLinks); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardLinkVariant.equivalentObject = NULL; ++ INIT_LIST_HEAD(&hl->hardLinks); ++ ++ } ++ ++ } ++ ++} ++ ++ ++ ++ ++ ++static int ybicmp(const void *a, const void *b){ ++ register int aseq = ((yaffs_BlockIndex *)a)->seq; ++ register int bseq = ((yaffs_BlockIndex *)b)->seq; ++ register int ablock = ((yaffs_BlockIndex *)a)->block; ++ register int bblock = ((yaffs_BlockIndex *)b)->block; ++ if( aseq == bseq ) ++ return ablock - bblock; ++ else ++ return aseq - bseq; ++ ++} ++ ++static int yaffs_Scan(yaffs_Device * dev) ++{ ++ yaffs_ExtendedTags tags; ++ int blk; ++ int blockIterator; ++ int startIterator; ++ int endIterator; ++ int nBlocksToScan = 0; ++ int result; ++ ++ int chunk; ++ int c; ++ int deleted; ++ yaffs_BlockState state; ++ yaffs_Object *hardList = NULL; ++ yaffs_Object *hl; ++ yaffs_BlockInfo *bi; ++ int sequenceNumber; ++ yaffs_ObjectHeader *oh; ++ yaffs_Object *in; ++ yaffs_Object *parent; ++ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; ++ ++ __u8 *chunkData; ++ ++ yaffs_BlockIndex *blockIndex = NULL; ++ ++ if (dev->isYaffs2) { ++ T(YAFFS_TRACE_SCAN, ++ (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)); ++ ++ chunkData = yaffs_GetTempBuffer(dev, __LINE__); ++ ++ dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ if (dev->isYaffs2) { ++ blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); ++ } ++ ++ /* Scan all the blocks to determine their state */ ++ for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { ++ bi = yaffs_GetBlockInfo(dev, blk); ++ yaffs_ClearChunkBits(dev, blk); ++ bi->pagesInUse = 0; ++ bi->softDeletions = 0; ++ ++ yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); ++ ++ bi->blockState = state; ++ bi->sequenceNumber = sequenceNumber; ++ ++ T(YAFFS_TRACE_SCAN_DEBUG, ++ (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, ++ state, sequenceNumber)); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ T(YAFFS_TRACE_BAD_BLOCKS, ++ (TSTR("block %d is bad" TENDSTR), blk)); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ T(YAFFS_TRACE_SCAN_DEBUG, ++ (TSTR("Block empty " TENDSTR))); ++ dev->nErasedBlocks++; ++ dev->nFreeChunks += dev->nChunksPerBlock; ++ } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ ++ /* Determine the highest sequence number */ ++ if (dev->isYaffs2 && ++ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ ++ blockIndex[nBlocksToScan].seq = sequenceNumber; ++ blockIndex[nBlocksToScan].block = blk; ++ ++ nBlocksToScan++; ++ ++ if (sequenceNumber >= dev->sequenceNumber) { ++ dev->sequenceNumber = sequenceNumber; ++ } ++ } else if (dev->isYaffs2) { ++ /* TODO: Nasty sequence number! */ ++ T(YAFFS_TRACE_SCAN, ++ (TSTR ++ ("Block scanning block %d has bad sequence number %d" ++ TENDSTR), blk, sequenceNumber)); ++ ++ } ++ } ++ } ++ ++ /* Sort the blocks ++ * Dungy old bubble sort for now... ++ */ ++ if (dev->isYaffs2) { ++ yaffs_BlockIndex temp; ++ int i; ++ int j; ++ ++ for (i = 0; i < nBlocksToScan; i++) ++ for (j = i + 1; j < nBlocksToScan; j++) ++ if (blockIndex[i].seq > blockIndex[j].seq) { ++ temp = blockIndex[j]; ++ blockIndex[j] = blockIndex[i]; ++ blockIndex[i] = temp; ++ } ++ } ++ ++ /* Now scan the blocks looking at the data. */ ++ if (dev->isYaffs2) { ++ startIterator = 0; ++ endIterator = nBlocksToScan - 1; ++ T(YAFFS_TRACE_SCAN_DEBUG, ++ (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); ++ } else { ++ startIterator = dev->internalStartBlock; ++ endIterator = dev->internalEndBlock; ++ } ++ ++ /* For each block.... */ ++ for (blockIterator = startIterator; blockIterator <= endIterator; ++ blockIterator++) { ++ ++ if (dev->isYaffs2) { ++ /* get the block to scan in the correct order */ ++ blk = blockIndex[blockIterator].block; ++ } else { ++ blk = blockIterator; ++ } ++ ++ bi = yaffs_GetBlockInfo(dev, blk); ++ state = bi->blockState; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning....*/ ++ 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; ++ ++ result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!dev->isYaffs2 && tags.chunkDeleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->nFreeChunks++; ++ /*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 is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->nErasedBlocks++; ++ } else { ++ /* this is the block being allocated from */ ++ T(YAFFS_TRACE_SCAN, ++ (TSTR ++ (" Allocating from %d %d" TENDSTR), ++ blk, c)); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->allocationBlock = blk; ++ dev->allocationPage = c; ++ 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 ++ */ ++ if (dev->isYaffs2 ++ && (dev->sequenceNumber != ++ bi->sequenceNumber)) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("yaffs: Allocation block %d was not highest sequence id:" ++ " block seq = %d, dev seq = %d" ++ TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber)); ++ } ++ } ++ ++ dev->nFreeChunks += (dev->nChunksPerBlock - c); ++ } else if (tags.chunkId > 0) { ++ /* chunkId > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_SetChunkBit(dev, blk, c); ++ bi->pagesInUse++; ++ ++ in = yaffs_FindOrCreateObjectByNumber(dev, ++ tags. ++ objectId, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash (two data chunks with ++ * the same chunkId). ++ */ ++ yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, ++ 1); ++ endpos = ++ (tags.chunkId - 1) * dev->nDataBytesPerChunk + ++ tags.byteCount; ++ if (in->variantType == YAFFS_OBJECT_TYPE_FILE ++ && in->variant.fileVariant.scannedFileSize < ++ endpos) { ++ in->variant.fileVariant. ++ scannedFileSize = endpos; ++ if (!dev->useHeaderFileSize) { ++ in->variant.fileVariant. ++ fileSize = ++ in->variant.fileVariant. ++ scannedFileSize; ++ } ++ ++ } ++ /* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); */ ++ } else { ++ /* chunkId == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make the object ++ */ ++ yaffs_SetChunkBit(dev, blk, c); ++ bi->pagesInUse++; ++ ++ result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, ++ chunkData, ++ NULL); ++ ++ oh = (yaffs_ObjectHeader *) chunkData; ++ ++ in = yaffs_FindObjectByNumber(dev, ++ 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 ++ * deleted, and worse still it has changed type. Delete the old object. ++ */ ++ ++ yaffs_DestroyObject(in); ++ ++ in = 0; ++ } ++ ++ in = yaffs_FindOrCreateObjectByNumber(dev, ++ tags. ++ objectId, ++ oh->type); ++ ++ if (oh->shadowsObject > 0) { ++ yaffs_HandleShadowedObject(dev, ++ oh-> ++ shadowsObject, ++ 0); ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. We have a duplicate and need to resolve it. */ ++ ++ unsigned existingSerial = in->serial; ++ unsigned newSerial = tags.serialNumber; ++ ++ if (dev->isYaffs2 || ++ ((existingSerial + 1) & 3) == ++ newSerial) { ++ /* Use new one - destroy the exisiting one */ ++ yaffs_DeleteChunk(dev, ++ in->chunkId, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy this one. */ ++ yaffs_DeleteChunk(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (!in->valid && ++ (tags.objectId == YAFFS_OBJECTID_ROOT || ++ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle with directory structure */ ++ in->valid = 1; ++ in->variantType = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++#ifdef CONFIG_YAFFS_WINCE ++ in->win_atime[0] = oh->win_atime[0]; ++ in->win_ctime[0] = oh->win_ctime[0]; ++ in->win_mtime[0] = oh->win_mtime[0]; ++ in->win_atime[1] = oh->win_atime[1]; ++ in->win_ctime[1] = oh->win_ctime[1]; ++ in->win_mtime[1] = oh->win_mtime[1]; ++#else ++ in->yst_uid = oh->yst_uid; ++ in->yst_gid = oh->yst_gid; ++ in->yst_atime = oh->yst_atime; ++ in->yst_mtime = oh->yst_mtime; ++ in->yst_ctime = oh->yst_ctime; ++ in->yst_rdev = oh->yst_rdev; ++#endif ++ in->chunkId = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variantType = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++#ifdef CONFIG_YAFFS_WINCE ++ in->win_atime[0] = oh->win_atime[0]; ++ in->win_ctime[0] = oh->win_ctime[0]; ++ in->win_mtime[0] = oh->win_mtime[0]; ++ in->win_atime[1] = oh->win_atime[1]; ++ in->win_ctime[1] = oh->win_ctime[1]; ++ in->win_mtime[1] = oh->win_mtime[1]; ++#else ++ in->yst_uid = oh->yst_uid; ++ in->yst_gid = oh->yst_gid; ++ in->yst_atime = oh->yst_atime; ++ in->yst_mtime = oh->yst_mtime; ++ in->yst_ctime = oh->yst_ctime; ++ in->yst_rdev = oh->yst_rdev; ++#endif ++ in->chunkId = chunk; ++ ++ yaffs_SetObjectName(in, oh->name); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_FindOrCreateObjectByNumber ++ (dev, oh->parentObjectId, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (parent->variantType == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variantType = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent->variant. ++ directoryVariant. ++ children); ++ } else if (parent->variantType != ++ YAFFS_OBJECT_TYPE_DIRECTORY) ++ { ++ /* Hoosterman, another problem.... ++ * We're trying to use a non-directory as a directory ++ */ ++ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("yaffs tragedy: attempting to use non-directory as" ++ " a directory in scan. Put in lost+found." ++ TENDSTR))); ++ parent = dev->lostNFoundDir; ++ } ++ ++ yaffs_AddObjectToDirectory(parent, in); ++ ++ if (0 && (parent == dev->deletedDir || ++ parent == dev->unlinkedDir)) { ++ in->deleted = 1; /* If it is unlinked at start up then it wants deleting */ ++ dev->nDeletedFiles++; ++ } ++ /* Note re hardlinks. ++ * 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. ++ */ ++ ++ switch (in->variantType) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->isYaffs2 ++ && oh->isShrink) { ++ /* Prune back the shrunken chunks */ ++ yaffs_PruneResizedChunks ++ (in, oh->fileSize); ++ /* Mark the block as having a shrinkHeader */ ++ bi->hasShrinkHeader = 1; ++ } ++ ++ if (dev->useHeaderFileSize) ++ ++ in->variant.fileVariant. ++ fileSize = ++ oh->fileSize; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardLinkVariant. ++ equivalentObjectId = ++ oh->equivalentObjectId; ++ in->hardLinks.next = ++ (struct list_head *) ++ hardList; ++ hardList = in; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symLinkVariant. ++ alias = ++ yaffs_CloneString(oh->alias); ++ break; ++ } ++ ++ if (parent == dev->deletedDir) { ++ yaffs_DestroyObject(in); ++ bi->hasShrinkHeader = 1; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ /* If we got this far while scanning, then the block is fully allocated.*/ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ bi->blockState = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pagesInUse == 0 && ++ !bi->hasShrinkHeader && ++ bi->blockState == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_BlockBecameDirty(dev, blk); ++ } ++ ++ } ++ ++ 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 ++ * hardlinks. ++ */ ++ ++ yaffs_HardlinkFixup(dev,hardList); ++ ++ /* Handle the unlinked files. Since they were left in an unlinked state we should ++ * just delete them. ++ */ ++ { ++ struct list_head *i; ++ struct list_head *n; ++ ++ yaffs_Object *l; ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinkedDir->variant.directoryVariant. ++ children) { ++ if (i) { ++ l = list_entry(i, yaffs_Object, siblings); ++ yaffs_DestroyObject(l); ++ } ++ } ++ } ++ ++ yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); ++ ++ T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) ++{ ++ __u8 *chunkData; ++ yaffs_ObjectHeader *oh; ++ yaffs_Device *dev = in->myDev; ++ yaffs_ExtendedTags tags; ++ int result; ++ ++#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; ++ ++ in->yst_mode = oh->yst_mode; ++#ifdef CONFIG_YAFFS_WINCE ++ in->win_atime[0] = oh->win_atime[0]; ++ in->win_ctime[0] = oh->win_ctime[0]; ++ in->win_mtime[0] = oh->win_mtime[0]; ++ in->win_atime[1] = oh->win_atime[1]; ++ in->win_ctime[1] = oh->win_ctime[1]; ++ in->win_mtime[1] = oh->win_mtime[1]; ++#else ++ in->yst_uid = oh->yst_uid; ++ in->yst_gid = oh->yst_gid; ++ in->yst_atime = oh->yst_atime; ++ 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) ++ in->variant.symLinkVariant.alias = ++ yaffs_CloneString(oh->alias); ++ ++ yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); ++ } ++} ++ ++static int yaffs_ScanBackwards(yaffs_Device * dev) ++{ ++ yaffs_ExtendedTags tags; ++ int blk; ++ int blockIterator; ++ int startIterator; ++ int endIterator; ++ int nBlocksToScan = 0; ++ ++ int chunk; ++ int result; ++ int c; ++ int deleted; ++ yaffs_BlockState state; ++ yaffs_Object *hardList = NULL; ++ yaffs_BlockInfo *bi; ++ int sequenceNumber; ++ yaffs_ObjectHeader *oh; ++ yaffs_Object *in; ++ yaffs_Object *parent; ++ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; ++ int itsUnlinked; ++ __u8 *chunkData; ++ ++ int fileSize; ++ int isShrink; ++ int foundChunksInBlock; ++ int equivalentObjectId; ++ ++ ++ yaffs_BlockIndex *blockIndex = NULL; ++ int altBlockIndex = 0; ++ ++ if (!dev->isYaffs2) { ++ T(YAFFS_TRACE_SCAN, ++ (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ T(YAFFS_TRACE_SCAN, ++ (TSTR ++ ("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." ++ TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); ++ ++ ++ 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; ++ } ++ ++ chunkData = yaffs_GetTempBuffer(dev, __LINE__); ++ ++ /* Scan all the blocks to determine their state */ ++ for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { ++ bi = yaffs_GetBlockInfo(dev, blk); ++ yaffs_ClearChunkBits(dev, blk); ++ bi->pagesInUse = 0; ++ bi->softDeletions = 0; ++ ++ yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); ++ ++ bi->blockState = state; ++ bi->sequenceNumber = sequenceNumber; ++ ++ 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){ ++ /* todo .. fix free space ? */ ++ ++ } else if (state == YAFFS_BLOCK_STATE_DEAD) { ++ T(YAFFS_TRACE_BAD_BLOCKS, ++ (TSTR("block %d is bad" TENDSTR), blk)); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ T(YAFFS_TRACE_SCAN_DEBUG, ++ (TSTR("Block empty " TENDSTR))); ++ dev->nErasedBlocks++; ++ dev->nFreeChunks += dev->nChunksPerBlock; ++ } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ ++ /* Determine the highest sequence number */ ++ if (dev->isYaffs2 && ++ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ ++ blockIndex[nBlocksToScan].seq = sequenceNumber; ++ blockIndex[nBlocksToScan].block = blk; ++ ++ nBlocksToScan++; ++ ++ if (sequenceNumber >= dev->sequenceNumber) { ++ dev->sequenceNumber = sequenceNumber; ++ } ++ } else if (dev->isYaffs2) { ++ /* TODO: Nasty sequence number! */ ++ T(YAFFS_TRACE_SCAN, ++ (TSTR ++ ("Block scanning block %d has bad sequence number %d" ++ TENDSTR), blk, sequenceNumber)); ++ ++ } ++ } ++ } ++ ++ T(YAFFS_TRACE_SCAN, ++ (TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); ++ ++ ++ ++ YYIELD(); ++ ++ /* Sort the blocks */ ++#ifndef CONFIG_YAFFS_USE_OWN_SORT ++ { ++ /* Use qsort now. */ ++ qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); ++ } ++#else ++ { ++ /* Dungy old bubble sort... */ ++ ++ yaffs_BlockIndex temp; ++ int i; ++ int j; ++ ++ for (i = 0; i < nBlocksToScan; i++) ++ for (j = i + 1; j < nBlocksToScan; j++) ++ if (blockIndex[i].seq > blockIndex[j].seq) { ++ temp = blockIndex[j]; ++ blockIndex[j] = blockIndex[i]; ++ blockIndex[i] = temp; ++ } ++ } ++#endif ++ ++ YYIELD(); ++ ++ T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); ++ ++ /* Now scan the blocks looking at the data. */ ++ startIterator = 0; ++ endIterator = nBlocksToScan - 1; ++ T(YAFFS_TRACE_SCAN_DEBUG, ++ (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); ++ ++ /* For each block.... backwards */ ++ for (blockIterator = endIterator; blockIterator >= startIterator; ++ blockIterator--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ YYIELD(); ++ ++ /* get the block to scan in the correct order */ ++ 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; c >= 0 && ++ (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || ++ state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ chunk = blk * dev->nChunksPerBlock + c; ++ ++ result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunkUsed) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * 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 ++ * 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; ++ dev->nErasedBlocks++; ++ } else { ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || ++ 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" ++ TENDSTR), blk, c)); ++ ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->allocationBlock = blk; ++ dev->allocationPage = c; ++ 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; ++ ++ ++ yaffs_SetChunkBit(dev, blk, c); ++ bi->pagesInUse++; ++ ++ in = yaffs_FindOrCreateObjectByNumber(dev, ++ tags. ++ objectId, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (in->variantType == YAFFS_OBJECT_TYPE_FILE ++ && chunkBase < ++ in->variant.fileVariant.shrinkSize) { ++ /* This has not been invalidated by a resize */ ++ yaffs_PutChunkIntoFile(in, tags.chunkId, ++ chunk, -1); ++ ++ /* 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) { ++ in->variant.fileVariant. ++ scannedFileSize = endpos; ++ in->variant.fileVariant. ++ fileSize = ++ in->variant.fileVariant. ++ scannedFileSize; ++ } ++ ++ } else { ++ /* This chunk has been invalidated by a resize, so delete */ ++ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); ++ ++ } ++ } else { ++ /* chunkId == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make the object ++ */ ++ foundChunksInBlock = 1; ++ ++ yaffs_SetChunkBit(dev, blk, c); ++ bi->pagesInUse++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extraHeaderInfoAvailable) { ++ in = yaffs_FindOrCreateObjectByNumber ++ (dev, tags.objectId, ++ tags.extraObjectType); ++ } ++ ++ if (!in || ++#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD ++ !in->valid || ++#endif ++ tags.extraShadows || ++ (!in->valid && ++ (tags.objectId == YAFFS_OBJECTID_ROOT || ++ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) ++ ) { ++ ++ /* 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 ++ * living with invalid data until needed. ++ */ ++ ++ result = yaffs_ReadChunkWithTagsFromNAND(dev, ++ chunk, ++ chunkData, ++ NULL); ++ ++ oh = (yaffs_ObjectHeader *) chunkData; ++ ++ if (!in) ++ in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); ++ ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("yaffs tragedy: Could not make object for object %d " ++ "at chunk %d during scan" ++ TENDSTR), tags.objectId, chunk)); ++ ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * 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 && ++ oh-> type == YAFFS_OBJECT_TYPE_FILE)|| ++ (tags.extraHeaderInfoAvailable && ++ tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE)) ++ ) { ++ __u32 thisSize = ++ (oh) ? oh->fileSize : tags. ++ extraFileLength; ++ __u32 parentObjectId = ++ (oh) ? oh-> ++ parentObjectId : tags. ++ extraParentObjectId; ++ unsigned isShrink = ++ (oh) ? oh->isShrink : tags. ++ extraIsShrinkHeader; ++ ++ /* If it is deleted (unlinked at start also means deleted) ++ * we treat the file size as being zeroed at this point. ++ */ ++ if (parentObjectId == ++ YAFFS_OBJECTID_DELETED ++ || parentObjectId == ++ YAFFS_OBJECTID_UNLINKED) { ++ thisSize = 0; ++ isShrink = 1; ++ } ++ ++ if (isShrink && ++ in->variant.fileVariant. ++ shrinkSize > thisSize) { ++ in->variant.fileVariant. ++ shrinkSize = ++ thisSize; ++ } ++ ++ if (isShrink) { ++ bi->hasShrinkHeader = 1; ++ } ++ ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); ++ ++ } ++ ++ if (!in->valid && ++ (tags.objectId == YAFFS_OBJECTID_ROOT || ++ tags.objectId == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle with directory structure */ ++ in->valid = 1; ++ ++ if(oh) { ++ in->variantType = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++#ifdef CONFIG_YAFFS_WINCE ++ in->win_atime[0] = oh->win_atime[0]; ++ in->win_ctime[0] = oh->win_ctime[0]; ++ in->win_mtime[0] = oh->win_mtime[0]; ++ in->win_atime[1] = oh->win_atime[1]; ++ in->win_ctime[1] = oh->win_ctime[1]; ++ in->win_mtime[1] = oh->win_mtime[1]; ++#else ++ in->yst_uid = oh->yst_uid; ++ in->yst_gid = oh->yst_gid; ++ in->yst_atime = oh->yst_atime; ++ 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) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->chunkId = chunk; ++ ++ if(oh) { ++ in->variantType = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++#ifdef CONFIG_YAFFS_WINCE ++ in->win_atime[0] = oh->win_atime[0]; ++ in->win_ctime[0] = oh->win_ctime[0]; ++ in->win_mtime[0] = oh->win_mtime[0]; ++ in->win_atime[1] = oh->win_atime[1]; ++ in->win_ctime[1] = oh->win_ctime[1]; ++ in->win_mtime[1] = oh->win_mtime[1]; ++#else ++ in->yst_uid = oh->yst_uid; ++ in->yst_gid = oh->yst_gid; ++ in->yst_atime = oh->yst_atime; ++ in->yst_mtime = oh->yst_mtime; ++ in->yst_ctime = oh->yst_ctime; ++ in->yst_rdev = oh->yst_rdev; ++#endif ++ ++ if (oh->shadowsObject > 0) ++ yaffs_HandleShadowedObject(dev, ++ oh-> ++ shadowsObject, ++ 1); ++ ++ ++ yaffs_SetObjectName(in, oh->name); ++ parent = ++ yaffs_FindOrCreateObjectByNumber ++ (dev, oh->parentObjectId, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ fileSize = oh->fileSize; ++ isShrink = oh->isShrink; ++ equivalentObjectId = oh->equivalentObjectId; ++ ++ } ++ else { ++ in->variantType = tags.extraObjectType; ++ parent = ++ yaffs_FindOrCreateObjectByNumber ++ (dev, tags.extraParentObjectId, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ fileSize = tags.extraFileLength; ++ isShrink = tags.extraIsShrinkHeader; ++ equivalentObjectId = tags.extraEquivalentObjectId; ++ in->lazyLoaded = 1; ++ ++ } ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent->variantType == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variantType = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent->variant. ++ directoryVariant. ++ children); ++ } else if (parent->variantType != ++ YAFFS_OBJECT_TYPE_DIRECTORY) ++ { ++ /* Hoosterman, another problem.... ++ * We're trying to use a non-directory as a directory ++ */ ++ ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("yaffs tragedy: attempting to use non-directory as" ++ " a directory in scan. Put in lost+found." ++ TENDSTR))); ++ parent = dev->lostNFoundDir; ++ } ++ ++ yaffs_AddObjectToDirectory(parent, in); ++ ++ itsUnlinked = (parent == dev->deletedDir) || ++ (parent == dev->unlinkedDir); ++ ++ if (isShrink) { ++ /* Mark the block as having a shrinkHeader */ ++ bi->hasShrinkHeader = 1; ++ } ++ ++ /* Note re hardlinks. ++ * 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. ++ */ ++ ++ switch (in->variantType) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ ++ if (in->variant.fileVariant. ++ 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 ++ * than its current data extents. ++ */ ++ in->variant.fileVariant.fileSize = fileSize; ++ in->variant.fileVariant.scannedFileSize = ++ in->variant.fileVariant.fileSize; ++ } ++ ++ if (isShrink && ++ in->variant.fileVariant.shrinkSize > fileSize) { ++ in->variant.fileVariant.shrinkSize = fileSize; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ if(!itsUnlinked) { ++ in->variant.hardLinkVariant.equivalentObjectId = ++ equivalentObjectId; ++ in->hardLinks.next = ++ (struct list_head *) hardList; ++ hardList = in; ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if(oh) ++ in->variant.symLinkVariant.alias = ++ yaffs_CloneString(oh-> ++ alias); ++ break; ++ } ++ ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { ++ /* If we got this far while scanning, then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ bi->blockState = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pagesInUse == 0 && ++ !bi->hasShrinkHeader && ++ bi->blockState == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_BlockBecameDirty(dev, blk); ++ } ++ ++ } ++ ++ 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 ++ * hardlinks. ++ */ ++ yaffs_HardlinkFixup(dev,hardList); ++ ++ ++ /* ++ * Sort out state of unlinked and deleted objects. ++ */ ++ { ++ struct list_head *i; ++ struct list_head *n; ++ ++ yaffs_Object *l; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinkedDir->variant.directoryVariant. ++ children) { ++ if (i) { ++ l = list_entry(i, yaffs_Object, siblings); ++ yaffs_DestroyObject(l); ++ } ++ } ++ ++ /* Soft delete all the deletedDir files */ ++ list_for_each_safe(i, n, ++ &dev->deletedDir->variant.directoryVariant. ++ children) { ++ if (i) { ++ l = list_entry(i, yaffs_Object, siblings); ++ yaffs_DestroyObject(l); ++ ++ } ++ } ++ } ++ ++ yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); ++ ++ T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); ++ ++ return YAFFS_OK; ++} ++ ++/*------------------------------ Directory Functions ----------------------------- */ ++ ++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; ++} ++ ++ ++static void yaffs_AddObjectToDirectory(yaffs_Object * directory, ++ yaffs_Object * obj) ++{ ++ ++ if (!directory) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: Trying to add an object to a null pointer directory" ++ TENDSTR))); ++ YBUG(); ++ } ++ if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: Trying to add an object to a non-directory" ++ TENDSTR))); ++ YBUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ } else if (!list_empty(&obj->siblings)) { ++ /* If it is holed up somewhere else, un hook it */ ++ yaffs_RemoveObjectFromDirectory(obj); ++ } ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.directoryVariant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->myDev->unlinkedDir ++ || directory == obj->myDev->deletedDir) { ++ obj->unlinked = 1; ++ obj->myDev->nUnlinkedFiles++; ++ obj->renameAllowed = 0; ++ } ++} ++ ++yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory, ++ const YCHAR * name) ++{ ++ int sum; ++ ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ yaffs_Object *l; ++ ++ if (!name) { ++ return NULL; ++ } ++ ++ if (!directory) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: yaffs_FindObjectByName: null pointer directory" ++ TENDSTR))); ++ YBUG(); ++ } ++ if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); ++ YBUG(); ++ } ++ ++ sum = yaffs_CalcNameSum(name); ++ ++ 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 */ ++ if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) { ++ return l; ++ } ++ } 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_strcmp(name, buffer) == 0) { ++ return l; ++ } ++ ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++ ++#if 0 ++int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, ++ int (*fn) (yaffs_Object *)) ++{ ++ struct list_head *i; ++ yaffs_Object *l; ++ ++ if (!theDir) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: yaffs_FindObjectByName: null pointer directory" ++ TENDSTR))); ++ YBUG(); ++ } ++ if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); ++ YBUG(); ++ } ++ ++ list_for_each(i, &theDir->variant.directoryVariant.children) { ++ if (i) { ++ l = list_entry(i, yaffs_Object, siblings); ++ if (l && !fn(l)) { ++ return YAFFS_FAIL; ++ } ++ } ++ } ++ ++ return YAFFS_OK; ++ ++} ++#endif ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj) ++{ ++ 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; ++ } ++ return obj; ++ ++} ++ ++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) { ++ yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); ++ } else if (obj->chunkId <= 0) { ++ YCHAR locName[20]; ++ /* make up a name */ ++ yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX, ++ obj->objectId); ++ yaffs_strncpy(name, locName, buffSize - 1); ++ ++ } ++#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++ else if (obj->shortName[0]) { ++ yaffs_strcpy(name, obj->shortName); ++ } ++#endif ++ else { ++ int result; ++ __u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); ++ ++ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer; ++ ++ memset(buffer, 0, obj->myDev->nDataBytesPerChunk); ++ ++ if (obj->chunkId >= 0) { ++ result = yaffs_ReadChunkWithTagsFromNAND(obj->myDev, ++ obj->chunkId, buffer, ++ NULL); ++ } ++ yaffs_strncpy(name, oh->name, buffSize - 1); ++ ++ yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); ++ } ++ ++ return yaffs_strlen(name); ++} ++ ++int yaffs_GetObjectFileLength(yaffs_Object * obj) ++{ ++ ++ /* Dereference any hard linking */ ++ obj = yaffs_GetEquivalentObject(obj); ++ ++ if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { ++ return obj->variant.fileVariant.fileSize; ++ } ++ if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { ++ return yaffs_strlen(obj->variant.symLinkVariant.alias); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->myDev->nDataBytesPerChunk; ++ } ++} ++ ++int yaffs_GetObjectLinkCount(yaffs_Object * obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) { ++ count++; /* the object itself */ ++ } ++ list_for_each(i, &obj->hardLinks) { ++ count++; /* add the hard links; */ ++ } ++ return count; ++ ++} ++ ++int yaffs_GetObjectInode(yaffs_Object * obj) ++{ ++ obj = yaffs_GetEquivalentObject(obj); ++ ++ return obj->objectId; ++} ++ ++unsigned yaffs_GetObjectType(yaffs_Object * obj) ++{ ++ obj = yaffs_GetEquivalentObject(obj); ++ ++ switch (obj->variantType) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj) ++{ ++ obj = yaffs_GetEquivalentObject(obj); ++ if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { ++ return yaffs_CloneString(obj->variant.symLinkVariant.alias); ++ } else { ++ return yaffs_CloneString(_Y("")); ++ } ++} ++ ++#ifndef CONFIG_YAFFS_WINCE ++ ++int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = attr->ia_uid; ++ if (valid & ATTR_GID) ++ obj->yst_gid = attr->ia_gid; ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_ResizeFile(obj, attr->ia_size); ++ ++ yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); ++ ++ return YAFFS_OK; ++ ++} ++int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ attr->ia_uid = obj->yst_uid; ++ valid |= ATTR_UID; ++ attr->ia_gid = obj->yst_gid; ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_GetFileSize(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++ ++} ++ ++#endif ++ ++#if 0 ++int yaffs_DumpObject(yaffs_Object * obj) ++{ ++ YCHAR name[257]; ++ ++ yaffs_GetObjectName(obj, name, 256); ++ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d" ++ " chunk %d type %d size %d\n" ++ TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name, ++ obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId, ++ yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); ++ ++ return YAFFS_OK; ++} ++#endif ++ ++/*---------------------------- Initialisation code -------------------------------------- */ ++ ++static int yaffs_CheckDevFunctions(const yaffs_Device * dev) ++{ ++ ++ /* Common functions, gotta have */ ++ if (!dev->eraseBlockInNAND || !dev->initialiseNAND) ++ return 0; ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ ++ /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ ++ if (dev->writeChunkWithTagsToNAND && ++ dev->readChunkWithTagsFromNAND && ++ !dev->writeChunkToNAND && ++ !dev->readChunkFromNAND && ++ dev->markNANDBlockBad && dev->queryNANDBlock) ++ return 1; ++#endif ++ ++ /* Can use the "spare" style interface for yaffs1 */ ++ if (!dev->isYaffs2 && ++ !dev->writeChunkWithTagsToNAND && ++ !dev->readChunkWithTagsFromNAND && ++ dev->writeChunkToNAND && ++ dev->readChunkFromNAND && ++ !dev->markNANDBlockBad && !dev->queryNANDBlock) ++ return 1; ++ ++ return 0; /* bad */ ++} ++ ++ ++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); ++ ++ dev->rootDir = ++ yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lostNFoundDir = ++ yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); ++} ++ ++int yaffs_GutsInitialise(yaffs_Device * dev) ++{ ++ unsigned x; ++ int bits; ++ ++ T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR))); ++ ++ /* Check stuff that must be set */ ++ ++ if (!dev) { ++ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ dev->internalStartBlock = dev->startBlock; ++ dev->internalEndBlock = dev->endBlock; ++ dev->blockOffset = 0; ++ dev->chunkOffset = 0; ++ dev->nFreeChunks = 0; ++ ++ if (dev->startBlock == 0) { ++ dev->internalStartBlock = dev->startBlock + 1; ++ dev->internalEndBlock = dev->endBlock + 1; ++ dev->blockOffset = 1; ++ dev->chunkOffset = dev->nChunksPerBlock; ++ } ++ ++ /* 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 || ++ dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small ++ ) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " ++ TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : "")); ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_InitialiseNAND(dev) != YAFFS_OK) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: InitialiseNAND failed" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_CheckDevFunctions(dev)) { ++ /* Function missing */ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("yaffs: device function(s) missing or wrong\n" TENDSTR))); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* This is really a compilation check. */ ++ if (!yaffs_CheckStructures()) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs_CheckStructures failed\n" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->isMounted) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: device already mounted\n" TENDSTR))); ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished with most checks. One or two more checks happen later on too. */ ++ ++ dev->isMounted = 1; ++ ++ ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ /* Start off assuming it is a power of 2 */ ++ dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk); ++ dev->chunkMask = (1<chunkShift) - 1; ++ ++ if(dev->nDataBytesPerChunk == (dev->chunkMask + 1)){ ++ /* Yes it is a power of 2, disable crumbs */ ++ dev->crumbMask = 0; ++ dev->crumbShift = 0; ++ dev->crumbsPerChunk = 0; ++ } else { ++ /* Not a power of 2, use crumbs instead */ ++ dev->crumbShift = ShiftDiv(sizeof(yaffs_PackedTags2TagsPart)); ++ dev->crumbMask = (1<crumbShift)-1; ++ dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift); ++ dev->chunkShift = 0; ++ dev->chunkMask = 0; ++ } ++ ++ ++ /* ++ * Calculate chunkGroupBits. ++ * We need to find the next power of 2 > than internalEndBlock ++ */ ++ ++ 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 */ ++ if(bits & 1) ++ bits++; ++ if(bits < 16) ++ dev->tnodeWidth = 16; ++ else ++ dev->tnodeWidth = bits; ++ } ++ 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; ++ ++ if (dev->nChunksPerBlock < dev->chunkGroupSize) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("yaffs: chunk group too large\n" TENDSTR))); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* OK, we've finished verifying the device, lets continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->garbageCollections = 0; ++ dev->passiveGarbageCollections = 0; ++ dev->currentDirtyChecker = 0; ++ dev->bufferedBlock = -1; ++ dev->doingBufferedBlockRewrite = 0; ++ dev->nDeletedFiles = 0; ++ dev->nBackgroundDeletions = 0; ++ dev->nUnlinkedFiles = 0; ++ dev->eccFixed = 0; ++ dev->eccUnfixed = 0; ++ dev->tagsEccFixed = 0; ++ dev->tagsEccUnfixed = 0; ++ dev->nErasureFailures = 0; ++ dev->nErasedBlocks = 0; ++ dev->isDoingGC = 0; ++ dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ ++ ++ /* Initialise temporary buffers and caches. */ ++ { ++ 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; ++ ++ if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { ++ dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; ++ } ++ ++ dev->srCache = ++ YMALLOC(dev->nShortOpCaches * sizeof(yaffs_ChunkCache)); ++ ++ 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 = YMALLOC_DMA(dev->nDataBytesPerChunk); ++ } ++ dev->srLastUse = 0; ++ } ++ ++ dev->cacheHits = 0; ++ ++ dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); ++ ++ if (dev->isYaffs2) { ++ dev->useHeaderFileSize = 1; ++ } ++ ++ yaffs_InitialiseBlocks(dev); ++ yaffs_InitialiseTnodes(dev); ++ yaffs_InitialiseObjects(dev); ++ ++ yaffs_CreateInitialDirectories(dev); ++ ++ ++ /* 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); ++ yaffs_InitialiseBlocks(dev); ++ yaffs_InitialiseTnodes(dev); ++ yaffs_InitialiseObjects(dev); ++ yaffs_CreateInitialDirectories(dev); ++ ++ yaffs_ScanBackwards(dev); ++ } ++ }else ++ yaffs_Scan(dev); ++ ++ /* Zero out stats */ ++ dev->nPageReads = 0; ++ dev->nPageWrites = 0; ++ dev->nBlockErasures = 0; ++ dev->nGCCopies = 0; ++ dev->nRetriedWrites = 0; ++ ++ dev->nRetiredBlocks = 0; ++ ++ yaffs_VerifyFreeChunks(dev); ++ T(YAFFS_TRACE_TRACING, ++ (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); ++ return YAFFS_OK; ++ ++} ++ ++void yaffs_Deinitialise(yaffs_Device * dev) ++{ ++ if (dev->isMounted) { ++ int i; ++ ++ yaffs_DeinitialiseBlocks(dev); ++ yaffs_DeinitialiseTnodes(dev); ++ yaffs_DeinitialiseObjects(dev); ++ if (dev->nShortOpCaches > 0) { ++ ++ for (i = 0; i < dev->nShortOpCaches; i++) { ++ YFREE(dev->srCache[i].data); ++ } ++ ++ YFREE(dev->srCache); ++ } ++ ++ YFREE(dev->gcCleanupList); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ YFREE(dev->tempBuffer[i].buffer); ++ } ++ ++ dev->isMounted = 0; ++ } ++ ++} ++ ++static int yaffs_CountFreeChunks(yaffs_Device * dev) ++{ ++ int nFree; ++ int b; ++ ++ yaffs_BlockInfo *blk; ++ ++ for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; ++ b++) { ++ blk = yaffs_GetBlockInfo(dev, b); ++ ++ switch (blk->blockState) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ nFree += ++ (dev->nChunksPerBlock - blk->pagesInUse + ++ blk->softDeletions); ++ break; ++ default: ++ break; ++ } ++ ++ } ++ ++ return nFree; ++} ++ ++int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev) ++{ ++ /* This is what we report to the outside world */ ++ ++ int nFree; ++ int nDirtyCacheChunks; ++ int blocksForCheckpoint; ++ ++#if 1 ++ nFree = dev->nFreeChunks; ++#else ++ nFree = yaffs_CountFreeChunks(dev); ++#endif ++ ++ nFree += dev->nDeletedFiles; ++ ++ /* Now count the number of dirty chunks in the cache and subtract those */ ++ ++ { ++ int i; ++ for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) { ++ if (dev->srCache[i].dirty) ++ nDirtyCacheChunks++; ++ } ++ } ++ ++ 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) ++ nFree = 0; ++ ++ return nFree; ++ ++} ++ ++static int yaffs_freeVerificationFailures; ++ ++static void yaffs_VerifyFreeChunks(yaffs_Device * dev) ++{ ++ int counted = yaffs_CountFreeChunks(dev); ++ ++ int difference = dev->nFreeChunks - counted; ++ ++ if (difference) { ++ T(YAFFS_TRACE_ALWAYS, ++ (TSTR("Freechunks verification failure %d %d %d" TENDSTR), ++ dev->nFreeChunks, counted, difference)); ++ yaffs_freeVerificationFailures++; ++ } ++} ++ ++/*---------------------------------------- YAFFS test code ----------------------*/ ++ ++#define yaffs_CheckStruct(structure,syze, name) \ ++ if(sizeof(structure) != syze) \ ++ { \ ++ T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\ ++ name,syze,sizeof(structure))); \ ++ return YAFFS_FAIL; \ ++ } ++ ++static int yaffs_CheckStructures(void) ++{ ++/* yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */ ++/* yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */ ++/* yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */ ++#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode") ++#endif ++ yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader") ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h +new file mode 100644 +index 0000000..9ef651d +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.h +@@ -0,0 +1,889 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "devextras.h" ++#include "yportenv.h" ++ ++#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 an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941FF53 ++ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 6 ++ ++#ifndef CONFIG_YAFFS_NO_YAFFS1 ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++#endif ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++#define YAFFS_MAX_CHUNK_ID 0x000FFFFF ++ ++#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++ ++#define YAFFS_NCHECKPOINT_OBJECTS 5000 ++ ++#define YAFFS_CHECKPOINT_VERSION 2 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Sseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_SB_HEADER 0x10 ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++/* */ ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 4 ++ ++/* 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, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00 ++ ++/* ChunkCache is used for short read/write operations.*/ ++typedef struct { ++ struct yaffs_ObjectStruct *object; ++ int chunkId; ++ int lastUse; ++ int dirty; ++ int nBytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ __u8 *data; ++#else ++ __u8 data[YAFFS_BYTES_PER_CHUNK]; ++#endif ++} yaffs_ChunkCache; ++ ++ ++ ++/* Tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise ++ * the structure size will get blown out. ++ */ ++ ++#ifndef CONFIG_YAFFS_NO_YAFFS1 ++typedef struct { ++ unsigned chunkId:20; ++ unsigned serialNumber:2; ++ unsigned byteCount:10; ++ unsigned objectId:18; ++ unsigned ecc:12; ++ unsigned unusedStuff:2; ++ ++} yaffs_Tags; ++ ++typedef union { ++ yaffs_Tags asTags; ++ __u8 asBytes[8]; ++} yaffs_TagsUnion; ++ ++#endif ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++typedef enum { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++} yaffs_ECCResult; ++ ++typedef enum { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++} yaffs_ObjectType; ++ ++typedef struct { ++ ++ unsigned validMarker0; ++ unsigned chunkUsed; /* Status of the chunk: used or unused */ ++ unsigned objectId; /* If 0 then this is not part of an object (unused) */ ++ unsigned chunkId; /* If 0 then this is a header, else a data chunk */ ++ unsigned byteCount; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ yaffs_ECCResult eccResult; ++ unsigned blockBad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned chunkDeleted; /* The chunk is marked deleted */ ++ unsigned serialNumber; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned sequenceNumber; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ ++ unsigned extraParentObjectId; /* The parent object */ ++ unsigned extraIsShrinkHeader; /* Is it a shrink header? */ ++ unsigned extraShadows; /* Does this shadow another object? */ ++ ++ yaffs_ObjectType extraObjectType; /* What object type? */ ++ ++ unsigned extraFileLength; /* Length if it is a file */ ++ unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ ++ ++ unsigned validMarker1; ++ ++} yaffs_ExtendedTags; ++ ++/* Spare structure for YAFFS1 */ ++typedef struct { ++ __u8 tagByte0; ++ __u8 tagByte1; ++ __u8 tagByte2; ++ __u8 tagByte3; ++ __u8 pageStatus; /* set to 0 to delete the chunk */ ++ __u8 blockStatus; ++ __u8 tagByte4; ++ __u8 tagByte5; ++ __u8 ecc1[3]; ++ __u8 tagByte6; ++ __u8 tagByte7; ++ __u8 ecc2[3]; ++} yaffs_Spare; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_NANDSpare { ++ yaffs_Spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++typedef enum { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ YAFFS_BLOCK_STATE_NEEDS_SCANNING, ++ /* The block might have something on it (ie it is allocating or full, perhaps empty) ++ * but it needs to be scanned to determine its true state. ++ * This state is only valid during yaffs_Scan. ++ * NB We tolerate empty because the pre-scanner might be incapable of deciding ++ * However, if this state is returned on a YAFFS2 device, then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* 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, ++ /* All the pages in this block have been allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* All pages have been allocated and deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. ++ */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++} yaffs_BlockState; ++ ++typedef struct { ++ ++ int softDeletions:10; /* number of soft deleted pages */ ++ int pagesInUse:10; /* number of pages in use */ ++ yaffs_BlockState blockState:4; /* One of the above block states */ ++ __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 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 */ ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ __u32 hasShrinkHeader:1; /* This block has at least one shrink object header */ ++ __u32 sequenceNumber; /* block sequence number for yaffs2 */ ++#endif ++ ++} yaffs_BlockInfo; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++typedef struct { ++ yaffs_ObjectType type; ++ ++ /* Apply to everything */ ++ int parentObjectId; ++ __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* Thes following apply to directories, files, symlinks - not hard links */ ++ __u32 yst_mode; /* protection */ ++ ++#ifdef CONFIG_YAFFS_WINCE ++ __u32 notForWinCE[5]; ++#else ++ __u32 yst_uid; ++ __u32 yst_gid; ++ __u32 yst_atime; ++ __u32 yst_mtime; ++ __u32 yst_ctime; ++#endif ++ ++ /* File size applies to files only */ ++ int fileSize; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equivalentObjectId; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ ++ ++#ifdef CONFIG_YAFFS_WINCE ++ __u32 win_ctime[2]; ++ __u32 win_atime[2]; ++ __u32 win_mtime[2]; ++ __u32 roomToGrow[4]; ++#else ++ __u32 roomToGrow[10]; ++#endif ++ ++ int shadowsObject; /* This object header shadows the specified object if > 0 */ ++ ++ /* isShrink applies to object headers written when we shrink the file (ie resize) */ ++ __u32 isShrink; ++ ++} yaffs_ObjectHeader; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++union yaffs_Tnode_union { ++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG ++ union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1]; ++#else ++ union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; ++#endif ++/* __u16 level0[YAFFS_NTNODES_LEVEL0]; */ ++ ++}; ++ ++typedef union yaffs_Tnode_union yaffs_Tnode; ++ ++struct yaffs_TnodeList_struct { ++ struct yaffs_TnodeList_struct *next; ++ yaffs_Tnode *tnodes; ++}; ++ ++typedef struct yaffs_TnodeList_struct yaffs_TnodeList; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++typedef struct { ++ __u32 fileSize; ++ __u32 scannedFileSize; ++ __u32 shrinkSize; ++ int topLevel; ++ yaffs_Tnode *top; ++} yaffs_FileStructure; ++ ++typedef struct { ++ struct list_head children; /* list of child links */ ++} yaffs_DirectoryStructure; ++ ++typedef struct { ++ YCHAR *alias; ++} yaffs_SymLinkStructure; ++ ++typedef struct { ++ struct yaffs_ObjectStruct *equivalentObject; ++ __u32 equivalentObjectId; ++} yaffs_HardLinkStructure; ++ ++typedef union { ++ yaffs_FileStructure fileVariant; ++ yaffs_DirectoryStructure directoryVariant; ++ yaffs_SymLinkStructure symLinkVariant; ++ yaffs_HardLinkStructure hardLinkVariant; ++} yaffs_ObjectVariant; ++ ++struct yaffs_ObjectStruct { ++ __u8 deleted:1; /* This should only apply to unlinked files. */ ++ __u8 softDeleted:1; /* it has also been soft deleted */ ++ __u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/ ++ __u8 fake:1; /* A fake object has no presence on NAND. */ ++ __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 ++ * object might be created before the data ++ * is available (ie. file data records appear before the header). ++ */ ++ __u8 lazyLoaded:1; /* This object has been lazy loaded and is missing some detail */ ++ ++ __u8 deferedFree:1; /* For Linux kernel. Object is removed from NAND, but is ++ * still in the inode cache. Free of object is defered. ++ * until the inode is released. ++ */ ++ ++ __u8 serial; /* serial number of chunk in NAND. Cached here */ ++ __u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_DeviceStruct *myDev; /* The device I'm on */ ++ ++ struct list_head hashLink; /* list of objects in this hash bucket */ ++ ++ struct list_head hardLinks; /* all the equivalent hard linked objects */ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_ObjectStruct *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int chunkId; ++ ++ int nDataChunks; /* Number of data chunks attached to the file. */ ++ ++ __u32 objectId; /* the object id value */ ++ ++ __u32 yst_mode; ++ ++#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++ YCHAR shortName[YAFFS_SHORT_NAME_LENGTH + 1]; ++#endif ++ ++#ifndef __KERNEL__ ++ __u32 inUse; ++#endif ++ ++#ifdef CONFIG_YAFFS_WINCE ++ __u32 win_ctime[2]; ++ __u32 win_mtime[2]; ++ __u32 win_atime[2]; ++#else ++ __u32 yst_uid; ++ __u32 yst_gid; ++ __u32 yst_atime; ++ __u32 yst_mtime; ++ __u32 yst_ctime; ++#endif ++ ++ __u32 yst_rdev; ++ ++#ifdef __KERNEL__ ++ struct inode *myInode; ++ ++#endif ++ ++ yaffs_ObjectType variantType; ++ ++ yaffs_ObjectVariant variant; ++ ++}; ++ ++typedef struct yaffs_ObjectStruct yaffs_Object; ++ ++struct yaffs_ObjectList_struct { ++ yaffs_Object *objects; ++ struct yaffs_ObjectList_struct *next; ++}; ++ ++typedef struct yaffs_ObjectList_struct yaffs_ObjectList; ++ ++typedef struct { ++ struct list_head list; ++ int count; ++} yaffs_ObjectBucket; ++ ++ ++/* yaffs_CheckpointObject holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++typedef struct { ++ int structType; ++ __u32 objectId; ++ __u32 parentId; ++ int chunkId; ++ ++ yaffs_ObjectType variantType:3; ++ __u8 deleted:1; ++ __u8 softDeleted:1; ++ __u8 unlinked:1; ++ __u8 fake:1; ++ __u8 renameAllowed:1; ++ __u8 unlinkAllowed:1; ++ __u8 serial; ++ ++ int nDataChunks; ++ __u32 fileSizeOrEquivalentObjectId; ++ ++}yaffs_CheckpointObject; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few ++ */ ++ ++typedef struct { ++ __u8 *buffer; ++ int line; /* track from whence this buffer was allocated */ ++ int maxLine; ++} yaffs_TempBuffer; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_DeviceStruct { ++ struct list_head devList; ++ const char *name; ++ ++ /* Entry parameters set up way early. Yaffs sets up the rest.*/ ++ int nDataBytesPerChunk; /* Should be a power of 2 >= 512 */ ++ int nChunksPerBlock; /* does not need to be a power of 2 */ ++ int nBytesPerSpare; /* spare area size */ ++ int startBlock; /* Start block we're allowed to use */ ++ 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 ++ * the number of short op caches (don't use too many) ++ */ ++ ++ int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */ ++ ++ int useNANDECC; /* Flag to decide whether or not to use NANDECC */ ++ ++ void *genericDevice; /* Pointer to device context ++ * 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, ++ int chunkInNAND, const __u8 * data, ++ const yaffs_Spare * spare); ++ int (*readChunkFromNAND) (struct yaffs_DeviceStruct * dev, ++ int chunkInNAND, __u8 * data, ++ yaffs_Spare * spare); ++ int (*eraseBlockInNAND) (struct yaffs_DeviceStruct * dev, ++ int blockInNAND); ++ int (*initialiseNAND) (struct yaffs_DeviceStruct * dev); ++ ++#ifdef CONFIG_YAFFS_YAFFS2 ++ int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev, ++ int chunkInNAND, const __u8 * data, ++ const yaffs_ExtendedTags * tags); ++ int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev, ++ int chunkInNAND, __u8 * data, ++ yaffs_ExtendedTags * tags); ++ int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); ++ int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, ++ yaffs_BlockState * state, int *sequenceNumber); ++#endif ++ ++ int isYaffs2; ++ ++ /* 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. */ ++ ++ /* 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 ++ * at compile time so we have to allocate it. ++ */ ++ void (*putSuperFunc) (struct super_block * sb); ++#endif ++ ++ int isMounted; ++ ++ int isCheckpointed; ++ ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internalStartBlock; ++ int internalEndBlock; ++ int blockOffset; ++ int chunkOffset; ++ ++ ++ /* Runtime checkpointing stuff */ ++ int checkpointPageSequence; /* running sequence number of checkpoint pages */ ++ int checkpointByteCount; ++ int checkpointByteOffset; ++ __u8 *checkpointBuffer; ++ int checkpointOpenForWrite; ++ int blocksInCheckpoint; ++ int checkpointCurrentChunk; ++ int checkpointCurrentBlock; ++ int checkpointNextBlock; ++ int *checkpointBlockList; ++ int checkpointMaxBlocks; ++ ++ /* 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. ++ * Must be consistent with nChunksPerBlock. ++ */ ++ ++ int nErasedBlocks; ++ int allocationBlock; /* Current block being allocated off */ ++ __u32 allocationPage; ++ int allocationBlockFinder; /* Used to search for next allocation block */ ++ ++ /* Runtime state */ ++ int nTnodesCreated; ++ yaffs_Tnode *freeTnodes; ++ int nFreeTnodes; ++ yaffs_TnodeList *allocatedTnodeList; ++ ++ int isDoingGC; ++ ++ int nObjectsCreated; ++ yaffs_Object *freeObjects; ++ int nFreeObjects; ++ ++ yaffs_ObjectList *allocatedObjectList; ++ ++ yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; ++ ++ int nFreeChunks; ++ ++ int currentDirtyChecker; /* Used to find current dirtiest block */ ++ ++ __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ ++ ++ /* Statistcs */ ++ int nPageWrites; ++ int nPageReads; ++ int nBlockErasures; ++ int nErasureFailures; ++ int nGCCopies; ++ int garbageCollections; ++ int passiveGarbageCollections; ++ int nRetriedWrites; ++ int nRetiredBlocks; ++ int eccFixed; ++ int eccUnfixed; ++ int tagsEccFixed; ++ int tagsEccUnfixed; ++ int nDeletions; ++ int nUnmarkedDeletions; ++ ++ int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */ ++ ++ /* Special directories */ ++ yaffs_Object *rootDir; ++ yaffs_Object *lostNFoundDir; ++ ++ /* Buffer areas for storing data to recover from write failures TODO ++ * __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; ++ ++ yaffs_ChunkCache *srCache; ++ int srLastUse; ++ ++ int cacheHits; ++ ++ /* Stuff for background deletion and unlinked files.*/ ++ yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */ ++ yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */ ++ yaffs_Object *unlinkedDeletion; /* Current file being background deleted.*/ ++ int nDeletedFiles; /* Count of files awaiting deletion;*/ ++ int nUnlinkedFiles; /* Count of unlinked files. */ ++ int nBackgroundDeletions; /* Count of background deletions. */ ++ ++ ++ yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS]; ++ int maxTemp; ++ int unmanagedTempAllocations; ++ int unmanagedTempDeallocations; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned sequenceNumber; /* Sequence number of currently allocating block */ ++ unsigned oldestDirtySequence; ++ ++}; ++ ++typedef struct yaffs_DeviceStruct yaffs_Device; ++ ++/* The static layout of bllock usage etc is stored in the super block header */ ++typedef struct { ++ int StructType; ++ int version; ++ int checkpointStartBlock; ++ int checkpointEndBlock; ++ int startBlock; ++ 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. ++ */ ++typedef struct { ++ int structType; ++ int nErasedBlocks; ++ int allocationBlock; /* Current block being allocated off */ ++ __u32 allocationPage; ++ int nFreeChunks; ++ ++ int nDeletedFiles; /* Count of files awaiting deletion;*/ ++ int nUnlinkedFiles; /* Count of unlinked files. */ ++ int nBackgroundDeletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned sequenceNumber; /* Sequence number of currently allocating block */ ++ unsigned oldestDirtySequence; ++ ++} yaffs_CheckpointDevice; ++ ++ ++typedef struct { ++ int structType; ++ __u32 magic; ++ __u32 version; ++ __u32 head; ++} yaffs_CheckpointValidity; ++ ++/* Function to manipulate block info */ ++static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) ++{ ++ if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR), ++ blk)); ++ YBUG(); ++ } ++ return &dev->blockInfo[blk - dev->internalStartBlock]; ++} ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_GutsInitialise(yaffs_Device * dev); ++void yaffs_Deinitialise(yaffs_Device * dev); ++ ++int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev); ++ ++int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, ++ yaffs_Object * newDir, const YCHAR * newName); ++ ++int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name); ++int yaffs_DeleteFile(yaffs_Object * obj); ++ ++int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize); ++int yaffs_GetObjectFileLength(yaffs_Object * obj); ++int yaffs_GetObjectInode(yaffs_Object * obj); ++unsigned yaffs_GetObjectType(yaffs_Object * obj); ++int yaffs_GetObjectLinkCount(yaffs_Object * obj); ++ ++int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr); ++int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr); ++ ++/* File operations */ ++int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset, ++ int nBytes); ++int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset, ++ int nBytes, int writeThrough); ++int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize); ++ ++yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid); ++int yaffs_FlushFile(yaffs_Object * obj, int updateTime); ++ ++/* Flushing and checkpointing */ ++void yaffs_FlushEntireDeviceCache(yaffs_Device *dev); ++ ++int yaffs_CheckpointSave(yaffs_Device *dev); ++int yaffs_CheckpointRestore(yaffs_Device *dev); ++ ++/* Directory operations */ ++yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid); ++yaffs_Object *yaffs_FindObjectByName(yaffs_Object * theDir, const YCHAR * name); ++int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, ++ int (*fn) (yaffs_Object *)); ++ ++yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number); ++ ++/* Link operations */ ++yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, ++ yaffs_Object * equivalentObject); ++ ++yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj); ++ ++/* Symlink operations */ ++yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid, ++ const YCHAR * alias); ++YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, ++ __u32 mode, __u32 uid, __u32 gid, __u32 rdev); ++ ++/* Special directories */ ++yaffs_Object *yaffs_Root(yaffs_Device * dev); ++yaffs_Object *yaffs_LostNFound(yaffs_Device * dev); ++ ++#ifdef CONFIG_YAFFS_WINCE ++/* CONFIG_YAFFS_WINCE special stuff */ ++void yfsd_WinFileTimeNow(__u32 target[2]); ++#endif ++ ++#ifdef __KERNEL__ ++ ++void yaffs_HandleDeferedFree(yaffs_Object * obj); ++#endif ++ ++/* Debug dump */ ++int yaffs_DumpObject(yaffs_Object * obj); ++ ++void yaffs_GutsTest(yaffs_Device * dev); ++ ++/* A few useful functions */ ++void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); ++void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn); ++int yaffs_CheckFF(__u8 * buffer, int nBytes); ++void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif.c b/fs/yaffs2/yaffs_mtdif.c +new file mode 100644 +index 0000000..3147f54 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -0,0 +1,246 @@ ++/* ++ * 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 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. ++ */ ++ ++const char *yaffs_mtdif_c_version = ++ "$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" ++#include "linux/mtd/nand.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) ++static struct nand_oobinfo yaffs_oobinfo = { ++ .useecc = 1, ++ .eccbytes = 6, ++ .eccpos = {8, 9, 10, 13, 14, 15} ++}; ++ ++static struct nand_oobinfo yaffs_noeccinfo = { ++ .useecc = 0, ++}; ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) ++{ ++ oob[0] = spare->tagByte0; ++ oob[1] = spare->tagByte1; ++ oob[2] = spare->tagByte2; ++ oob[3] = spare->tagByte3; ++ oob[4] = spare->tagByte4; ++ oob[5] = spare->tagByte5 & 0x3f; ++ oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; ++ oob[5] |= spare->pageStatus == 0 ? 0: 0x40; ++ oob[6] = spare->tagByte6; ++ oob[7] = spare->tagByte7; ++} ++ ++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]; ++ spare->tagByte3 = oob[3]; ++ spare->tagByte4 = oob[4]; ++ spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; ++ spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; ++ spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; ++ spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff; ++ spare->tagByte6 = oob[6]; ++ spare->tagByte7 = oob[7]; ++ 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 ++ ++int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, const yaffs_Spare * spare) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ struct mtd_oob_ops ops; ++#endif ++ size_mtd_t dummy; ++ int retval = 0; ++ ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ __u8 spareAsBytes[8]; /* OOB */ ++ ++ if (data && !spare) ++ retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data); ++ else if (spare) { ++ if (dev->useNANDECC) { ++ translate_spare2oob(spare, spareAsBytes); ++ ops.mode = MTD_OOB_AUTO; ++ ops.ooblen = 8; /* temp hack */ ++ } else { ++ ops.mode = MTD_OOB_RAW; ++ ops.ooblen = YAFFS_BYTES_PER_SPARE; ++ } ++ ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; ++ ops.datbuf = (u8 *)data; ++ ops.ooboffs = 0; ++ ops.oobbuf = spareAsBytes; ++ retval = mtd->write_oob(mtd, addr, &ops); ++ } ++#else ++ __u8 *spareAsBytes = (__u8 *) spare; ++ ++ if (data && spare) { ++ if (dev->useNANDECC) ++ retval = ++ mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, spareAsBytes, ++ &yaffs_oobinfo); ++ else ++ retval = ++ mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, spareAsBytes, ++ &yaffs_noeccinfo); ++ } else { ++ if (data) ++ retval = ++ mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, ++ data); ++ if (spare) ++ retval = ++ mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, ++ &dummy, spareAsBytes); ++ } ++#endif ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, ++ yaffs_Spare * spare) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ struct mtd_oob_ops ops; ++#endif ++ size_mtd_t dummy; ++ int retval = 0; ++ ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ __u8 spareAsBytes[8]; /* OOB */ ++ ++ if (data && !spare) ++ retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data); ++ else if (spare) { ++ if (dev->useNANDECC) { ++ ops.mode = MTD_OOB_AUTO; ++ ops.ooblen = 8; /* temp hack */ ++ } else { ++ ops.mode = MTD_OOB_RAW; ++ ops.ooblen = YAFFS_BYTES_PER_SPARE; ++ } ++ ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; ++ ops.datbuf = data; ++ ops.ooboffs = 0; ++ ops.oobbuf = spareAsBytes; ++ retval = mtd->read_oob(mtd, addr, &ops); ++ if (dev->useNANDECC) ++ translate_oob2spare(spare, spareAsBytes); ++ } ++#else ++ __u8 *spareAsBytes = (__u8 *) spare; ++ ++ if (data && spare) { ++ if (dev->useNANDECC) { ++ /* Careful, this call adds 2 ints */ ++ /* to the end of the spare data. Calling function */ ++ /* should allocate enough memory for spare, */ ++ /* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */ ++ retval = ++ mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, spareAsBytes, ++ &yaffs_oobinfo); ++ } else { ++ retval = ++ mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, spareAsBytes, ++ &yaffs_noeccinfo); ++ } ++ } else { ++ if (data) ++ retval = ++ mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, ++ data); ++ if (spare) ++ retval = ++ mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, ++ &dummy, spareAsBytes); ++ } ++#endif ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++ __u64 addr = ++ ((loff_mtd_t) blockNumber) * dev->nDataBytesPerChunk ++ * dev->nChunksPerBlock; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ /* Todo finish off the ei if required */ ++ ++ sema_init(&dev->sem, 0); ++ ++ retval = mtd->erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd_InitialiseNAND(yaffs_Device * dev) ++{ ++ return YAFFS_OK; ++} ++ +diff --git a/fs/yaffs2/yaffs_mtdif.h b/fs/yaffs2/yaffs_mtdif.h +new file mode 100644 +index 0000000..f75e08c +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -0,0 +1,27 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, const yaffs_Spare * spare); ++int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, ++ yaffs_Spare * spare); ++int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber); ++int nandmtd_InitialiseNAND(yaffs_Device * dev); ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c +new file mode 100644 +index 0000000..f3a8263 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif2.c +@@ -0,0 +1,369 @@ ++/* ++ * 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 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. ++ */ ++ ++/* mtd interface for YAFFS2 */ ++ ++const char *yaffs_mtdif2_c_version = ++ "$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) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ struct mtd_oob_ops ops; ++#else ++ size_t dummy; ++#endif ++ int retval = 0; ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ yaffs_PackedTags2 pt; ++ ++ T(YAFFS_TRACE_MTD, ++ (TSTR ++ ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" ++ TENDSTR), chunkInNAND, data, tags)); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ if (tags) ++ yaffs_PackTags2(&pt, tags); ++ else ++ 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; //modify ++ ops.oobbuf = (void *)dev->spareBuffer; //modify ++ retval = mtd->write_oob(mtd, addr, &ops); ++ } else ++ BUG(); /* both tags and data should always be present */ ++#else ++ if (tags) { ++ yaffs_PackTags2(&pt, tags); ++ } ++ ++ if (data && tags) { ++ nandmtd2_pt2buf(dev, &pt, 0); ++ if (dev->useNANDECC) ++ retval = ++ mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, dev->spareBuffer, NULL); ++ else ++ retval = ++ mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, dev->spareBuffer, NULL); ++ } else { ++ if (data) { ++ retval = ++ mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, ++ data); ++ } ++ if (tags) { ++ nandmtd2_pt2buf(dev, &pt, 1); ++ retval = ++ mtd->write_oob(mtd, addr, mtd->oobsize, &dummy, ++ dev->spareBuffer); ++ } ++ } ++#endif ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, ++ __u8 * data, yaffs_ExtendedTags * tags) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ struct mtd_oob_ops ops; ++#endif ++ size_mtd_t dummy; ++ int retval = 0; ++ loff_mtd_t addr = ((loff_mtd_t) chunkInNAND) * dev->nDataBytesPerChunk; ++ yaffs_PackedTags2 pt; ++ ++ T(YAFFS_TRACE_MTD, ++ (TSTR ++ ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p" ++ TENDSTR), chunkInNAND, data, tags)); ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) ++ if (data && !tags) ++ retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data); ++ else if (tags) { ++ ops.mode = MTD_OOB_AUTO; ++ ops.ooblen = sizeof(pt); ++ ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt); ++ ops.ooboffs = 0; ++ 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) { ++ if (dev->useNANDECC) { ++ retval = ++ mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, dev->spareBuffer, ++ NULL); ++ } else { ++ retval = ++ mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, ++ &dummy, data, dev->spareBuffer, ++ NULL); ++ } ++ nandmtd2_buf2pt(dev, &pt, 0); ++ } else { ++ if (data) { ++ retval = ++ mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, ++ data); ++ } ++ if (tags) { ++ retval = ++ mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, ++ dev->spareBuffer); ++ nandmtd2_buf2pt(dev, &pt, 1); ++ } ++ } ++#endif ++ ++ if (tags) ++ yaffs_UnpackTags2(tags, &pt); ++ ++ if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR) ++ tags->eccResult = YAFFS_ECC_RESULT_UNFIXED; ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++ int retval; ++ T(YAFFS_TRACE_MTD, ++ (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo)); ++ ++ retval = ++ mtd->block_markbad(mtd, ++ (u64)blockNo * dev->nChunksPerBlock * ++ dev->nDataBytesPerChunk); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++ ++} ++ ++int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, ++ yaffs_BlockState * state, int *sequenceNumber) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); ++ int retval; ++ ++ T(YAFFS_TRACE_MTD, ++ (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo)); ++ ++ retval = ++ mtd->block_isbad(mtd, ++ (u64)blockNo * dev->nChunksPerBlock * ++ dev->nDataBytesPerChunk); ++ if (retval) { ++ T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR))); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *sequenceNumber = 0; ++ } else { ++ yaffs_ExtendedTags t; ++ nandmtd2_ReadChunkWithTagsFromNAND(dev, ++ blockNo * ++ dev->nChunksPerBlock, NULL, ++ &t); ++ ++ if (t.chunkUsed) { ++ *sequenceNumber = t.sequenceNumber; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ } else { ++ *sequenceNumber = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ T(YAFFS_TRACE_MTD, ++ (TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber, ++ *state)); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ +diff --git a/fs/yaffs2/yaffs_mtdif2.h b/fs/yaffs2/yaffs_mtdif2.h +new file mode 100644 +index 0000000..e70d751 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif2.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_MTDIF2_H__ ++#define __YAFFS_MTDIF2_H__ ++ ++#include "yaffs_guts.h" ++int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, ++ const yaffs_ExtendedTags * tags); ++int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, ++ __u8 * data, yaffs_ExtendedTags * tags); ++int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); ++int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, ++ yaffs_BlockState * state, int *sequenceNumber); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c +new file mode 100644 +index 0000000..ef90b4a +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.c +@@ -0,0 +1,134 @@ ++/* ++ * 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 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. ++ */ ++ ++const char *yaffs_nand_c_version = ++ "$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" ++#include "yaffs_tagsvalidity.h" ++ ++ ++int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, ++ __u8 * buffer, ++ yaffs_ExtendedTags * tags) ++{ ++ 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; ++ ++ if (dev->readChunkWithTagsFromNAND) ++ result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer, ++ tags); ++ else ++ result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev, ++ realignedChunkInNAND, ++ buffer, ++ 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; ++} ++ ++int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ const __u8 * buffer, ++ yaffs_ExtendedTags * tags) ++{ ++ chunkInNAND -= dev->chunkOffset; ++ ++ ++ if (tags) { ++ tags->sequenceNumber = dev->sequenceNumber; ++ tags->chunkUsed = 1; ++ if (!yaffs_ValidateTags(tags)) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("Writing uninitialised tags" TENDSTR))); ++ YBUG(); ++ } ++ T(YAFFS_TRACE_WRITE, ++ (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND, ++ tags->objectId, tags->chunkId)); ++ } else { ++ T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR))); ++ YBUG(); ++ } ++ ++ if (dev->writeChunkWithTagsToNAND) ++ return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer, ++ tags); ++ else ++ return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev, ++ chunkInNAND, ++ buffer, ++ tags); ++} ++ ++int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo) ++{ ++ blockNo -= dev->blockOffset; ++ ++; ++ if (dev->markNANDBlockBad) ++ return dev->markNANDBlockBad(dev, blockNo); ++ else ++ return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo); ++} ++ ++int yaffs_QueryInitialBlockState(yaffs_Device * dev, ++ int blockNo, ++ yaffs_BlockState * state, ++ unsigned *sequenceNumber) ++{ ++ blockNo -= dev->blockOffset; ++ ++ if (dev->queryNANDBlock) ++ return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber); ++ else ++ return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo, ++ state, ++ sequenceNumber); ++} ++ ++ ++int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, ++ int blockInNAND) ++{ ++ int result; ++ ++ blockInNAND -= dev->blockOffset; ++ ++ ++ dev->nBlockErasures++; ++ result = dev->eraseBlockInNAND(dev, blockInNAND); ++ ++ return result; ++} ++ ++int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev) ++{ ++ return dev->initialiseNAND(dev); ++} ++ ++ ++ +diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h +new file mode 100644 +index 0000000..8ed1a2d +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.h +@@ -0,0 +1,44 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++ ++ ++int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, ++ __u8 * buffer, ++ yaffs_ExtendedTags * tags); ++ ++int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ const __u8 * buffer, ++ yaffs_ExtendedTags * tags); ++ ++int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo); ++ ++int yaffs_QueryInitialBlockState(yaffs_Device * dev, ++ int blockNo, ++ yaffs_BlockState * state, ++ unsigned *sequenceNumber); ++ ++int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, ++ int blockInNAND); ++ ++int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev); ++ ++#endif ++ +diff --git a/fs/yaffs2/yaffs_nandemul2k.h b/fs/yaffs2/yaffs_nandemul2k.h +new file mode 100644 +index 0000000..13520e1 +--- /dev/null ++++ b/fs/yaffs2/yaffs_nandemul2k.h +@@ -0,0 +1,39 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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. ++ */ ++ ++/* Interface to emulated NAND functions (2k page size) */ ++ ++#ifndef __YAFFS_NANDEMUL2K_H__ ++#define __YAFFS_NANDEMUL2K_H__ ++ ++#include "yaffs_guts.h" ++ ++int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND, const __u8 * data, ++ yaffs_ExtendedTags * tags); ++int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND, __u8 * data, ++ yaffs_ExtendedTags * tags); ++int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); ++int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, ++ yaffs_BlockState * state, int *sequenceNumber); ++int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, ++ int blockInNAND); ++int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev); ++int nandemul2k_GetBytesPerChunk(void); ++int nandemul2k_GetChunksPerBlock(void); ++int nandemul2k_GetNumberOfBlocks(void); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags1.c b/fs/yaffs2/yaffs_packedtags1.c +new file mode 100644 +index 0000000..f480bf1 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.c +@@ -0,0 +1,52 @@ ++/* ++ * 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 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. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t) ++{ ++ pt->chunkId = t->chunkId; ++ pt->serialNumber = t->serialNumber; ++ pt->byteCount = t->byteCount; ++ pt->objectId = t->objectId; ++ pt->ecc = 0; ++ pt->deleted = (t->chunkDeleted) ? 0 : 1; ++ pt->unusedStuff = 0; ++ pt->shouldBeFF = 0xFFFFFFFF; ++ ++} ++ ++void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt) ++{ ++ static const __u8 allFF[] = ++ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++0xff }; ++ ++ if (memcmp(allFF, pt, sizeof(yaffs_PackedTags1))) { ++ t->blockBad = 0; ++ if (pt->shouldBeFF != 0xFFFFFFFF) { ++ t->blockBad = 1; ++ } ++ t->chunkUsed = 1; ++ t->objectId = pt->objectId; ++ t->chunkId = pt->chunkId; ++ t->byteCount = pt->byteCount; ++ t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; ++ t->chunkDeleted = (pt->deleted) ? 0 : 1; ++ t->serialNumber = pt->serialNumber; ++ } else { ++ memset(t, 0, sizeof(yaffs_ExtendedTags)); ++ ++ } ++} +diff --git a/fs/yaffs2/yaffs_packedtags1.h b/fs/yaffs2/yaffs_packedtags1.h +new file mode 100644 +index 0000000..627b2f8 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.h +@@ -0,0 +1,37 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++typedef struct { ++ unsigned chunkId:20; ++ unsigned serialNumber:2; ++ unsigned byteCount:10; ++ unsigned objectId:18; ++ unsigned ecc:12; ++ unsigned deleted:1; ++ unsigned unusedStuff:1; ++ unsigned shouldBeFF; ++ ++} yaffs_PackedTags1; ++ ++void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t); ++void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt); ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags2.c b/fs/yaffs2/yaffs_packedtags2.c +new file mode 100644 +index 0000000..23e2886 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.c +@@ -0,0 +1,184 @@ ++/* ++ * 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 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. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_tagsvalidity.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunkId */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xF0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt) ++{ ++ T(YAFFS_TRACE_MTD, ++// 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)); ++} ++ ++static void yaffs_DumpTags2(const yaffs_ExtendedTags * t) ++{ ++ T(YAFFS_TRACE_MTD, ++// T(YAFFS_TRACE_ALWAYS, ++ (TSTR ++ ("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)); ++ ++} ++ ++void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t) ++{ ++ pt->t.chunkId = t->chunkId; ++ pt->t.sequenceNumber = t->sequenceNumber; ++ pt->t.byteCount = t->byteCount; ++ pt->t.objectId = t->objectId; ++ ++ if (t->chunkId == 0 && t->extraHeaderInfoAvailable) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunkId */ ++ pt->t.chunkId = EXTRA_HEADER_INFO_FLAG ++ | t->extraParentObjectId; ++ if (t->extraIsShrinkHeader) { ++ pt->t.chunkId |= EXTRA_SHRINK_FLAG; ++ } ++ if (t->extraShadows) { ++ pt->t.chunkId |= EXTRA_SHADOWS_FLAG; ++ } ++ ++ pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK; ++ pt->t.objectId |= ++ (t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { ++ pt->t.byteCount = t->extraEquivalentObjectId; ++ } else if (t->extraObjectType == YAFFS_OBJECT_TYPE_FILE) { ++ pt->t.byteCount = t->extraFileLength; ++ } else { ++ pt->t.byteCount = 0; ++ } ++ } ++ ++// yaffs_DumpPackedTags2(pt); ++// yaffs_DumpTags2(t); ++ ++#if !defined(YAFFS_IGNORE_TAGS_ECC) && !defined(CONFIG_MTD_HW_BCH_ECC) ++ { ++ yaffs_ECCCalculateOther((unsigned char *)&pt->t, ++ sizeof(yaffs_PackedTags2TagsPart), ++ &pt->ecc); ++ } ++#endif ++} ++ ++void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt) ++{ ++ yaffs_InitialiseTags(t); ++ ++ if (pt->t.sequenceNumber != 0xFFFFFFFF) { ++ /* Page is in use */ ++#if defined(YAFFS_IGNORE_TAGS_ECC) || defined(CONFIG_MTD_HW_BCH_ECC) ++ { ++ t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; ++ } ++#else ++ { ++ 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; ++ break; ++ case 1: ++ t->eccResult = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ t->eccResult = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ t->eccResult = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++#endif ++ t->blockBad = 0; ++ t->chunkUsed = 1; ++ t->objectId = pt->t.objectId; ++ t->chunkId = pt->t.chunkId; ++ t->byteCount = pt->t.byteCount; ++ t->chunkDeleted = 0; ++ t->serialNumber = 0; ++ t->sequenceNumber = pt->t.sequenceNumber; ++ ++ /* Do extra header info stuff */ ++ ++ if (pt->t.chunkId & EXTRA_HEADER_INFO_FLAG) { ++ t->chunkId = 0; ++ t->byteCount = 0; ++ ++ t->extraHeaderInfoAvailable = 1; ++ t->extraParentObjectId = ++ pt->t.chunkId & (~(ALL_EXTRA_FLAGS)); ++ t->extraIsShrinkHeader = ++ (pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0; ++ t->extraShadows = ++ (pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0; ++ t->extraObjectType = ++ pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->objectId &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { ++ t->extraEquivalentObjectId = pt->t.byteCount; ++ } else { ++ t->extraFileLength = pt->t.byteCount; ++ } ++ } ++ } ++// printk("\n TAG %d",t->chunkUsed); ++ yaffs_DumpPackedTags2(pt); ++ yaffs_DumpTags2(t); ++// printk("\n TAG %d",t->chunkUsed); ++} +diff --git a/fs/yaffs2/yaffs_packedtags2.h b/fs/yaffs2/yaffs_packedtags2.h +new file mode 100644 +index 0000000..7c4a72c +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.h +@@ -0,0 +1,38 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++typedef struct { ++ unsigned sequenceNumber; ++ unsigned objectId; ++ unsigned chunkId; ++ unsigned byteCount; ++} yaffs_PackedTags2TagsPart; ++ ++typedef struct { ++ yaffs_PackedTags2TagsPart t; ++ yaffs_ECCOther ecc; ++} yaffs_PackedTags2; ++ ++void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t); ++void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt); ++#endif +diff --git a/fs/yaffs2/yaffs_qsort.c b/fs/yaffs2/yaffs_qsort.c +new file mode 100644 +index 0000000..887c7b7 +--- /dev/null ++++ b/fs/yaffs2/yaffs_qsort.c +@@ -0,0 +1,155 @@ ++/* ++ * Copyright (c) 1992, 1993 ++ * The Regents of the University of California. 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 University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 "yportenv.h" ++//#include ++ ++/* ++ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". ++ */ ++#define swapcode(TYPE, parmi, parmj, n) { \ ++ long i = (n) / sizeof (TYPE); \ ++ register TYPE *pi = (TYPE *) (parmi); \ ++ register TYPE *pj = (TYPE *) (parmj); \ ++ do { \ ++ register TYPE t = *pi; \ ++ *pi++ = *pj; \ ++ *pj++ = t; \ ++ } while (--i > 0); \ ++} ++ ++#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ ++ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; ++ ++static __inline void ++swapfunc(char *a, char *b, int n, int swaptype) ++{ ++ if (swaptype <= 1) ++ swapcode(long, a, b, n) ++ else ++ swapcode(char, a, b, n) ++} ++ ++#define swap(a, b) \ ++ if (swaptype == 0) { \ ++ long t = *(long *)(a); \ ++ *(long *)(a) = *(long *)(b); \ ++ *(long *)(b) = t; \ ++ } else \ ++ swapfunc(a, b, es, swaptype) ++ ++#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) ++ ++static __inline char * ++med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) ++{ ++ return cmp(a, b) < 0 ? ++ (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) ++ :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); ++} ++ ++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; ++ register char *a = aa; ++ ++loop: SWAPINIT(a, es); ++ swap_cnt = 0; ++ if (n < 7) { ++ for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es) ++ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; ++ pl -= es) ++ swap(pl, pl - es); ++ return; ++ } ++ pm = (char *)a + (n / 2) * es; ++ if (n > 7) { ++ pl = (char *)a; ++ pn = (char *)a + (n - 1) * es; ++ if (n > 40) { ++ d = (n / 8) * es; ++ pl = med3(pl, pl + d, pl + 2 * d, cmp); ++ pm = med3(pm - d, pm, pm + d, cmp); ++ pn = med3(pn - 2 * d, pn - d, pn, cmp); ++ } ++ pm = med3(pl, pm, pn, cmp); ++ } ++ swap(a, pm); ++ pa = pb = (char *)a + es; ++ ++ pc = pd = (char *)a + (n - 1) * es; ++ for (;;) { ++ while (pb <= pc && (r = cmp(pb, a)) <= 0) { ++ if (r == 0) { ++ swap_cnt = 1; ++ swap(pa, pb); ++ pa += es; ++ } ++ pb += es; ++ } ++ while (pb <= pc && (r = cmp(pc, a)) >= 0) { ++ if (r == 0) { ++ swap_cnt = 1; ++ swap(pc, pd); ++ pd -= es; ++ } ++ pc -= es; ++ } ++ if (pb > pc) ++ break; ++ swap(pb, pc); ++ swap_cnt = 1; ++ pb += es; ++ pc -= es; ++ } ++ 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; ++ pl -= es) ++ swap(pl, pl - es); ++ return; ++ } ++ ++ pn = (char *)a + n * es; ++ r = min(pa - (char *)a, pb - pa); ++ vecswap(a, pb - r, r); ++ r = min((long)(pd - pc), (long)(pn - pd - es)); ++ vecswap(pb, pn - r, r); ++ if ((r = pb - pa) > 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; ++ } ++/* qsort(pn - r, r / es, es, cmp);*/ ++} +diff --git a/fs/yaffs2/yaffs_qsort.h b/fs/yaffs2/yaffs_qsort.h +new file mode 100644 +index 0000000..6d3f554 +--- /dev/null ++++ b/fs/yaffs2/yaffs_qsort.h +@@ -0,0 +1,23 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_QSORT_H__ ++#define __YAFFS_QSORT_H__ ++ ++extern void qsort (void *const base, size_t total_elems, size_t size, ++ int (*cmp)(const void *, const void *)); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_tagscompat.c b/fs/yaffs2/yaffs_tagscompat.c +new file mode 100644 +index 0000000..d594d61 +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.c +@@ -0,0 +1,530 @@ ++/* ++ * 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 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. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++ ++static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND); ++#ifdef NOTYET ++static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND); ++static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, ++ const yaffs_Spare * spare); ++static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, ++ const yaffs_Spare * spare); ++static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND); ++#endif ++ ++static const char yaffs_countBitsTable[256] = { ++ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, ++ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 ++}; ++ ++static int yaffs_CountBits(__u8 x) ++{ ++ int retVal; ++ retVal = yaffs_countBitsTable[x]; ++ return retVal; ++} ++ ++/********** Tags ECC calculations *********/ ++ ++void yaffs_CalcECC(const __u8 * data, yaffs_Spare * spare) ++{ ++ yaffs_ECCCalculate(data, spare->ecc1); ++ yaffs_ECCCalculate(&data[256], spare->ecc2); ++} ++ ++void yaffs_CalcTagsECC(yaffs_Tags * tags) ++{ ++ /* Calculate an ecc */ ++ ++ unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) { ++ ecc ^= bit; ++ } ++ } ++ } ++ ++ tags->ecc = ecc; ++ ++} ++ ++int yaffs_CheckECCOnTags(yaffs_Tags * tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_CalcTagsECC(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_CalcTagsECC(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++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]; ++} ++ ++static void yaffs_GetTagsFromSpare(yaffs_Device * dev, yaffs_Spare * sparePtr, ++ yaffs_Tags * tagsPtr) ++{ ++ yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr; ++ int result; ++ ++ tu->asBytes[0] = sparePtr->tagByte0; ++ tu->asBytes[1] = sparePtr->tagByte1; ++ tu->asBytes[2] = sparePtr->tagByte2; ++ tu->asBytes[3] = sparePtr->tagByte3; ++ tu->asBytes[4] = sparePtr->tagByte4; ++ tu->asBytes[5] = sparePtr->tagByte5; ++ tu->asBytes[6] = sparePtr->tagByte6; ++ tu->asBytes[7] = sparePtr->tagByte7; ++ ++ result = yaffs_CheckECCOnTags(tagsPtr); ++ if (result > 0) { ++ dev->tagsEccFixed++; ++ } else if (result < 0) { ++ dev->tagsEccUnfixed++; ++ } ++} ++ ++static void yaffs_SpareInitialise(yaffs_Spare * spare) ++{ ++ memset(spare, 0xFF, sizeof(yaffs_Spare)); ++} ++ ++static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND, const __u8 * data, ++ yaffs_Spare * spare) ++{ ++ if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR("**>> yaffs chunk %d is not valid" TENDSTR), ++ chunkInNAND)); ++ return YAFFS_FAIL; ++ } ++ ++ dev->nPageWrites++; ++ return dev->writeChunkToNAND(dev, chunkInNAND, data, spare); ++} ++ ++static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND, ++ __u8 * data, ++ yaffs_Spare * spare, ++ yaffs_ECCResult * eccResult, ++ int doErrorCorrection) ++{ ++ int retVal; ++ yaffs_Spare localSpare; ++ ++ dev->nPageReads++; ++ ++ if (!spare && data) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &localSpare; ++ } ++ ++ if (!dev->useNANDECC) { ++ retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare); ++ if (data && doErrorCorrection) { ++ /* Do ECC correction */ ++ /* Todo handle any errors */ ++ int eccResult1, eccResult2; ++ __u8 calcEcc[3]; ++ ++ yaffs_ECCCalculate(data, calcEcc); ++ eccResult1 = ++ yaffs_ECCCorrect(data, spare->ecc1, calcEcc); ++ yaffs_ECCCalculate(&data[256], calcEcc); ++ eccResult2 = ++ yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc); ++ ++ if (eccResult1 > 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>yaffs ecc error fix performed on chunk %d:0" ++ TENDSTR), chunkInNAND)); ++ dev->eccFixed++; ++ } else if (eccResult1 < 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>yaffs ecc error unfixed on chunk %d:0" ++ TENDSTR), chunkInNAND)); ++ dev->eccUnfixed++; ++ } ++ ++ if (eccResult2 > 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>yaffs ecc error fix performed on chunk %d:1" ++ TENDSTR), chunkInNAND)); ++ dev->eccFixed++; ++ } else if (eccResult2 < 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>yaffs ecc error unfixed on chunk %d:1" ++ TENDSTR), chunkInNAND)); ++ dev->eccUnfixed++; ++ } ++ ++ if (eccResult1 || eccResult2) { ++ /* We had a data problem on this page */ ++ yaffs_HandleReadDataError(dev, chunkInNAND); ++ } ++ ++ if (eccResult1 < 0 || eccResult2 < 0) ++ *eccResult = YAFFS_ECC_RESULT_UNFIXED; ++ else if (eccResult1 > 0 || eccResult2 > 0) ++ *eccResult = YAFFS_ECC_RESULT_FIXED; ++ else ++ *eccResult = YAFFS_ECC_RESULT_NO_ERROR; ++ } ++ } else { ++ /* Must allocate enough memory for spare+2*sizeof(int) */ ++ /* for ecc results from device. */ ++ struct yaffs_NANDSpare nspare; ++ retVal = ++ dev->readChunkFromNAND(dev, chunkInNAND, data, ++ (yaffs_Spare *) & nspare); ++ memcpy(spare, &nspare, sizeof(yaffs_Spare)); ++ if (data && doErrorCorrection) { ++ if (nspare.eccres1 > 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>mtd ecc error fix performed on chunk %d:0" ++ TENDSTR), chunkInNAND)); ++ } else if (nspare.eccres1 < 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>mtd ecc error unfixed on chunk %d:0" ++ TENDSTR), chunkInNAND)); ++ } ++ ++ if (nspare.eccres2 > 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>mtd ecc error fix performed on chunk %d:1" ++ TENDSTR), chunkInNAND)); ++ } else if (nspare.eccres2 < 0) { ++ T(YAFFS_TRACE_ERROR, ++ (TSTR ++ ("**>>mtd ecc error unfixed on chunk %d:1" ++ TENDSTR), chunkInNAND)); ++ } ++ ++ if (nspare.eccres1 || nspare.eccres2) { ++ /* We had a data problem on this page */ ++ yaffs_HandleReadDataError(dev, chunkInNAND); ++ } ++ ++ if (nspare.eccres1 < 0 || nspare.eccres2 < 0) ++ *eccResult = YAFFS_ECC_RESULT_UNFIXED; ++ else if (nspare.eccres1 > 0 || nspare.eccres2 > 0) ++ *eccResult = YAFFS_ECC_RESULT_FIXED; ++ else ++ *eccResult = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ } ++ } ++ return retVal; ++} ++ ++#ifdef NOTYET ++static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, ++ int chunkInNAND) ++{ ++ ++ static int init = 0; ++ static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; ++ static __u8 data[YAFFS_BYTES_PER_CHUNK]; ++ /* Might as well always allocate the larger size for */ ++ /* dev->useNANDECC == true; */ ++ static __u8 spare[sizeof(struct yaffs_NANDSpare)]; ++ ++ dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare); ++ ++ if (!init) { ++ memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK); ++ init = 1; ++ } ++ ++ if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK)) ++ return YAFFS_FAIL; ++ if (memcmp(cmpbuf, spare, 16)) ++ return YAFFS_FAIL; ++ ++ return YAFFS_OK; ++ ++} ++#endif ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND) ++{ ++ int blockInNAND = chunkInNAND / dev->nChunksPerBlock; ++ ++ /* Mark the block for retirement */ ++ yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; ++ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ (TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND)); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++#ifdef NOTYET ++static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND) ++{ ++} ++ ++static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, ++ const __u8 * data, ++ const yaffs_Spare * spare) ++{ ++} ++ ++static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, ++ const yaffs_Spare * spare) ++{ ++} ++ ++static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND) ++{ ++ int blockInNAND = chunkInNAND / dev->nChunksPerBlock; ++ ++ /* Mark the block for retirement */ ++ yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; ++ /* Delete the chunk */ ++ yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); ++} ++ ++static int yaffs_VerifyCompare(const __u8 * d0, const __u8 * d1, ++ const yaffs_Spare * s0, const yaffs_Spare * s1) ++{ ++ ++ if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 || ++ s0->tagByte0 != s1->tagByte0 || ++ s0->tagByte1 != s1->tagByte1 || ++ s0->tagByte2 != s1->tagByte2 || ++ s0->tagByte3 != s1->tagByte3 || ++ s0->tagByte4 != s1->tagByte4 || ++ s0->tagByte5 != s1->tagByte5 || ++ s0->tagByte6 != s1->tagByte6 || ++ s0->tagByte7 != s1->tagByte7 || ++ s0->ecc1[0] != s1->ecc1[0] || ++ s0->ecc1[1] != s1->ecc1[1] || ++ s0->ecc1[2] != s1->ecc1[2] || ++ s0->ecc2[0] != s1->ecc2[0] || ++ s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) { ++ return 0; ++ } ++ ++ return 1; ++} ++#endif /* NOTYET */ ++ ++int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ const __u8 * data, ++ const yaffs_ExtendedTags * ++ eTags) ++{ ++ yaffs_Spare spare; ++ yaffs_Tags tags; ++ ++ yaffs_SpareInitialise(&spare); ++ ++ if (eTags->chunkDeleted) { ++ spare.pageStatus = 0; ++ } else { ++ tags.objectId = eTags->objectId; ++ tags.chunkId = eTags->chunkId; ++ tags.byteCount = eTags->byteCount; ++ tags.serialNumber = eTags->serialNumber; ++ ++ if (!dev->useNANDECC && data) { ++ yaffs_CalcECC(data, &spare); ++ } ++ yaffs_LoadTagsIntoSpare(&spare, &tags); ++ ++ } ++ ++ return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare); ++} ++ ++int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ __u8 * data, ++ yaffs_ExtendedTags * eTags) ++{ ++ ++ yaffs_Spare spare; ++ yaffs_Tags tags; ++ yaffs_ECCResult eccResult; ++ ++ static yaffs_Spare spareFF; ++ static int init; ++ ++ if (!init) { ++ memset(&spareFF, 0xFF, sizeof(spareFF)); ++ init = 1; ++ } ++ ++ if (yaffs_ReadChunkFromNAND ++ (dev, chunkInNAND, data, &spare, &eccResult, 1)) { ++ /* eTags may be NULL */ ++ if (eTags) { ++ ++ int deleted = ++ (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; ++ ++ eTags->chunkDeleted = deleted; ++ eTags->eccResult = eccResult; ++ eTags->blockBad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ eTags->chunkUsed = ++ (memcmp(&spareFF, &spare, sizeof(spareFF)) != ++ 0) ? 1 : 0; ++ ++ if (eTags->chunkUsed) { ++ yaffs_GetTagsFromSpare(dev, &spare, &tags); ++ ++ eTags->objectId = tags.objectId; ++ eTags->chunkId = tags.chunkId; ++ eTags->byteCount = tags.byteCount; ++ eTags->serialNumber = tags.serialNumber; ++ } ++ } ++ ++ return YAFFS_OK; ++ } else { ++ return YAFFS_FAIL; ++ } ++} ++ ++int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, ++ int blockInNAND) ++{ ++ ++ yaffs_Spare spare; ++ ++ memset(&spare, 0xff, sizeof(yaffs_Spare)); ++ ++ spare.blockStatus = 'Y'; ++ ++ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL, ++ &spare); ++ yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, ++ int blockNo, yaffs_BlockState * ++ state, ++ int *sequenceNumber) ++{ ++ ++ yaffs_Spare spare0, spare1; ++ static yaffs_Spare spareFF; ++ static int init; ++ yaffs_ECCResult dummy; ++ ++ if (!init) { ++ memset(&spareFF, 0xFF, sizeof(spareFF)); ++ init = 1; ++ } ++ ++ *sequenceNumber = 0; ++ ++ yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL, ++ &spare0, &dummy, 1); ++ yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL, ++ &spare1, &dummy, 1); ++ ++ if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_tagscompat.h b/fs/yaffs2/yaffs_tagscompat.h +new file mode 100644 +index 0000000..c746ad9 +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.h +@@ -0,0 +1,36 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++#include "yaffs_guts.h" ++int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ const __u8 * data, ++ const yaffs_ExtendedTags * ++ tags); ++int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, ++ int chunkInNAND, ++ __u8 * data, ++ yaffs_ExtendedTags * ++ tags); ++int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, ++ int blockNo); ++int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, ++ int blockNo, yaffs_BlockState * ++ state, int *sequenceNumber); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_tagsvalidity.c b/fs/yaffs2/yaffs_tagsvalidity.c +new file mode 100644 +index 0000000..9e0bd1c +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsvalidity.c +@@ -0,0 +1,28 @@ ++/* ++ * 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 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. ++ */ ++ ++#include "yaffs_tagsvalidity.h" ++ ++void yaffs_InitialiseTags(yaffs_ExtendedTags * tags) ++{ ++ memset(tags, 0, sizeof(yaffs_ExtendedTags)); ++ tags->validMarker0 = 0xAAAAAAAA; ++ tags->validMarker1 = 0x55555555; ++} ++ ++int yaffs_ValidateTags(yaffs_ExtendedTags * tags) ++{ ++ return (tags->validMarker0 == 0xAAAAAAAA && ++ tags->validMarker1 == 0x55555555); ++ ++} +diff --git a/fs/yaffs2/yaffs_tagsvalidity.h b/fs/yaffs2/yaffs_tagsvalidity.h +new file mode 100644 +index 0000000..ba56727 +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsvalidity.h +@@ -0,0 +1,24 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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_TAGS_VALIDITY_H__ ++#define __YAFFS_TAGS_VALIDITY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); ++int yaffs_ValidateTags(yaffs_ExtendedTags * tags); ++#endif +diff --git a/fs/yaffs2/yaffsinterface.h b/fs/yaffs2/yaffsinterface.h +new file mode 100644 +index 0000000..0cfdfcf +--- /dev/null ++++ b/fs/yaffs2/yaffsinterface.h +@@ -0,0 +1,21 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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 __YAFFSINTERFACE_H__ ++#define __YAFFSINTERFACE_H__ ++ ++int yaffs_Initialise(unsigned nBlocks); ++ ++#endif +diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h +new file mode 100644 +index 0000000..b46b9a9 +--- /dev/null ++++ b/fs/yaffs2/yportenv.h +@@ -0,0 +1,162 @@ ++/* ++ * 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 Charles Manning ++ * ++ * 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 __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++#if defined CONFIG_YAFFS_WINCE ++ ++#include "ywinceenv.h" ++ ++#elif defined __KERNEL__ ++ ++#include "moduleconfig.h" ++ ++/* Linux kernel */ ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++#define yaffs_strcpy(a,b) strcpy(a,b) ++#define yaffs_strncpy(a,b,c) strncpy(a,b,c) ++#define yaffs_strlen(s) strlen(s) ++#define yaffs_sprintf sprintf ++#define yaffs_toupper(a) toupper(a) ++ ++#define Y_INLINE inline ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++/* #define YPRINTF(x) printk x */ ++#define YMALLOC(x) kmalloc(x,GFP_KERNEL) ++#define YFREE(x) kfree(x) ++#define YMALLOC_ALT(x) vmalloc(x) ++#define YFREE_ALT(x) vfree(x) ++#define YMALLOC_DMA(x) YMALLOC(x) ++ ++// KR - added for use in scan so processes aren't blocked indefinitely. ++#define YYIELD() schedule() ++ ++#define YAFFS_ROOT_MODE 0666 ++#define YAFFS_LOSTNFOUND_MODE 0666 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define yaffs_SumCompare(x,y) ((x) == (y)) ++#define yaffs_strcmp(a,b) strcmp(a,b) ++ ++#define TENDSTR "\n" ++#define TSTR(x) KERN_WARNING x ++#define TOUT(p) printk p ++ ++#elif defined CONFIG_YAFFS_DIRECT ++ ++/* Direct interface */ ++#include "ydirectenv.h" ++ ++#elif defined CONFIG_YAFFS_UTIL ++ ++/* Stuff for YAFFS utilities */ ++ ++#include "stdlib.h" ++#include "stdio.h" ++#include "string.h" ++ ++#include "devextras.h" ++ ++#define YMALLOC(x) malloc(x) ++#define YFREE(x) free(x) ++#define YMALLOC_ALT(x) malloc(x) ++#define YFREE_ALT(x) free(x) ++ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++#define yaffs_strcpy(a,b) strcpy(a,b) ++#define yaffs_strncpy(a,b,c) strncpy(a,b,c) ++#define yaffs_strlen(s) strlen(s) ++#define yaffs_sprintf sprintf ++#define yaffs_toupper(a) toupper(a) ++ ++#define Y_INLINE inline ++ ++/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */ ++/* #define YALERT(s) YINFO(s) */ ++ ++#define TENDSTR "\n" ++#define TSTR(x) x ++#define TOUT(p) printf p ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++/* #define YPRINTF(x) printf x */ ++ ++#define YAFFS_ROOT_MODE 0666 ++#define YAFFS_LOSTNFOUND_MODE 0666 ++ ++#define yaffs_SumCompare(x,y) ((x) == (y)) ++#define yaffs_strcmp(a,b) strcmp(a,b) ++ ++#else ++/* Should have specified a configuration type */ ++#error Unknown configuration ++ ++#endif ++ ++extern unsigned yaffs_traceMask; ++ ++#define YAFFS_TRACE_ERROR 0x00000001 ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++#define YAFFS_TRACE_ALWAYS 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++ ++#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__)) ++#endif ++ ++#endif +diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h +index b2dd9b3..a5f6e38 100644 +--- a/include/asm-mips/bootinfo.h ++++ b/include/asm-mips/bootinfo.h +@@ -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 --git a/include/asm-mips/cpu.h b/include/asm-mips/cpu.h +index 54fc18a..9e83fef 100644 +--- a/include/asm-mips/cpu.h ++++ b/include/asm-mips/cpu.h +@@ -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 @@ enum cpu_type_enum { + */ + CPU_5KC, CPU_20KC, CPU_25KF, CPU_SB1, CPU_SB1A, CPU_LOONGSON2, + ++ /* ++ * Ingenic class processors ++ */ ++ CPU_JZRISC, CPU_XBURST, ++ + CPU_LAST + }; + +diff --git a/include/asm-mips/jzsoc.h b/include/asm-mips/jzsoc.h +new file mode 100644 +index 0000000..54df338 +--- /dev/null ++++ b/include/asm-mips/jzsoc.h +@@ -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 --git a/include/asm-mips/mach-generic/irq.h b/include/asm-mips/mach-generic/irq.h +index 70d9a25..73b7a83 100644 +--- a/include/asm-mips/mach-generic/irq.h ++++ b/include/asm-mips/mach-generic/irq.h +@@ -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 --git a/include/asm-mips/mach-jz4730/board-pmp.h b/include/asm-mips/mach-jz4730/board-pmp.h +new file mode 100644 +index 0000000..44475d2 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/board-pmp.h +@@ -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 --git a/include/asm-mips/mach-jz4730/clock.h b/include/asm-mips/mach-jz4730/clock.h +new file mode 100644 +index 0000000..16971d0 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/clock.h +@@ -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 --git a/include/asm-mips/mach-jz4730/dma.h b/include/asm-mips/mach-jz4730/dma.h +new file mode 100644 +index 0000000..511152e +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/dma.h +@@ -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 --git a/include/asm-mips/mach-jz4730/jz4730.h b/include/asm-mips/mach-jz4730/jz4730.h +new file mode 100644 +index 0000000..5688f01 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/jz4730.h +@@ -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 --git a/include/asm-mips/mach-jz4730/misc.h b/include/asm-mips/mach-jz4730/misc.h +new file mode 100644 +index 0000000..ea01474 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/misc.h +@@ -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 --git a/include/asm-mips/mach-jz4730/ops.h b/include/asm-mips/mach-jz4730/ops.h +new file mode 100644 +index 0000000..625419e +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/ops.h +@@ -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 --git a/include/asm-mips/mach-jz4730/regs.h b/include/asm-mips/mach-jz4730/regs.h +new file mode 100644 +index 0000000..86bde91 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/regs.h +@@ -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 --git a/include/asm-mips/mach-jz4730/serial.h b/include/asm-mips/mach-jz4730/serial.h +new file mode 100644 +index 0000000..4a0afc3 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/serial.h +@@ -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 --git a/include/asm-mips/mach-jz4730/war.h b/include/asm-mips/mach-jz4730/war.h +new file mode 100644 +index 0000000..3a5bc17 +--- /dev/null ++++ b/include/asm-mips/mach-jz4730/war.h +@@ -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 --git a/include/asm-mips/mach-jz4740/board-dipper.h b/include/asm-mips/mach-jz4740/board-dipper.h +new file mode 100644 +index 0000000..ae84f24 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-dipper.h +@@ -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 --git a/include/asm-mips/mach-jz4740/board-leo.h b/include/asm-mips/mach-jz4740/board-leo.h +new file mode 100644 +index 0000000..4b883e2 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-leo.h +@@ -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 --git a/include/asm-mips/mach-jz4740/board-lyra.h b/include/asm-mips/mach-jz4740/board-lyra.h +new file mode 100644 +index 0000000..29e0ce0 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-lyra.h +@@ -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 --git a/include/asm-mips/mach-jz4740/board-pavo.h b/include/asm-mips/mach-jz4740/board-pavo.h +new file mode 100644 +index 0000000..f33831b +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-pavo.h +@@ -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 --git a/include/asm-mips/mach-jz4740/board-virgo.h b/include/asm-mips/mach-jz4740/board-virgo.h +new file mode 100644 +index 0000000..acd7bb7 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-virgo.h +@@ -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 --git a/include/asm-mips/mach-jz4740/clock.h b/include/asm-mips/mach-jz4740/clock.h +new file mode 100644 +index 0000000..11ffe88 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/clock.h +@@ -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 --git a/include/asm-mips/mach-jz4740/dma.h b/include/asm-mips/mach-jz4740/dma.h +new file mode 100644 +index 0000000..b82b984 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/dma.h +@@ -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 --git a/include/asm-mips/mach-jz4740/jz4740.h b/include/asm-mips/mach-jz4740/jz4740.h +new file mode 100644 +index 0000000..437caf4 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/jz4740.h +@@ -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 --git a/include/asm-mips/mach-jz4740/misc.h b/include/asm-mips/mach-jz4740/misc.h +new file mode 100644 +index 0000000..8f14a5a +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/misc.h +@@ -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 --git a/include/asm-mips/mach-jz4740/ops.h b/include/asm-mips/mach-jz4740/ops.h +new file mode 100644 +index 0000000..6ff050a +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/ops.h +@@ -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 --git a/include/asm-mips/mach-jz4740/regs.h b/include/asm-mips/mach-jz4740/regs.h +new file mode 100644 +index 0000000..07528bc +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/regs.h +@@ -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 --git a/include/asm-mips/mach-jz4740/serial.h b/include/asm-mips/mach-jz4740/serial.h +new file mode 100644 +index 0000000..c4819b9 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/serial.h +@@ -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 --git a/include/asm-mips/mach-jz4740/war.h b/include/asm-mips/mach-jz4740/war.h +new file mode 100644 +index 0000000..3a5bc17 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/war.h +@@ -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 --git a/include/asm-mips/mach-jz4750/board-apus.h b/include/asm-mips/mach-jz4750/board-apus.h +new file mode 100644 +index 0000000..18bba40 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/board-apus.h +@@ -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 --git a/include/asm-mips/mach-jz4750/board-fuwa.h b/include/asm-mips/mach-jz4750/board-fuwa.h +new file mode 100644 +index 0000000..8b6a199 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/board-fuwa.h +@@ -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 --git a/include/asm-mips/mach-jz4750/clock.h b/include/asm-mips/mach-jz4750/clock.h +new file mode 100644 +index 0000000..5747e45 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/clock.h +@@ -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 --git a/include/asm-mips/mach-jz4750/dma.h b/include/asm-mips/mach-jz4750/dma.h +new file mode 100644 +index 0000000..fc1f2c8 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/dma.h +@@ -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 --git a/include/asm-mips/mach-jz4750/jz4750.h b/include/asm-mips/mach-jz4750/jz4750.h +new file mode 100644 +index 0000000..9517780 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/jz4750.h +@@ -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 --git a/include/asm-mips/mach-jz4750/misc.h b/include/asm-mips/mach-jz4750/misc.h +new file mode 100644 +index 0000000..f6c75c9 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/misc.h +@@ -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 --git a/include/asm-mips/mach-jz4750/ops.h b/include/asm-mips/mach-jz4750/ops.h +new file mode 100644 +index 0000000..2a92d85 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/ops.h +@@ -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 --git a/include/asm-mips/mach-jz4750/war.h b/include/asm-mips/mach-jz4750/war.h +new file mode 100644 +index 0000000..3a5bc17 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750/war.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/board-fuwa1.h b/include/asm-mips/mach-jz4750d/board-fuwa1.h +new file mode 100644 +index 0000000..05af0ca +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/board-fuwa1.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/clock.h b/include/asm-mips/mach-jz4750d/clock.h +new file mode 100644 +index 0000000..57b3957 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/clock.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/dma.h b/include/asm-mips/mach-jz4750d/dma.h +new file mode 100644 +index 0000000..3cca844 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/dma.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/jz4750d.h b/include/asm-mips/mach-jz4750d/jz4750d.h +new file mode 100644 +index 0000000..be80a57 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/jz4750d.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/misc.h b/include/asm-mips/mach-jz4750d/misc.h +new file mode 100644 +index 0000000..cd0b13c +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/misc.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/ops.h b/include/asm-mips/mach-jz4750d/ops.h +new file mode 100644 +index 0000000..cfb762c +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/ops.h +@@ -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 --git a/include/asm-mips/mach-jz4750d/war.h b/include/asm-mips/mach-jz4750d/war.h +new file mode 100644 +index 0000000..3a5bc17 +--- /dev/null ++++ b/include/asm-mips/mach-jz4750d/war.h +@@ -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 --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h +index 786f7e3..1198c69 100644 +--- a/include/asm-mips/ptrace.h ++++ b/include/asm-mips/ptrace.h +@@ -79,7 +79,7 @@ struct pt_regs { + /* + * 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 --git a/include/asm-mips/r4kcache.h b/include/asm-mips/r4kcache.h +index 2b8466f..1f93273 100644 +--- a/include/asm-mips/r4kcache.h ++++ b/include/asm-mips/r4kcache.h +@@ -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 @@ static inline void flush_icache_line_indexed(unsigned long addr) + { + __iflush_prologue + cache_op(Index_Invalidate_I, addr); ++ INVALIDATE_BTB(); + __iflush_epilogue + } + +@@ -151,6 +204,7 @@ static inline void flush_dcache_line_indexed(unsigned long addr) + { + __dflush_prologue + cache_op(Index_Writeback_Inv_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -163,6 +217,7 @@ static inline void flush_icache_line(unsigned long addr) + { + __iflush_prologue + cache_op(Hit_Invalidate_I, addr); ++ INVALIDATE_BTB(); + __iflush_epilogue + } + +@@ -170,6 +225,7 @@ static inline void flush_dcache_line(unsigned long addr) + { + __dflush_prologue + cache_op(Hit_Writeback_Inv_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -177,6 +233,7 @@ static inline void invalidate_dcache_line(unsigned long addr) + { + __dflush_prologue + cache_op(Hit_Invalidate_D, addr); ++ SYNC_WB(); + __dflush_epilogue + } + +@@ -209,6 +266,7 @@ static inline void flush_scache_line(unsigned long addr) + 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_flush_icache_line(unsigned long addr) + 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 @@ static inline void blast_##pfx##cache##lsize##_page_indexed(unsigned long page) + __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 @@ static inline void prot##blast_##pfx##cache##_range(unsigned long start, \ + __##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 --git a/include/asm-mips/sizes.h b/include/asm-mips/sizes.h +new file mode 100644 +index 0000000..503843d +--- /dev/null ++++ b/include/asm-mips/sizes.h +@@ -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 --git a/include/linux/fs.h b/include/linux/fs.h +index b3ec4a4..ed8b69b 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1670,6 +1670,8 @@ static inline void invalidate_remote_inode(struct inode *inode) + 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 --git a/include/linux/i2c-dev.h b/include/linux/i2c-dev.h +index 311315b..3611d21 100644 +--- a/include/linux/i2c-dev.h ++++ b/include/linux/i2c-dev.h +@@ -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 @@ struct i2c_rdwr_ioctl_data { + #define I2C_MAJOR 89 /* Device major number */ + #endif + ++extern void i2c_jz_setclk(unsigned int i2cclk); + #endif /* _LINUX_I2C_DEV_H */ +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 7ab962f..b0e08eb 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -41,6 +41,7 @@ struct mmc_ios { + + #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 --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index 783fc98..7d41040 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -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 erase_info { + }; + + 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 @@ typedef enum { + */ + 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; +@@ -101,7 +101,7 @@ struct mtd_oob_ops { + 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 +@@ -143,18 +143,18 @@ struct mtd_info { + 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); + + /* +@@ -162,33 +162,33 @@ struct mtd_info { + * 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 */ + +@@ -233,10 +233,10 @@ 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); ++ 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 --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index c42bc7f..a14d127 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -39,14 +39,14 @@ extern void nand_release (struct mtd_info *mtd); + 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 @@ extern void nand_wait_ready(struct mtd_info *mtd); + * 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 @@ struct nand_chip { + 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 @@ struct nand_chip { + 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 @@ struct nand_flash_dev { + char *name; + int id; + unsigned long pagesize; +- unsigned long chipsize; ++ u64 chipsize; + unsigned long erasesize; + unsigned long options; + }; +@@ -538,13 +544,13 @@ struct nand_bbt_descr { + #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 --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h +index da6b3d6..2aabc15 100644 +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -38,8 +38,9 @@ + + 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 --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h +index 3d967b6..f71201d 100644 +--- a/include/linux/mtd/ubi.h ++++ b/include/linux/mtd/ubi.h +@@ -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 ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + 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 --git a/include/linux/vt.h b/include/linux/vt.h +index 02c1c02..edbb787 100644 +--- a/include/linux/vt.h ++++ b/include/linux/vt.h +@@ -18,10 +18,16 @@ extern int unregister_vt_notifier(struct notifier_block *nb); + * 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 --git a/include/linux/writeback.h b/include/linux/writeback.h +index c6148bb..15c7a0e 100644 +--- a/include/linux/writeback.h ++++ b/include/linux/writeback.h +@@ -70,6 +70,7 @@ struct writeback_control { + 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 --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h +index f71dac4..132c835 100644 +--- a/include/mtd/mtd-abi.h ++++ b/include/mtd/mtd-abi.h +@@ -1,5 +1,5 @@ + /* +- * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $ ++ * $Id: mtd-abi.h,v 1.1.1.1 2008-03-28 04:29:21 jlwei Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ +@@ -7,9 +7,17 @@ + #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 +26,14 @@ struct mtd_oob_buf { + 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 +68,7 @@ struct mtd_oob_buf { + 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 +79,7 @@ struct mtd_info_user { + }; + + 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 +102,8 @@ struct otp_info { + #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 +111,7 @@ 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 +@@ -104,7 +121,8 @@ struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; +- uint32_t eccpos[32]; ++ uint32_t eccpos[104]; /* more fields(13*8) are required for ++ * 8-bit BCH ECC and 4KB pagesize nand, by Regen */ + }; + + struct nand_oobfree { +@@ -119,7 +137,7 @@ struct nand_oobfree { + */ + 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 --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h +index fe06ded..cb0536d 100644 +--- a/include/mtd/ubi-user.h ++++ b/include/mtd/ubi-user.h +@@ -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 @@ struct ubi_mkvol_req { + 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 @@ struct ubi_rsvol_req { + 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 --git a/include/sound/pcm.h b/include/sound/pcm.h +index 5e9cc46..c2fb4df 100644 +--- a/include/sound/pcm.h ++++ b/include/sound/pcm.h +@@ -107,23 +107,23 @@ struct snd_pcm_ops { + #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 --git a/include/sound/pcm.h.org b/include/sound/pcm.h.org +new file mode 100644 +index 0000000..5e9cc46 +--- /dev/null ++++ b/include/sound/pcm.h.org +@@ -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 --git a/init/do_mounts.c b/init/do_mounts.c +index 4efa1e5..a2a6586 100644 +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -440,7 +440,9 @@ void __init prepare_namespace(void) + + 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 --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c +index fb3dde4..d76c4b7 100644 +--- a/sound/core/pcm_native.c ++++ b/sound/core/pcm_native.c +@@ -1780,12 +1780,13 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, + 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 @@ 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; ++#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 --git a/sound/core/pcm_native.c.org b/sound/core/pcm_native.c.org +new file mode 100644 +index 0000000..fb3dde4 +--- /dev/null ++++ b/sound/core/pcm_native.c.org +@@ -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 --git a/sound/oss/Kconfig b/sound/oss/Kconfig +index 857008b..ed2e499 100644 +--- a/sound/oss/Kconfig ++++ b/sound/oss/Kconfig +@@ -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 @@ config SOUND_BCM_CS4297A + 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 @@ config SOUND_HAL2 + 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 @@ config SOUND_TRIDENT + + + 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 @@ config MSND_FIFOSIZE + 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 @@ config SOUND_DMAP + + 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 @@ config SOUND_AEDSP16 + 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 @@ config SOUND_WAVEARTIST + 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 --git a/sound/oss/Makefile b/sound/oss/Makefile +index f883c4b..b4bfb33 100644 +--- a/sound/oss/Makefile ++++ b/sound/oss/Makefile +@@ -10,6 +10,12 @@ obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o + + # 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 --git a/sound/oss/ak4642en.c b/sound/oss/ak4642en.c +new file mode 100644 +index 0000000..092f7f0 +--- /dev/null ++++ b/sound/oss/ak4642en.c +@@ -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 --git a/sound/oss/jz_ac97.c b/sound/oss/jz_ac97.c +new file mode 100644 +index 0000000..698a003 +--- /dev/null ++++ b/sound/oss/jz_ac97.c +@@ -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 --git a/sound/oss/jz_i2s.c b/sound/oss/jz_i2s.c +new file mode 100644 +index 0000000..814403c +--- /dev/null ++++ b/sound/oss/jz_i2s.c +@@ -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 --git a/sound/oss/jz_i2s_4750.c b/sound/oss/jz_i2s_4750.c +new file mode 100644 +index 0000000..b18270f +--- /dev/null ++++ b/sound/oss/jz_i2s_4750.c +@@ -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 --git a/sound/oss/jz_i2s_dlv_dma_test.c b/sound/oss/jz_i2s_dlv_dma_test.c +new file mode 100644 +index 0000000..49a64a2 +--- /dev/null ++++ b/sound/oss/jz_i2s_dlv_dma_test.c +@@ -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 --git a/sound/oss/jz_i2s_for_4750.c b/sound/oss/jz_i2s_for_4750.c +new file mode 100644 +index 0000000..f492295 +--- /dev/null ++++ b/sound/oss/jz_i2s_for_4750.c +@@ -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 --git a/sound/oss/jz_pcm_tlv320aic1106_dma.c b/sound/oss/jz_pcm_tlv320aic1106_dma.c +new file mode 100644 +index 0000000..1a1f4cf +--- /dev/null ++++ b/sound/oss/jz_pcm_tlv320aic1106_dma.c +@@ -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 --git a/sound/oss/jzcodec.c b/sound/oss/jzcodec.c +new file mode 100644 +index 0000000..0af6580 +--- /dev/null ++++ b/sound/oss/jzcodec.c +@@ -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 --git a/sound/oss/jzdlv.c b/sound/oss/jzdlv.c +new file mode 100644 +index 0000000..8f8f2ba +--- /dev/null ++++ b/sound/oss/jzdlv.c +@@ -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 --git a/sound/oss/jzdlv.h b/sound/oss/jzdlv.h +new file mode 100644 +index 0000000..d361563 +--- /dev/null ++++ b/sound/oss/jzdlv.h +@@ -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 --git a/sound/oss/os.h b/sound/oss/os.h +index a1a962d..2fb00ab 100644 +--- a/sound/oss/os.h ++++ b/sound/oss/os.h +@@ -9,7 +9,6 @@ + #ifdef __KERNEL__ + #include + #include +-#include + #include + #include + #include +diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig +index 97b2552..5805b5d 100644 +--- a/sound/soc/Kconfig ++++ b/sound/soc/Kconfig +@@ -28,6 +28,7 @@ source "sound/soc/at91/Kconfig" + 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 --git a/sound/soc/Makefile b/sound/soc/Makefile +index 3041403..41d3e95 100644 +--- a/sound/soc/Makefile ++++ b/sound/soc/Makefile +@@ -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 --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index 7824880..f1876c4 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -37,3 +37,8 @@ config SND_SOC_CS4270_VD33_ERRATA + 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 --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 7ad78e3..7494158 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -4,6 +4,7 @@ snd-soc-wm8750-objs := wm8750.o + 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_WM8750) += snd-soc-wm8750.o + 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 --git a/sound/soc/codecs/jzcodec.c b/sound/soc/codecs/jzcodec.c +new file mode 100644 +index 0000000..a65934d +--- /dev/null ++++ b/sound/soc/codecs/jzcodec.c +@@ -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 --git a/sound/soc/codecs/jzcodec.h b/sound/soc/codecs/jzcodec.h +new file mode 100644 +index 0000000..7e4dedf +--- /dev/null ++++ b/sound/soc/codecs/jzcodec.h +@@ -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 --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig +new file mode 100644 +index 0000000..0bfbffe +--- /dev/null ++++ b/sound/soc/jz4740/Kconfig +@@ -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 --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile +new file mode 100644 +index 0000000..4c79b13 +--- /dev/null ++++ b/sound/soc/jz4740/Makefile +@@ -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 --git a/sound/soc/jz4740/jz4740-ac97.c b/sound/soc/jz4740/jz4740-ac97.c +new file mode 100644 +index 0000000..84da470 +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-ac97.c +@@ -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 --git a/sound/soc/jz4740/jz4740-ac97.h b/sound/soc/jz4740/jz4740-ac97.h +new file mode 100644 +index 0000000..e80e2a0 +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-ac97.h +@@ -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 --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c +new file mode 100644 +index 0000000..de40d4d +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-i2s.c +@@ -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 --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h +new file mode 100644 +index 0000000..f4cbcec +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-i2s.h +@@ -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 --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c +new file mode 100644 +index 0000000..516dd1f +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-pcm.c +@@ -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 --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h +new file mode 100644 +index 0000000..68ea842 +--- /dev/null ++++ b/sound/soc/jz4740/jz4740-pcm.h +@@ -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 --git a/sound/soc/jz4740/pavo.c b/sound/soc/jz4740/pavo.c +new file mode 100644 +index 0000000..9d6056e +--- /dev/null ++++ b/sound/soc/jz4740/pavo.c +@@ -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"); +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0003-add-uImage-to-gitignore.patch b/target/linux/xburst/patches-2.6.24/0003-add-uImage-to-gitignore.patch new file mode 100644 index 000000000..0d8360321 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0003-add-uImage-to-gitignore.patch @@ -0,0 +1,24 @@ +From f4b9d5f6b84d8a80f37bd21d851f2f0f620cd740 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Tue, 21 Apr 2009 22:18:02 +0800 +Subject: [PATCH] add uImage to gitignore + +--- + .gitignore | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +diff --git a/.gitignore b/.gitignore +index 8d14531..a79dfeb 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -52,3 +52,7 @@ cscope.* + + *.orig + *.rej ++ ++semantic.cache* ++uImage ++*~ +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0004-first-pi-work-kernel.patch b/target/linux/xburst/patches-2.6.24/0004-first-pi-work-kernel.patch new file mode 100644 index 000000000..43a273060 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0004-first-pi-work-kernel.patch @@ -0,0 +1,3818 @@ +From aadf2187e0236fd2ac90719f89f912f84ef87a5a Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Tue, 21 Apr 2009 22:18:22 +0800 +Subject: [PATCH] first pi work kernel + +--- + arch/mips/Kconfig | 8 + + arch/mips/configs/pi_defconfig | 1297 +++++++++++++++++++ + arch/mips/jz4740/Makefile | 1 + + arch/mips/jz4740/board-pi.c | 114 ++ + drivers/mtd/nand/jz4740_nand.c | 2122 ++++++++++++++++--------------- + drivers/video/jzlcd.c | 21 +- + drivers/video/jzlcd.h | 38 +- + include/asm-mips/mach-jz4740/board-pi.h | 68 + + include/asm-mips/mach-jz4740/jz4740.h | 4 + + 9 files changed, 2623 insertions(+), 1050 deletions(-) + create mode 100644 arch/mips/configs/pi_defconfig + create mode 100644 arch/mips/jz4740/board-pi.c + create mode 100644 include/asm-mips/mach-jz4740/board-pi.h + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 3d6eddf..bdaf1ce 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -32,6 +32,14 @@ config JZ4740_PAVO + select SYS_SUPPORTS_LITTLE_ENDIAN + select SOC_JZ4740 + ++config JZ4740_PI ++ bool "Ingenic JZ4740 PI " ++ 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 +diff --git a/arch/mips/configs/pi_defconfig b/arch/mips/configs/pi_defconfig +new file mode 100644 +index 0000000..f9451c7 +--- /dev/null ++++ b/arch/mips/configs/pi_defconfig +@@ -0,0 +1,1297 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Tue Apr 21 17:17:32 2009 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++CONFIG_JZ4740_PI=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_JZ4750D_FUWA1 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_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 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_JZ_TCSM 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 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=y ++# 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_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_JZ4750 is not set ++# 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_UDC_USE_LB_CACHE=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_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_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 --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile +index 7592f4e..dcf4d0c 100644 +--- a/arch/mips/jz4740/Makefile ++++ b/arch/mips/jz4740/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_PROC_FS) += proc.o + # board specific support + + obj-$(CONFIG_JZ4740_PAVO) += board-pavo.o ++obj-$(CONFIG_JZ4740_PI) += board-pi.o + obj-$(CONFIG_JZ4740_LEO) += board-leo.o + obj-$(CONFIG_JZ4740_LYRA) += board-lyra.o + obj-$(CONFIG_JZ4725_DIPPER) += board-dipper.o +diff --git a/arch/mips/jz4740/board-pi.c b/arch/mips/jz4740/board-pi.c +new file mode 100644 +index 0000000..8c8544f +--- /dev/null ++++ b/arch/mips/jz4740/board-pi.c +@@ -0,0 +1,114 @@ ++/* ++ * linux/arch/mips/jz4740/board-pi.c ++ * ++ * JZ4740 PI setup routines. ++ * ++ * Copyright (c) 2009 PI. ++ * Author: xiangfu ++ * ++ * 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 pi_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 PI setup\n"); ++ ++ board_cpm_setup(); ++ board_gpio_setup(); ++ ++ jz_timer_callback = pavo_timer_callback; ++} +diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c +index ccd4ed6..0fe5b36 100644 +--- a/drivers/mtd/nand/jz4740_nand.c ++++ b/drivers/mtd/nand/jz4740_nand.c +@@ -1,1037 +1,1085 @@ +-/* +- * 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 ++/* ++ * 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_PI ++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_PI */ ++ ++ ++#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 --git a/drivers/video/jzlcd.c b/drivers/video/jzlcd.c +index bb461fd..93b9e6f 100644 +--- a/drivers/video/jzlcd.c ++++ b/drivers/video/jzlcd.c +@@ -126,15 +126,18 @@ static struct jzfb_info jzfb = { + 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_FOXCONN_PT035TN01) ++ #if defined(CONFIG_JZ4740_PAVO) ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | MODE_TFT_18BIT | PCLK_N, ++ 320, 240, 18, 80, 1, 1, 10, 50, 10, 13 ++ #elif defined(CONFIG_JZ4740_PI) ++ MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N, ++ 320, 240, 32, 70, 1, 1, 273, 140, 1, 20 ++ #else ++ MODE_TFT_GEN | HSYNC_N | VSYNC_N | PCLK_N, ++ 320, 240, 16, 110, 1, 1, 10, 50, 10, 13 ++ #endif ++#endif /* CONFIG_JZLCD_FOXCONN_PT035TN01 */ + #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 +diff --git a/drivers/video/jzlcd.h b/drivers/video/jzlcd.h +index 3676b9b..d441d97 100644 +--- a/drivers/video/jzlcd.h ++++ b/drivers/video/jzlcd.h +@@ -359,12 +359,16 @@ do { \ + + #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 */ ++#if defined(CONFIG_JZ4740_PI) ++#define MODE 0xc9 ++#else + #define MODE 0xcd /* 24bit parellel RGB */ + #endif ++#endif ++ + #if defined(CONFIG_JZLCD_INNOLUX_PT035TN01_SERIAL) + #define MODE 0xc9 /* 8bit serial RGB */ + #endif +@@ -384,6 +388,11 @@ do { \ + #define SPCK (32*1+17) //LCD_CLS + #define SPDA (32*2+12) //LCD_D12 + #define LCD_RET (32*2+23) //LCD_REV, GPC23 ++#elif defined(CONFIG_JZ4740_PI) ++ #define SPEN (32*2+21) //LCD_SPL ++ #define SPCK (32*2+23) //LCD_CLS ++ #define SPDA (32*2+22) //LCD_D12 ++ #define LCD_RET (32*3+27) + #if 0 /*old driver*/ + #define SPEN (32*1+18) //LCD_SPL + #define SPCK (32*1+17) //LCD_CLS +@@ -653,7 +662,6 @@ do { \ + + #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 */ +@@ -708,11 +716,32 @@ __gpio_as_output(GPIO_PWM); \ + __gpio_clear_pin(GPIO_PWM); \ + } while (0) + ++#elif defined(CONFIG_JZ4740_PI) ++#define GPIO_PWM 123 /* GP_D27 */ ++#define PWM_CHN 4 /* pwm channel */ ++#define PWM_FULL 101 ++#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) ++#define __lcd_display_pin_init() \ ++do { \ ++ __gpio_as_output(GPIO_DISP_OFF_N); \ ++ __cpm_start_tcu(); \ ++ __lcd_special_pin_init(); \ ++} while (0) /* CONFIG_MIPS_JZ4740_PI) */ + #else + #define __lcd_set_backlight_level(n) + #define __lcd_close_backlight() + +-#endif /* #if defined(CONFIG_MIPS_JZ4740_PAVO) */ ++#endif + + #define __lcd_display_pin_init() \ + do { \ +@@ -735,7 +764,7 @@ do { \ + __gpio_clear_pin(GPIO_DISP_OFF_N); \ + } while (0) + +-#endif /* CONFIG_MIPS_JZ4740_LEO */ ++#endif /* (CONFIG_SOC_JZ4740) */ + + #if defined(CONFIG_JZLCD_MSTN_240x128) + +@@ -772,6 +801,7 @@ static void vsync_irq(int irq, void *dev_id, struct pt_regs *reg) + /* We uses AC BIAs pin to generate VCOM signal, so above code should be removed. + */ + #endif ++ + /***************************************************************************** + * LCD display pin dummy macros + *****************************************************************************/ +diff --git a/include/asm-mips/mach-jz4740/board-pi.h b/include/asm-mips/mach-jz4740/board-pi.h +new file mode 100644 +index 0000000..54a7c95 +--- /dev/null ++++ b/include/asm-mips/mach-jz4740/board-pi.h +@@ -0,0 +1,68 @@ ++/* ++ * linux/include/asm-mips/mach-jz4740/board-pi.h ++ * ++ * JZ4730-based PI ver 2.x definition. ++ * ++ * Copyright (c) 2009 PI. ++ * Author: xiangfu ++ * ++ * 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_PI_H__ ++#define __ASM_JZ4740_PI_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 98 /* GPD2 */ ++#define GPIO_SD_CD_N 96 /* GPD0 */ ++#define GPIO_SD_WP 112 /* GPD16 */ ++#define GPIO_USB_DETE 124 /* GPD28 */ ++#define GPIO_DISP_OFF_N 117 /* GPD21 */ ++#define GPIO_LED_EN 124 ++#define GPIO_DC_DETE_N 100 ++#define GPIO_CHARG_STAT_N 91 /* GPC27 */ ++ ++#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_PI_H__ */ +diff --git a/include/asm-mips/mach-jz4740/jz4740.h b/include/asm-mips/mach-jz4740/jz4740.h +index 437caf4..b2c3872 100644 +--- a/include/asm-mips/mach-jz4740/jz4740.h ++++ b/include/asm-mips/mach-jz4740/jz4740.h +@@ -27,6 +27,10 @@ + #include + #endif + ++#ifdef CONFIG_JZ4740_PI ++#include ++#endif ++ + #ifdef CONFIG_JZ4740_LEO + #include + #endif +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0005-fix-timer-callback.patch b/target/linux/xburst/patches-2.6.24/0005-fix-timer-callback.patch new file mode 100644 index 000000000..034e99686 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0005-fix-timer-callback.patch @@ -0,0 +1,23 @@ +From c3231a3e316c05b63b573b9d8e6b92970de6037c Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Tue, 21 Apr 2009 22:19:10 +0800 +Subject: [PATCH] fix timer callback + +--- + arch/mips/jz4740/board-pi.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/jz4740/board-pi.c b/arch/mips/jz4740/board-pi.c +index 8c8544f..309ed9f 100644 +--- a/arch/mips/jz4740/board-pi.c ++++ b/arch/mips/jz4740/board-pi.c +@@ -110,5 +110,5 @@ void __init jz_board_setup(void) + board_cpm_setup(); + board_gpio_setup(); + +- jz_timer_callback = pavo_timer_callback; ++ jz_timer_callback = pi_timer_callback; + } +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0006-lcd-work.patch b/target/linux/xburst/patches-2.6.24/0006-lcd-work.patch new file mode 100644 index 000000000..064b56269 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0006-lcd-work.patch @@ -0,0 +1,459 @@ +From 3e775be4242bb0301cb2024cc647988d1a51731f Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Wed, 22 Apr 2009 13:04:25 +0800 +Subject: [PATCH] lcd work + +--- + arch/mips/configs/pi_defconfig | 121 +++++-------------------------- + arch/mips/jz4740/board-pi.c | 6 +- + drivers/char/jzchar/poweroff.c | 2 - + drivers/mtd/nand/jz4740_nand.c | 1 - + drivers/video/jzlcd.c | 9 +-- + drivers/video/jzlcd.h | 45 +++--------- + include/asm-mips/mach-jz4740/board-pi.h | 35 +++++++--- + include/asm-mips/mach-jz4740/jz4740.h | 1 - + include/asm-mips/mach-jz4740/serial.h | 9 ++- + 9 files changed, 68 insertions(+), 161 deletions(-) + +diff --git a/arch/mips/configs/pi_defconfig b/arch/mips/configs/pi_defconfig +index f9451c7..8c0019a 100644 +--- a/arch/mips/configs/pi_defconfig ++++ b/arch/mips/configs/pi_defconfig +@@ -1,7 +1,7 @@ + # + # Automatically generated make config: don't edit + # Linux kernel version: 2.6.24.3 +-# Tue Apr 21 17:17:32 2009 ++# Wed Apr 22 12:15:04 2009 + # + CONFIG_MIPS=y + +@@ -535,65 +535,7 @@ CONFIG_SCSI_LOWLEVEL=y + # 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_NETDEVICES is not set + # CONFIG_ISDN is not set + # CONFIG_PHONE is not set + +@@ -607,18 +549,24 @@ CONFIG_INPUT=y + # + # 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_MOUSEDEV is not set + # CONFIG_INPUT_JOYDEV is not set +-CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVDEV is not set + # CONFIG_INPUT_EVBUG is not set + + # + # Input Device Drivers + # +-# CONFIG_INPUT_KEYBOARD is not set ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ATKBD is not set ++# 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_5x5_KEYBOARD_JZ is not set ++CONFIG_KEYBOARD_GPIO=y + # CONFIG_INPUT_MOUSE is not set + # CONFIG_INPUT_JOYSTICK is not set + # CONFIG_INPUT_TABLET is not set +@@ -628,11 +576,7 @@ CONFIG_INPUT_EVDEV=y + # + # 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_SERIO is not set + # CONFIG_GAMEPORT is not set + + # +@@ -724,33 +668,7 @@ CONFIG_SSB_POSSIBLE=y + # + # 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_VIDEO_DEV is not set + # CONFIG_DVB_CORE is not set + # CONFIG_DAB is not set + +@@ -819,7 +737,7 @@ CONFIG_JZLCD_FOXCONN_PT035TN01=y + # 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_DETECT_PRIMARY=y + # CONFIG_FRAMEBUFFER_CONSOLE_CURSOR_FLASH is not set + # CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set + CONFIG_FONTS=y +@@ -833,10 +751,7 @@ CONFIG_FONT_8x16=y + # 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 ++# CONFIG_LOGO is not set + + # + # Sound +diff --git a/arch/mips/jz4740/board-pi.c b/arch/mips/jz4740/board-pi.c +index 309ed9f..298d853 100644 +--- a/arch/mips/jz4740/board-pi.c ++++ b/arch/mips/jz4740/board-pi.c +@@ -33,10 +33,10 @@ static void dancing(void) + + count ++; + count &= 1; +- /* if (count) ++ if (count) + __gpio_set_pin(GPIO_LED_EN); + else +- __gpio_clear_pin(GPIO_LED_EN); */ ++ __gpio_clear_pin(GPIO_LED_EN); + } + + static void pi_timer_callback(void) +@@ -65,7 +65,7 @@ static void __init board_gpio_setup(void) + /* + * Initialize MSC pins + */ +- /* __gpio_as_msc(); */ ++ __gpio_as_msc(); + + /* + * Initialize LCD pins +diff --git a/drivers/char/jzchar/poweroff.c b/drivers/char/jzchar/poweroff.c +index 66b9f5c..8651f89 100644 +--- a/drivers/char/jzchar/poweroff.c ++++ b/drivers/char/jzchar/poweroff.c +@@ -78,8 +78,6 @@ do { \ + 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); \ +diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c +index 0fe5b36..4c9c1d9 100644 +--- a/drivers/mtd/nand/jz4740_nand.c ++++ b/drivers/mtd/nand/jz4740_nand.c +@@ -153,7 +153,6 @@ static int partition_reserved_badblocks[] = { + 20}; /* reserved blocks of mtd5 */ + #endif /* CONFIG_JZ4740_PI */ + +- + #ifdef CONFIG_JZ4740_LEO + static struct mtd_partition partition_info[] = { + { name: "NAND BOOT partition", +diff --git a/drivers/video/jzlcd.c b/drivers/video/jzlcd.c +index 93b9e6f..50d91f3 100644 +--- a/drivers/video/jzlcd.c ++++ b/drivers/video/jzlcd.c +@@ -1501,7 +1501,7 @@ static int __init jzfb_init(void) + } + + __lcd_enable_ofu_intr(); /* enable OutFifo underrun */ +-// __lcd_enable_ifu0_intr(); /* needn't enable InFifo underrun */ ++ /* __lcd_enable_ifu0_intr(); */ /* needn't enable InFifo underrun */ + + #if defined(CONFIG_JZLCD_FRAMEBUFFER_ROTATE_SUPPORT) + jzfb_rotate_change(rotate_angle); +@@ -1526,8 +1526,7 @@ static int __init jzfb_init(void) + cfb->pm->data = cfb; + #endif + +- __lcd_display_on(); +- ++ __lcd_display_off(); + return 0; + + failed: +@@ -1563,8 +1562,8 @@ 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(); ++ /* driver_unregister(&jzfb_driver); */ ++ /* jzfb_remove(); */ + } + + module_init(jzfb_init); +diff --git a/drivers/video/jzlcd.h b/drivers/video/jzlcd.h +index d441d97..352506d 100644 +--- a/drivers/video/jzlcd.h ++++ b/drivers/video/jzlcd.h +@@ -663,21 +663,21 @@ do { \ + #endif /* CONFIG_JZ4730_PMP */ + + #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 ++#if defined(CONFIG_JZ4740_PAVO) || defined(CONFIG_JZ4740_LYRA) || defined(CONFIG_JZ4740_PI) ++#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); \ ++ __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); \ ++ __gpio_as_output(GPIO_PWM); \ ++ __gpio_clear_pin(GPIO_PWM); \ + } while (0) + + #elif defined(CONFIG_JZ4720_VIRGO) +@@ -712,31 +712,10 @@ do { \ + + #define __lcd_close_backlight() \ + do { \ +-__gpio_as_output(GPIO_PWM); \ +-__gpio_clear_pin(GPIO_PWM); \ +-} while (0) +- +-#elif defined(CONFIG_JZ4740_PI) +-#define GPIO_PWM 123 /* GP_D27 */ +-#define PWM_CHN 4 /* pwm channel */ +-#define PWM_FULL 101 +-#define __lcd_set_backlight_level(n)\ +-do { \ +-__gpio_as_output(32*3+27); \ +-__gpio_set_pin(32*3+27); \ ++ __gpio_as_output(GPIO_PWM); \ ++ __gpio_clear_pin(GPIO_PWM); \ + } while (0) + +-#define __lcd_close_backlight() \ +-do { \ +-__gpio_as_output(GPIO_PWM); \ +-__gpio_clear_pin(GPIO_PWM); \ +-} while (0) +-#define __lcd_display_pin_init() \ +-do { \ +- __gpio_as_output(GPIO_DISP_OFF_N); \ +- __cpm_start_tcu(); \ +- __lcd_special_pin_init(); \ +-} while (0) /* CONFIG_MIPS_JZ4740_PI) */ + #else + #define __lcd_set_backlight_level(n) + #define __lcd_close_backlight() +@@ -749,12 +728,12 @@ do { \ + __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); \ ++ __lcd_set_backlight_level(20); \ + } while (0) + + #define __lcd_display_off() \ +diff --git a/include/asm-mips/mach-jz4740/board-pi.h b/include/asm-mips/mach-jz4740/board-pi.h +index 54a7c95..afde2ec 100644 +--- a/include/asm-mips/mach-jz4740/board-pi.h ++++ b/include/asm-mips/mach-jz4740/board-pi.h +@@ -1,13 +1,13 @@ + /* + * linux/include/asm-mips/mach-jz4740/board-pi.h + * +- * JZ4730-based PI ver 2.x definition. ++ * JZ4730-based PI definition. + * + * Copyright (c) 2009 PI. + * Author: xiangfu + * + * 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 ++ * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +@@ -23,16 +23,31 @@ + /*====================================================================== + * GPIO + */ +-#define GPIO_SD_VCC_EN_N 98 /* GPD2 */ +-#define GPIO_SD_CD_N 96 /* GPD0 */ +-#define GPIO_SD_WP 112 /* GPD16 */ +-#define GPIO_USB_DETE 124 /* GPD28 */ +-#define GPIO_DISP_OFF_N 117 /* GPD21 */ +-#define GPIO_LED_EN 124 +-#define GPIO_DC_DETE_N 100 +-#define GPIO_CHARG_STAT_N 91 /* GPC27 */ ++#define GPIO_DC_DETE_N (2 * 32 + 26) ++#define GPIO_CHARG_STAT_N (2 * 32 + 27) ++#define GPIO_LED_EN (2 * 32 + 28) + ++#define GPIO_LCD_CS (2 * 32 + 21) ++#define GPIO_DISP_OFF_N (3 * 32 + 21) ++#define GPIO_PWM (3 * 32 + 27) ++ ++#define GPIO_AMP_EN (3 * 32 + 4) ++ ++#define GPIO_SD_CD_N (3 * 32 + 0) ++#define GPIO_SD_VCC_EN_N (3 * 32 + 2) ++#define GPIO_SD_WP (3 * 32 + 16) /* SD Card wirte protect */ ++ ++#define GPIO_USB_DETE (3 * 32 + 28) ++#define GPIO_BUZZ_PWM (3 * 32 + 27) + #define GPIO_UDC_HOTPLUG GPIO_USB_DETE ++ ++#define GPIO_AUDIO_POP (1 * 32 + 29) ++#define GPIO_COB_TEST (1 * 32 + 30) ++ ++#define GPIO_KEYOUT_BASE (2 * 32 + 10) ++#define GPIO_KEYIN_BASE (3 * 32 + 18) ++#define GPIO_KEYIN_8 (3 * 32 + 26) ++ + /*====================================================================== + * MMC/SD + */ +diff --git a/include/asm-mips/mach-jz4740/jz4740.h b/include/asm-mips/mach-jz4740/jz4740.h +index b2c3872..4787814 100644 +--- a/include/asm-mips/mach-jz4740/jz4740.h ++++ b/include/asm-mips/mach-jz4740/jz4740.h +@@ -49,7 +49,6 @@ + + /* Add other platform definition here ... */ + +- + /*------------------------------------------------------------------ + * Follows are related to platform definitions + */ +diff --git a/include/asm-mips/mach-jz4740/serial.h b/include/asm-mips/mach-jz4740/serial.h +index c4819b9..534234a 100644 +--- a/include/asm-mips/mach-jz4740/serial.h ++++ b/include/asm-mips/mach-jz4740/serial.h +@@ -23,8 +23,11 @@ + #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 }, ++ { .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__ */ +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0007-already-init-in-u-boot.patch b/target/linux/xburst/patches-2.6.24/0007-already-init-in-u-boot.patch new file mode 100644 index 000000000..ad7e2a874 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0007-already-init-in-u-boot.patch @@ -0,0 +1,95 @@ +From ea98a48a0bde70f525bd9b195cd68a73f2636203 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Wed, 22 Apr 2009 15:09:55 +0800 +Subject: [PATCH] already init in u-boot + +--- + arch/mips/jz4740/board-pi.c | 35 +++++++++++++++++++++-------------- + drivers/mmc/host/jz_mmc.h | 3 --- + 2 files changed, 21 insertions(+), 17 deletions(-) + +diff --git a/arch/mips/jz4740/board-pi.c b/arch/mips/jz4740/board-pi.c +index 298d853..05e577e 100644 +--- a/arch/mips/jz4740/board-pi.c ++++ b/arch/mips/jz4740/board-pi.c +@@ -65,42 +65,49 @@ static void __init board_gpio_setup(void) + /* + * Initialize MSC pins + */ +- __gpio_as_msc(); ++ /* __gpio_as_msc(); */ ++ ++ /* ++ * Initialize UART0 pins ++ */ ++ /* __gpio_as_uart0(); */ + + /* + * Initialize LCD pins + */ +- __gpio_as_lcd_18bit(); ++ /* __gpio_as_lcd_18bit(); */ + + /* + * Initialize SSI pins + */ +- __gpio_as_ssi(); ++ /* __gpio_as_ssi(); */ + + /* + * Initialize I2C pins + */ +- __gpio_as_i2c(); ++ /* __gpio_as_i2c(); */ + + /* + * Initialize Other pins + */ +- __gpio_as_output(GPIO_SD_VCC_EN_N); +- __gpio_clear_pin(GPIO_SD_VCC_EN_N); ++ /* __gpio_as_output(GPIO_SD_VCC_EN_N); */ ++ /* __gpio_clear_pin(GPIO_SD_VCC_EN_N); */ ++ /* __gpio_set_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_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_SD_WP); +- __gpio_disable_pull(GPIO_SD_WP); ++ /* __gpio_as_output(GPIO_LED_EN); */ ++ ++ /* __gpio_as_input(GPIO_USB_DETE); */ ++ /* __gpio_as_output(GPIO_DISP_OFF_N); */ + + __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) +diff --git a/drivers/mmc/host/jz_mmc.h b/drivers/mmc/host/jz_mmc.h +index c733529..0bdf971 100644 +--- a/drivers/mmc/host/jz_mmc.h ++++ b/drivers/mmc/host/jz_mmc.h +@@ -17,9 +17,6 @@ typedef struct jzsoc_dma_desc { + volatile u32 dcmd; /* DCMD value for the current transfer */ + } jzsoc_dma_desc; + +- +- +- + #include + + struct device; +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0008-add-build-script.patch b/target/linux/xburst/patches-2.6.24/0008-add-build-script.patch new file mode 100644 index 000000000..eef9f24eb --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0008-add-build-script.patch @@ -0,0 +1,67 @@ +From 15ed8daa3f7a56f32d8b9bbce9ec2dab47c79704 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Thu, 23 Apr 2009 08:30:49 +0800 +Subject: [PATCH] add build script + +--- + build | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 48 insertions(+), 0 deletions(-) + create mode 100755 build + +diff --git a/build b/build +new file mode 100755 +index 0000000..7c6180b +--- /dev/null ++++ b/build +@@ -0,0 +1,48 @@ ++#!/bin/sh ++# ++# Kernel building helper script (C)2009 PI ++# xiangfu ++# ++# Licensed under GPLv3 or later ++# ++# ++# you need to run this from the top level source dir, but it creates all ++# object files into a subdir given in the first argument, eg ++# ++# ++# ++# this radically speeds up swapping between build contexts. Note the config ++# for each build lives in the subdir. ++ ++PARALLEL=16 ++ ++if [ -z "$1" ] ; then ++ echo "Specify the build subdir, eg, PI which contains the .config" ++ echo "and will hold the object files" ++ exit 1 ++fi ++ ++mkdir -p $1 ++ ++if [ -z "$CROSS_COMPILE" ]; then ++ export CROSS_COMPILE=/opt/mipseltools-gcc412-glibc261/bin/mipsel-linux- ++fi ++make O=$1 pi_defconfig ++ ++ ++VERSION= ++if [ -d .git ] ; then ++ HEAD=`git show --pretty=oneline | head -n1 | cut -d' ' -f1 | cut -b1-16` ++ BRANCH=`git branch | grep ^\* | cut -d' ' -f2 | sed s/-hist//g` ++ VERSION=-$PRODUCT\_$BRANCH ++fi ++ ++if make -j$PARALLEL O=$1 uImage ; then ++ cp arch/mips/boot/uImage $1/uImage$VERSION-$HEAD ++ cp arch/mips/boot/uImage /home/xiangfu/virtual_share/usbboot/usbboot1.4a-tools/ ++ exit 0 ++fi ++ ++exit 1 ++ ++ +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0009-format-the-build-shell-script.patch b/target/linux/xburst/patches-2.6.24/0009-format-the-build-shell-script.patch new file mode 100644 index 000000000..7bb261146 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0009-format-the-build-shell-script.patch @@ -0,0 +1,42 @@ +From 6bd871f2f9be7d93524ed031b30af65b8f7ccd61 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Thu, 23 Apr 2009 13:29:16 +0800 +Subject: [PATCH] format the build shell script + +--- + build | 9 ++++----- + 1 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/build b/build +index 7c6180b..3051f47 100755 +--- a/build ++++ b/build +@@ -25,16 +25,16 @@ fi + mkdir -p $1 + + if [ -z "$CROSS_COMPILE" ]; then +- export CROSS_COMPILE=/opt/mipseltools-gcc412-glibc261/bin/mipsel-linux- ++ export CROSS_COMPILE=/opt/mipseltools-gcc412-glibc261/bin/mipsel-linux- + fi + make O=$1 pi_defconfig + + + VERSION= + if [ -d .git ] ; then +- HEAD=`git show --pretty=oneline | head -n1 | cut -d' ' -f1 | cut -b1-16` +- BRANCH=`git branch | grep ^\* | cut -d' ' -f2 | sed s/-hist//g` +- VERSION=-$PRODUCT\_$BRANCH ++ HEAD=`git show --pretty=oneline | head -n1 | cut -d' ' -f1 | cut -b1-16` ++ BRANCH=`git branch | grep ^\* | cut -d' ' -f2 | sed s/-hist//g` ++ VERSION=-$PRODUCT\_$BRANCH + fi + + if make -j$PARALLEL O=$1 uImage ; then +@@ -45,4 +45,3 @@ fi + + exit 1 + +- +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0010-test-email.patch b/target/linux/xburst/patches-2.6.24/0010-test-email.patch new file mode 100644 index 000000000..98513eb09 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0010-test-email.patch @@ -0,0 +1,29 @@ +From 345541b72b832c8b78a32ae08a552b4d778e3550 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Fri, 24 Apr 2009 11:28:38 +0800 +Subject: [PATCH] test email + +--- + build | 3 --- + 1 files changed, 0 insertions(+), 3 deletions(-) + +diff --git a/build b/build +index 3051f47..70d5fcf 100755 +--- a/build ++++ b/build +@@ -5,12 +5,9 @@ + # + # Licensed under GPLv3 or later + # +-# + # you need to run this from the top level source dir, but it creates all + # object files into a subdir given in the first argument, eg + # +-# +-# + # this radically speeds up swapping between build contexts. Note the config + # for each build lives in the subdir. + +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0011-add-vmlinux.lds.h-file.patch b/target/linux/xburst/patches-2.6.24/0011-add-vmlinux.lds.h-file.patch new file mode 100644 index 000000000..1a34b22a1 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0011-add-vmlinux.lds.h-file.patch @@ -0,0 +1,297 @@ +From 4bd575c2847336aa013f1362e5c561209b7b7a71 Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Wed, 24 Jun 2009 20:18:07 +0800 +Subject: [PATCH] add vmlinux.lds.h file + +--- + .gitignore | 1 + + include/asm-generic/vmlinux.lds.h | 265 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 266 insertions(+), 0 deletions(-) + create mode 100644 include/asm-generic/vmlinux.lds.h + +diff --git a/.gitignore b/.gitignore +index a79dfeb..7970e70 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -25,6 +25,7 @@ tags + TAGS + vmlinux* + !vmlinux.lds.S ++!vmlinux.lds.h + System.map + Module.symvers + !.gitignore +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +new file mode 100644 +index 0000000..9f584cc +--- /dev/null ++++ b/include/asm-generic/vmlinux.lds.h +@@ -0,0 +1,265 @@ ++#ifndef LOAD_OFFSET ++#define LOAD_OFFSET 0 ++#endif ++ ++#ifndef VMLINUX_SYMBOL ++#define VMLINUX_SYMBOL(_sym_) _sym_ ++#endif ++ ++/* Align . to a 8 byte boundary equals to maximum function alignment. */ ++#define ALIGN_FUNCTION() . = ALIGN(8) ++ ++/* .data section */ ++#define DATA_DATA \ ++ *(.data) \ ++ *(.data.init.refok) \ ++ . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__start___markers) = .; \ ++ *(__markers) \ ++ VMLINUX_SYMBOL(__stop___markers) = .; ++ ++#define RO_DATA(align) \ ++ . = ALIGN((align)); \ ++ .rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_rodata) = .; \ ++ *(.rodata) *(.rodata.*) \ ++ *(__vermagic) /* Kernel version magic */ \ ++ *(__markers_strings) /* Markers: strings */ \ ++ } \ ++ \ ++ .rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \ ++ *(.rodata1) \ ++ } \ ++ \ ++ /* PCI quirks */ \ ++ .pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \ ++ *(.pci_fixup_early) \ ++ VMLINUX_SYMBOL(__end_pci_fixups_early) = .; \ ++ VMLINUX_SYMBOL(__start_pci_fixups_header) = .; \ ++ *(.pci_fixup_header) \ ++ VMLINUX_SYMBOL(__end_pci_fixups_header) = .; \ ++ VMLINUX_SYMBOL(__start_pci_fixups_final) = .; \ ++ *(.pci_fixup_final) \ ++ VMLINUX_SYMBOL(__end_pci_fixups_final) = .; \ ++ VMLINUX_SYMBOL(__start_pci_fixups_enable) = .; \ ++ *(.pci_fixup_enable) \ ++ VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \ ++ VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \ ++ *(.pci_fixup_resume) \ ++ VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \ ++ } \ ++ \ ++ /* RapidIO route ops */ \ ++ .rio_route : AT(ADDR(.rio_route) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_rio_route_ops) = .; \ ++ *(.rio_route_ops) \ ++ VMLINUX_SYMBOL(__end_rio_route_ops) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: Normal symbols */ \ ++ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___ksymtab) = .; \ ++ *(__ksymtab) \ ++ VMLINUX_SYMBOL(__stop___ksymtab) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-only symbols */ \ ++ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___ksymtab_gpl) = .; \ ++ *(__ksymtab_gpl) \ ++ VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: Normal unused symbols */ \ ++ __ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___ksymtab_unused) = .; \ ++ *(__ksymtab_unused) \ ++ VMLINUX_SYMBOL(__stop___ksymtab_unused) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-only unused symbols */ \ ++ __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .; \ ++ *(__ksymtab_unused_gpl) \ ++ VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-future-only symbols */ \ ++ __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .; \ ++ *(__ksymtab_gpl_future) \ ++ VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: Normal symbols */ \ ++ __kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___kcrctab) = .; \ ++ *(__kcrctab) \ ++ VMLINUX_SYMBOL(__stop___kcrctab) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-only symbols */ \ ++ __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___kcrctab_gpl) = .; \ ++ *(__kcrctab_gpl) \ ++ VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: Normal unused symbols */ \ ++ __kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___kcrctab_unused) = .; \ ++ *(__kcrctab_unused) \ ++ VMLINUX_SYMBOL(__stop___kcrctab_unused) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-only unused symbols */ \ ++ __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .; \ ++ *(__kcrctab_unused_gpl) \ ++ VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: GPL-future-only symbols */ \ ++ __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .; \ ++ *(__kcrctab_gpl_future) \ ++ VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .; \ ++ } \ ++ \ ++ /* Kernel symbol table: strings */ \ ++ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ ++ *(__ksymtab_strings) \ ++ } \ ++ \ ++ /* Built-in module parameters. */ \ ++ __param : AT(ADDR(__param) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___param) = .; \ ++ *(__param) \ ++ VMLINUX_SYMBOL(__stop___param) = .; \ ++ VMLINUX_SYMBOL(__end_rodata) = .; \ ++ } \ ++ \ ++ . = ALIGN((align)); ++ ++/* RODATA provided for backward compatibility. ++ * All archs are supposed to use RO_DATA() */ ++#define RODATA RO_DATA(4096) ++ ++#define SECURITY_INIT \ ++ .security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__security_initcall_start) = .; \ ++ *(.security_initcall.init) \ ++ VMLINUX_SYMBOL(__security_initcall_end) = .; \ ++ } ++ ++/* .text section. Map to function alignment to avoid address changes ++ * during second ld run in second ld pass when generating System.map */ ++#define TEXT_TEXT \ ++ ALIGN_FUNCTION(); \ ++ *(.text) \ ++ *(.text.init.refok) \ ++ *(.exit.text.refok) ++ ++/* sched.text is aling to function alignment to secure we have same ++ * address even at second ld pass when generating System.map */ ++#define SCHED_TEXT \ ++ ALIGN_FUNCTION(); \ ++ VMLINUX_SYMBOL(__sched_text_start) = .; \ ++ *(.sched.text) \ ++ VMLINUX_SYMBOL(__sched_text_end) = .; ++ ++/* spinlock.text is aling to function alignment to secure we have same ++ * address even at second ld pass when generating System.map */ ++#define LOCK_TEXT \ ++ ALIGN_FUNCTION(); \ ++ VMLINUX_SYMBOL(__lock_text_start) = .; \ ++ *(.spinlock.text) \ ++ VMLINUX_SYMBOL(__lock_text_end) = .; ++ ++#define KPROBES_TEXT \ ++ ALIGN_FUNCTION(); \ ++ VMLINUX_SYMBOL(__kprobes_text_start) = .; \ ++ *(.kprobes.text) \ ++ VMLINUX_SYMBOL(__kprobes_text_end) = .; ++ ++ /* DWARF debug sections. ++ Symbols in the DWARF debugging sections are relative to ++ the beginning of the section so we begin them at 0. */ ++#define DWARF_DEBUG \ ++ /* DWARF 1 */ \ ++ .debug 0 : { *(.debug) } \ ++ .line 0 : { *(.line) } \ ++ /* GNU DWARF 1 extensions */ \ ++ .debug_srcinfo 0 : { *(.debug_srcinfo) } \ ++ .debug_sfnames 0 : { *(.debug_sfnames) } \ ++ /* DWARF 1.1 and DWARF 2 */ \ ++ .debug_aranges 0 : { *(.debug_aranges) } \ ++ .debug_pubnames 0 : { *(.debug_pubnames) } \ ++ /* DWARF 2 */ \ ++ .debug_info 0 : { *(.debug_info \ ++ .gnu.linkonce.wi.*) } \ ++ .debug_abbrev 0 : { *(.debug_abbrev) } \ ++ .debug_line 0 : { *(.debug_line) } \ ++ .debug_frame 0 : { *(.debug_frame) } \ ++ .debug_str 0 : { *(.debug_str) } \ ++ .debug_loc 0 : { *(.debug_loc) } \ ++ .debug_macinfo 0 : { *(.debug_macinfo) } \ ++ /* SGI/MIPS DWARF 2 extensions */ \ ++ .debug_weaknames 0 : { *(.debug_weaknames) } \ ++ .debug_funcnames 0 : { *(.debug_funcnames) } \ ++ .debug_typenames 0 : { *(.debug_typenames) } \ ++ .debug_varnames 0 : { *(.debug_varnames) } \ ++ ++ /* Stabs debugging sections. */ ++#define STABS_DEBUG \ ++ .stab 0 : { *(.stab) } \ ++ .stabstr 0 : { *(.stabstr) } \ ++ .stab.excl 0 : { *(.stab.excl) } \ ++ .stab.exclstr 0 : { *(.stab.exclstr) } \ ++ .stab.index 0 : { *(.stab.index) } \ ++ .stab.indexstr 0 : { *(.stab.indexstr) } \ ++ .comment 0 : { *(.comment) } ++ ++#define BUG_TABLE \ ++ . = ALIGN(8); \ ++ __bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \ ++ __start___bug_table = .; \ ++ *(__bug_table) \ ++ __stop___bug_table = .; \ ++ } ++ ++#define NOTES \ ++ .notes : AT(ADDR(.notes) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_notes) = .; \ ++ *(.note.*) \ ++ VMLINUX_SYMBOL(__stop_notes) = .; \ ++ } ++ ++#define INITCALLS \ ++ *(.initcall0.init) \ ++ *(.initcall0s.init) \ ++ *(.initcall1.init) \ ++ *(.initcall1s.init) \ ++ *(.initcall2.init) \ ++ *(.initcall2s.init) \ ++ *(.initcall3.init) \ ++ *(.initcall3s.init) \ ++ *(.initcall4.init) \ ++ *(.initcall4s.init) \ ++ *(.initcall5.init) \ ++ *(.initcall5s.init) \ ++ *(.initcallrootfs.init) \ ++ *(.initcall6.init) \ ++ *(.initcall6s.init) \ ++ *(.initcall7.init) \ ++ *(.initcall7s.init) ++ ++#define PERCPU(align) \ ++ . = ALIGN(align); \ ++ __per_cpu_start = .; \ ++ .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { \ ++ *(.data.percpu) \ ++ *(.data.percpu.shared_aligned) \ ++ } \ ++ __per_cpu_end = .; +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0012-fix-make-zImage-erro.path.patch b/target/linux/xburst/patches-2.6.24/0012-fix-make-zImage-erro.path.patch new file mode 100644 index 000000000..f06eacd2f --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0012-fix-make-zImage-erro.path.patch @@ -0,0 +1,39 @@ +From b70f44257dc5d9560eb8fec6ad48a2a894ef044a Mon Sep 17 00:00:00 2001 +From: xiangfu +Date: Wed, 1 Jul 2009 11:29:24 +0800 +Subject: [PATCH] fix-make-zImage-erro.path + +--- + Makefile | 2 +- + arch/mips/Makefile | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Makefile b/Makefile +index 9f73a2b..38800ef 100644 +--- a/Makefile ++++ b/Makefile +@@ -191,7 +191,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + + ARCH ?= mips +-CROSS_COMPILE ?= mipsel-linux- ++CROSS_COMPILE ?= mipsel-openwrt-linux- + + # Architecture as present in compile.h + UTS_MACHINE := $(ARCH) +diff --git a/arch/mips/Makefile b/arch/mips/Makefile +index acad988..ce7dc4a 100644 +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -170,7 +170,7 @@ load-$(CONFIG_SOC_JZ4730) += 0xffffffff80010000 + # + + core-$(CONFIG_SOC_JZ4740) += arch/mips/jz4740/ +-cflags-$(CONFIG_SOC_JZ4740) += -Iinclude/asm-mips/mach-jz4740 ++cflags-$(CONFIG_SOC_JZ4740) += -Iinclude/asm-mips/mach-jz4740 -Iinclude + load-$(CONFIG_SOC_JZ4740) += 0xffffffff80010000 + + # +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0013-fix-mmc-driver.patch b/target/linux/xburst/patches-2.6.24/0013-fix-mmc-driver.patch new file mode 100644 index 000000000..77b0b2a59 --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0013-fix-mmc-driver.patch @@ -0,0 +1,190 @@ +From 7d5623d6b172373f943ffe6d976e2830a2081050 Mon Sep 17 00:00:00 2001 +From: Xiangfu Liu +Date: Wed, 15 Jul 2009 15:57:56 +0800 +Subject: [PATCH] fix mmc driver + +--- + arch/mips/jz4740/board-pi.c | 45 ++++++++++++++++++++----------- + drivers/mmc/host/jz_mmc.c | 13 +++++--- + drivers/mtd/nand/jz4740_nand.c | 17 +++++------ + include/asm-mips/mach-jz4740/board-pi.h | 6 ++-- + 4 files changed, 48 insertions(+), 33 deletions(-) + +diff --git a/arch/mips/jz4740/board-pi.c b/arch/mips/jz4740/board-pi.c +index 05e577e..7123352 100644 +--- a/arch/mips/jz4740/board-pi.c ++++ b/arch/mips/jz4740/board-pi.c +@@ -61,12 +61,6 @@ 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 UART0 pins + */ +@@ -88,26 +82,45 @@ static void __init board_gpio_setup(void) + /* __gpio_as_i2c(); */ + + /* ++ * Initialize MSC pins ++ */ ++ /* __gpio_as_msc(); */ ++ ++ /* + * Initialize Other pins + */ +- /* __gpio_as_output(GPIO_SD_VCC_EN_N); */ +- /* __gpio_clear_pin(GPIO_SD_VCC_EN_N); */ +- /* __gpio_set_pin(GPIO_SD_VCC_EN_N); */ + +- /* __gpio_as_input(GPIO_SD_CD_N); */ +- /* __gpio_disable_pull(GPIO_SD_CD_N); */ ++ /* unsigned int i; ++ for (i = 0; i < 8; i++) { ++ __gpio_as_output(GPIO_KEYOUT_BASE + i); ++ __gpio_set_pin(GPIO_KEYOUT_BASE + i); ++ } + +- /* __gpio_as_input(GPIO_SD_WP); */ +- /* __gpio_disable_pull(GPIO_SD_WP); */ ++ for (i = 0; i < 7; i++){ ++ __gpio_as_input(GPIO_KEYIN_BASE + i); ++ __gpio_enable_pull(GPIO_KEYIN_BASE + i); ++ } ++ __gpio_as_input( GPIO_KEYIN_8 ); ++ __gpio_enable_pull( GPIO_KEYIN_8 ); ++ */ + +- /* __gpio_as_output(GPIO_LED_EN); */ ++ __gpio_as_output(GPIO_SD_VCC_EN_N); ++ __gpio_disable_pull(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_USB_DETE); */ +- /* __gpio_as_output(GPIO_DISP_OFF_N); */ + + __gpio_as_input(GPIO_DC_DETE_N); + __gpio_as_input(GPIO_CHARG_STAT_N); + ++ /* __gpio_as_output(GPIO_DISP_OFF_N); */ ++ /* __gpio_as_input(GPIO_USB_DETE); */ ++ /* __gpio_as_output(GPIO_LED_EN); */ + } + + void __init jz_board_setup(void) +diff --git a/drivers/mmc/host/jz_mmc.c b/drivers/mmc/host/jz_mmc.c +index 29ab9bb..de52028 100644 +--- a/drivers/mmc/host/jz_mmc.c ++++ b/drivers/mmc/host/jz_mmc.c +@@ -843,9 +843,9 @@ static int jz_mmc_probe(struct platform_device *pdev) + 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, ++ /* Request card detect interrupt */ ++ retval = request_irq(19 /*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"); +@@ -889,10 +889,13 @@ static int jz_mmc_probe(struct platform_device *pdev) + #endif + return 0; + +-err1:free_irq(IRQ_MSC, &host); ++err1: ++ free_irq(IRQ_MSC, &host); + #ifdef USE_DMA +- err2:jz_free_dma(rxdmachan); +- err3:jz_free_dma(txdmachan); ++ err2: ++ jz_free_dma(rxdmachan); ++ err3: ++ jz_free_dma(txdmachan); + #endif + out: + if (host) { +diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c +index 4c9c1d9..b36ed44 100644 +--- a/drivers/mtd/nand/jz4740_nand.c ++++ b/drivers/mtd/nand/jz4740_nand.c +@@ -118,23 +118,22 @@ static struct mtd_partition partition_info[] = { + use_planes: 0 }, + { name: "NAND ROOTFS partition", + offset: 8 * 0x100000, +- size: 120 * 0x100000, ++ size: 504 * 0x100000, + use_planes: 0 }, + { name: "NAND DATA1 partition", +- offset: 128 * 0x100000, +- size: 128 * 0x100000, ++ offset: 512 * 0x100000, ++ size: 512 * 0x100000, + use_planes: 1 }, + { name: "NAND DATA2 partition", +- offset: 256 * 0x100000, +- size: 256 * 0x100000, ++ offset: 1024 * 0x100000, ++ size: 512 * 0x100000, + use_planes: 1 }, + { name: "NAND VFAT partition", +- offset: 512 * 0x100000, ++ offset: (1024 + 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. + * +@@ -149,8 +148,8 @@ static int partition_reserved_badblocks[] = { + 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 */ ++ 10, /* reserved blocks of mtd4 */ ++ 10}; /* reserved blocks of mtd5 */ + #endif /* CONFIG_JZ4740_PI */ + + #ifdef CONFIG_JZ4740_LEO +diff --git a/include/asm-mips/mach-jz4740/board-pi.h b/include/asm-mips/mach-jz4740/board-pi.h +index afde2ec..152147a 100644 +--- a/include/asm-mips/mach-jz4740/board-pi.h ++++ b/include/asm-mips/mach-jz4740/board-pi.h +@@ -3,8 +3,8 @@ + * + * JZ4730-based PI definition. + * +- * Copyright (c) 2009 PI. +- * Author: xiangfu ++ * Copyright (c) 2009 Qi Hardware inc., ++ * Author: Xiangfu Liu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as +@@ -75,7 +75,7 @@ do { \ + #define __msc_card_detected(s) \ + ({ \ + int detected = 1; \ +- if (__gpio_get_pin(GPIO_SD_CD_N)) \ ++ if (!__gpio_get_pin(GPIO_SD_CD_N)) \ + detected = 0; \ + detected; \ + }) +-- +1.6.0.4 + diff --git a/target/linux/xburst/patches-2.6.24/0014-add-defautl-command.patch b/target/linux/xburst/patches-2.6.24/0014-add-defautl-command.patch new file mode 100644 index 000000000..7846ed00d --- /dev/null +++ b/target/linux/xburst/patches-2.6.24/0014-add-defautl-command.patch @@ -0,0 +1,2451 @@ +From 71f18f8fd448da9bd4b157c2e0e6776368393073 Mon Sep 17 00:00:00 2001 +From: Xiangfu Liu +Date: Sun, 19 Jul 2009 10:14:00 +0800 +Subject: [PATCH] add defautl command + +--- + arch/mips/configs/pi_defconfig | 1212 ----------------------------------- + arch/mips/configs/qi_lb60_defconfig | 1212 +++++++++++++++++++++++++++++++++++ + 2 files changed, 1212 insertions(+), 1212 deletions(-) + delete mode 100644 arch/mips/configs/pi_defconfig + create mode 100644 arch/mips/configs/qi_lb60_defconfig + +diff --git a/arch/mips/configs/pi_defconfig b/arch/mips/configs/pi_defconfig +deleted file mode 100644 +index 8c0019a..0000000 +--- a/arch/mips/configs/pi_defconfig ++++ /dev/null +@@ -1,1212 +0,0 @@ +-# +-# Automatically generated make config: don't edit +-# Linux kernel version: 2.6.24.3 +-# Wed Apr 22 12:15:04 2009 +-# +-CONFIG_MIPS=y +- +-# +-# Machine selection +-# +-# CONFIG_JZ4730_PMP is not set +-# CONFIG_JZ4740_PAVO is not set +-CONFIG_JZ4740_PI=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_JZ4750D_FUWA1 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_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 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 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=y +-# CONFIG_KEYBOARD_ATKBD is not set +-# 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_5x5_KEYBOARD_JZ is not set +-CONFIG_KEYBOARD_GPIO=y +-# 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 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_JZ_TCSM 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 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 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=y +-# 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=y +-# 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 is not set +- +-# +-# 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_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_JZ4750 is not set +-# 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_UDC_USE_LB_CACHE=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_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_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 --git a/arch/mips/configs/qi_lb60_defconfig b/arch/mips/configs/qi_lb60_defconfig +new file mode 100644 +index 0000000..ba77299 +--- /dev/null ++++ b/arch/mips/configs/qi_lb60_defconfig +@@ -0,0 +1,1212 @@ ++# ++# Automatically generated make config: don't edit ++# Linux kernel version: 2.6.24.3 ++# Wed Jul 15 15:34:05 2009 ++# ++CONFIG_MIPS=y ++ ++# ++# Machine selection ++# ++# CONFIG_JZ4730_PMP is not set ++# CONFIG_JZ4740_PAVO is not set ++CONFIG_JZ4740_PI=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_JZ4750D_FUWA1 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_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 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 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=y ++# CONFIG_KEYBOARD_ATKBD is not set ++# 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_5x5_KEYBOARD_JZ is not set ++CONFIG_KEYBOARD_GPIO=y ++# 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 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_JZ_TCSM 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 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 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=y ++# 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=y ++# 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 is not set ++ ++# ++# 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_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_JZ4750 is not set ++# 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_UDC_USE_LB_CACHE=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_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_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="mem=32M console=ttyS0,57600n8 root=/dev/mmcblk0p2 rootfstype=ext2 rw" ++ ++# ++# 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 +-- +1.6.0.4 +