From a5287abe398b74df9040d6dcd8356cf53a174e84 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 4 Feb 2002 18:38:57 -0800 Subject: [PATCH] v2.4.5.2 -> v2.4.5.3 - remember to increment the version number - Chris Mason: reiserfs mark_journal_new and bh leak fix - Richard Gooch: devfs update - Alexander Viro: further FS cleanup (superblock list) - David Woodhouse: MTD update - Kai Germaschewski: ISDN update (stanford checker fixes etc) - Rich Baum: gcc-3.0 warning fixes - Jeff Garzik: network driver updates - Geert Uytterhoeven: m68k fbdev logo merge glitch fix - Andrea Arcangeli: fix signal return path - David Miller: Sparc updates - Johannes Erdfelt: USB update - Carsten Otte, Andries Brouwer: don't clear blk_size unconditionally on partition check - Martin Frey: alpha Sable irq fix - Paul Mackerras: PPC softirq update - Patrick Mochel: PCI power management infrastructure - Robert Siemer: miroSOUND driver update - Neil Brown: knfsd updates, including ability to export ReiserFS filesystems - Trond Myklebust: NFS readdir fixup, don't update atime on client - Andrew Morton: truncate_inode_pages speedup - Paul Menage: make inode quota count all inodes.. --- CREDITS | 4 +- Documentation/Configure.help | 670 +++++-- Documentation/filesystems/devfs/ChangeLog | 32 + Documentation/filesystems/devfs/README | 255 ++- Documentation/sound/README.OSS | 13 +- MAINTAINERS | 11 +- Makefile | 5 +- arch/alpha/kernel/osf_sys.c | 1 - arch/alpha/kernel/sys_sable.c | 4 +- arch/i386/defconfig | 58 + arch/i386/kernel/entry.S | 17 +- arch/i386/math-emu/fpu_trig.c | 1 + arch/ppc/kernel/entry.S | 22 +- arch/ppc/kernel/ppc_ksyms.c | 3 +- arch/ppc/xmon/xmon.c | 6 +- arch/sparc/kernel/rtrap.S | 7 +- arch/sparc64/kernel/devices.c | 1 + arch/sparc64/kernel/ebus.c | 8 +- arch/sparc64/kernel/pci.c | 69 +- arch/sparc64/kernel/pci_common.c | 62 +- arch/sparc64/kernel/pci_sabre.c | 24 +- arch/sparc64/kernel/power.c | 7 +- arch/sparc64/kernel/rtrap.S | 7 +- arch/sparc64/kernel/time.c | 35 +- drivers/atm/fore200e.c | 1 + drivers/block/ll_rw_blk.c | 35 +- drivers/char/console.c | 2 +- drivers/char/random.c | 4 +- drivers/char/rtc.c | 22 +- drivers/ide/ide-tape.c | 2 +- drivers/isdn/avmb1/b1.c | 107 +- drivers/isdn/avmb1/b1dma.c | 51 +- drivers/isdn/avmb1/b1isa.c | 75 +- drivers/isdn/avmb1/b1pci.c | 103 +- drivers/isdn/avmb1/b1pcmcia.c | 81 +- drivers/isdn/avmb1/c4.c | 105 +- drivers/isdn/avmb1/capicmd.h | 24 +- drivers/isdn/avmb1/capifs.c | 4 +- drivers/isdn/avmb1/capifs.h | 18 +- drivers/isdn/avmb1/capiutil.h | 37 +- drivers/isdn/avmb1/kcapi.c | 120 +- drivers/isdn/avmb1/t1isa.c | 96 +- drivers/isdn/avmb1/t1pci.c | 72 +- drivers/isdn/hisax/callc.c | 8 +- drivers/isdn/hisax/config.c | 56 +- drivers/isdn/hisax/fsm.c | 8 +- drivers/isdn/hisax/gazel.c | 14 +- drivers/isdn/hisax/hisax.h | 14 +- drivers/isdn/hisax/isdnl1.c | 39 +- drivers/isdn/hisax/isdnl2.c | 8 +- drivers/isdn/hisax/isdnl3.c | 8 +- drivers/isdn/hisax/md5sums.asc | 22 +- drivers/isdn/hisax/tei.c | 8 +- drivers/isdn/hysdn/hycapi.c | 6 +- drivers/isdn/hysdn/hysdn_net.c | 7 +- drivers/isdn/hysdn/hysdn_proclog.c | 8 +- drivers/isdn/isdn_ppp.c | 7 +- drivers/media/radio/Config.in | 1 + drivers/media/radio/Makefile | 5 +- .../{radio-miropcm20.c => miropcm20-radio.c} | 46 +- .../{rds-miropcm20.c => miropcm20-rds-core.c} | 93 +- drivers/media/radio/miropcm20-rds-core.h | 19 + drivers/media/radio/miropcm20-rds.c | 140 ++ drivers/media/video/tuner.c | 1 + drivers/mtd/Config.in | 79 +- drivers/mtd/Makefile | 75 +- drivers/mtd/bootldr.c | 149 ++ drivers/mtd/cfi_cmdset_0001.c | 891 --------- drivers/mtd/cfi_cmdset_0002.c | 628 ------ drivers/mtd/cfi_probe.c | 517 ----- drivers/mtd/chips/Config.in | 42 + drivers/mtd/chips/Makefile | 27 + drivers/mtd/chips/amd_flash.c | 1251 ++++++++++++ drivers/mtd/chips/cfi_cmdset_0001.c | 1636 ++++++++++++++++ drivers/mtd/chips/cfi_cmdset_0002.c | 936 +++++++++ drivers/mtd/chips/cfi_jedec.c | 289 +++ drivers/mtd/chips/cfi_probe.c | 671 +++++++ drivers/mtd/chips/chipreg.c | 92 + drivers/mtd/{ => chips}/jedec.c | 271 ++- drivers/mtd/{ => chips}/map_ram.c | 27 +- drivers/mtd/{ => chips}/map_rom.c | 29 +- drivers/mtd/chips/sharp.c | 593 ++++++ drivers/mtd/devices/Config.in | 51 + drivers/mtd/devices/Makefile | 23 + drivers/mtd/{ => devices}/doc1000.c | 0 drivers/mtd/{ => devices}/doc2000.c | 24 +- drivers/mtd/{ => devices}/doc2001.c | 94 +- drivers/mtd/{ => devices}/docecc.c | 0 drivers/mtd/{ => devices}/docprobe.c | 20 +- drivers/mtd/{ => devices}/mtdram.c | 74 +- drivers/mtd/{ => devices}/pmc551.c | 127 +- drivers/mtd/devices/slram.c | 341 ++++ drivers/mtd/ftl.c | 112 +- drivers/mtd/maps/Config.in | 41 + drivers/mtd/maps/Makefile | 29 + drivers/mtd/maps/cfi_flagadm.c | 184 ++ drivers/mtd/maps/cstm_mips_ixx.c | 314 +++ drivers/mtd/maps/dbox2-flash.c | 151 ++ drivers/mtd/maps/dc21285.c | 193 ++ drivers/mtd/maps/elan-104nc.c | 277 +++ drivers/mtd/maps/iq80310.c | 133 ++ drivers/mtd/maps/netsc520.c | 192 ++ drivers/mtd/{ => maps}/nora.c | 8 +- drivers/mtd/maps/ocelot.c | 199 ++ drivers/mtd/{ => maps}/octagon-5066.c | 12 +- drivers/mtd/{ => maps}/physmap.c | 24 +- drivers/mtd/{ => maps}/pnc2000.c | 16 +- drivers/mtd/{ => maps}/rpxlite.c | 10 +- drivers/mtd/maps/sa1100-flash.c | 644 ++++++ drivers/mtd/maps/sbc_gxx.c | 278 +++ drivers/mtd/maps/sc520cdp.c | 348 ++++ drivers/mtd/maps/sun_uflash.c | 224 +++ drivers/mtd/{ => maps}/vmax301.c | 14 +- drivers/mtd/mixmem.c | 151 -- drivers/mtd/mtdblock_ro.c | 289 +++ drivers/mtd/mtdchar.c | 160 +- drivers/mtd/mtdcore.c | 9 +- drivers/mtd/mtdpart.c | 123 +- drivers/mtd/nand/Config.in | 16 + drivers/mtd/nand/Makefile | 14 + drivers/mtd/nand/nand.c | 1369 +++++++++++++ drivers/mtd/nand/nand_ecc.c | 204 ++ drivers/mtd/nand/spia.c | 129 ++ drivers/mtd/{nftl.c => nftlcore.c} | 138 +- drivers/mtd/nftlmount.c | 20 +- drivers/mtd/redboot.c | 150 ++ drivers/mtd/slram.c | 227 --- drivers/net/eepro100.c | 10 +- drivers/net/tokenring/ibmtr.c | 2 +- drivers/net/wan/hdlc.c | 4 +- drivers/net/wan/sdla_fr.c | 3 +- drivers/net/wan/sdla_x25.c | 2 +- drivers/pci/Makefile | 6 +- drivers/pci/pci.c | 227 ++- drivers/pcmcia/pci_socket.c | 6 +- drivers/scsi/NCR53c406a.c | 32 +- drivers/scsi/aic7xxx_old.c | 2 +- drivers/scsi/constants.c | 15 +- drivers/scsi/osst.c | 4 +- drivers/scsi/scsi.c | 2 +- drivers/scsi/scsi_scan.c | 24 +- drivers/scsi/sd.c | 3 +- drivers/scsi/sym53c8xx.c | 1 + drivers/sound/aci.c | 48 +- drivers/sound/aci.h | 33 +- drivers/sound/gus_wave.c | 3 + drivers/sound/pss.c | 1 + drivers/sound/wf_midi.c | 1 + drivers/sound/ymfpci.c | 1 + drivers/usb/acm.c | 3 +- drivers/usb/audio.c | 3 +- drivers/usb/bluetooth.c | 42 +- drivers/usb/dabusb.c | 3 +- drivers/usb/dc2xx.c | 3 +- drivers/usb/dsbr100.c | 3 +- drivers/usb/hid.c | 3 +- drivers/usb/ibmcam.c | 3 +- drivers/usb/mdc800.c | 3 +- drivers/usb/microtek.c | 3 +- drivers/usb/net1080.c | 5 +- drivers/usb/ov511.c | 3 +- drivers/usb/pegasus.c | 3 +- drivers/usb/plusb.c | 3 +- drivers/usb/printer.c | 3 +- drivers/usb/rio500.c | 3 +- drivers/usb/serial/belkin_sa.c | 3 +- drivers/usb/serial/digi_acceleport.c | 3 +- drivers/usb/serial/empeg.c | 3 +- drivers/usb/serial/ftdi_sio.c | 3 +- drivers/usb/serial/io_edgeport.c | 3 +- drivers/usb/serial/keyspan.c | 3 +- drivers/usb/serial/keyspan_pda.c | 3 +- drivers/usb/serial/mct_u232.c | 3 +- drivers/usb/serial/omninet.c | 3 +- drivers/usb/serial/usbserial.c | 3 +- drivers/usb/serial/visor.c | 3 +- drivers/usb/serial/whiteheat.c | 3 +- drivers/usb/storage/debug.c | 2 + drivers/usb/uhci.c | 13 +- drivers/usb/usb-ohci.c | 3 +- drivers/usb/usb-ohci.h | 4 +- drivers/usb/usb-uhci.c | 3 +- drivers/usb/usb.c | 2 +- drivers/usb/usbkbd.c | 3 +- drivers/usb/usbmouse.c | 3 +- drivers/usb/uss720.c | 3 +- drivers/usb/wacom.c | 3 +- drivers/video/creatorfb.c | 20 +- fs/buffer.c | 7 +- fs/devfs/base.c | 270 ++- fs/devfs/util.c | 15 +- fs/dquot.c | 11 +- fs/inode.c | 5 +- fs/namei.c | 14 +- fs/nfs/dir.c | 130 +- fs/nfs/inode.c | 9 +- fs/nfsd/export.c | 3 +- fs/nfsd/nfsfh.c | 99 +- fs/partitions/check.c | 3 +- fs/proc/array.c | 7 +- fs/proc/base.c | 9 +- fs/reiserfs/fix_node.c | 10 +- fs/reiserfs/inode.c | 87 +- fs/reiserfs/journal.c | 1 + fs/reiserfs/stree.c | 34 +- fs/reiserfs/super.c | 3 + fs/super.c | 4 +- fs/sysv/inode.c | 2 +- include/asm-alpha/delay.h | 1 + include/asm-i386/mca_dma.h | 2 +- include/asm-m68k/linux_logo.h | 1735 +++++++++-------- include/asm-ppc/hardirq.h | 5 +- include/asm-ppc/softirq.h | 5 +- include/asm-sparc/hardirq.h | 4 +- include/asm-sparc/linux_logo.h | 2 +- include/asm-sparc/softirq.h | 17 +- include/asm-sparc64/hardirq.h | 4 +- include/asm-sparc64/linux_logo.h | 2 +- include/asm-sparc64/softirq.h | 17 +- include/asm-sparc64/string.h | 3 +- include/linux/coda_cache.h | 2 +- include/linux/devfs_fs_kernel.h | 2 +- include/linux/fs.h | 26 +- include/linux/kernel.h | 1 + include/linux/mtd/cfi.h | 298 ++- include/linux/mtd/cfi_endian.h | 141 ++ include/linux/mtd/doc2000.h | 20 +- include/linux/mtd/flashchip.h | 7 +- include/linux/mtd/map.h | 55 +- include/linux/mtd/mapped.h | 92 - include/linux/mtd/mtd.h | 73 +- include/linux/mtd/nand_ecc.h | 28 + include/linux/mtd/nftl.h | 3 +- include/linux/mtd/partitions.h | 20 +- include/linux/pci.h | 24 +- include/linux/pci_ids.h | 22 +- include/linux/quotaops.h | 1 - include/linux/reiserfs_fs.h | 4 + include/linux/swap.h | 10 + kernel/timer.c | 2 +- mm/filemap.c | 32 +- mm/vmscan.c | 88 +- net/khttpd/security.h | 2 +- 243 files changed, 16182 insertions(+), 6063 deletions(-) rename drivers/media/radio/{radio-miropcm20.c => miropcm20-radio.c} (89%) rename drivers/media/radio/{rds-miropcm20.c => miropcm20-rds-core.c} (70%) create mode 100644 drivers/media/radio/miropcm20-rds-core.h create mode 100644 drivers/media/radio/miropcm20-rds.c create mode 100644 drivers/mtd/bootldr.c delete mode 100644 drivers/mtd/cfi_cmdset_0001.c delete mode 100644 drivers/mtd/cfi_cmdset_0002.c delete mode 100644 drivers/mtd/cfi_probe.c create mode 100644 drivers/mtd/chips/Config.in create mode 100644 drivers/mtd/chips/Makefile create mode 100644 drivers/mtd/chips/amd_flash.c create mode 100644 drivers/mtd/chips/cfi_cmdset_0001.c create mode 100644 drivers/mtd/chips/cfi_cmdset_0002.c create mode 100644 drivers/mtd/chips/cfi_jedec.c create mode 100644 drivers/mtd/chips/cfi_probe.c create mode 100644 drivers/mtd/chips/chipreg.c rename drivers/mtd/{ => chips}/jedec.c (76%) rename drivers/mtd/{ => chips}/map_ram.c (80%) rename drivers/mtd/{ => chips}/map_rom.c (68%) create mode 100644 drivers/mtd/chips/sharp.c create mode 100644 drivers/mtd/devices/Config.in create mode 100644 drivers/mtd/devices/Makefile rename drivers/mtd/{ => devices}/doc1000.c (100%) rename drivers/mtd/{ => devices}/doc2000.c (97%) rename drivers/mtd/{ => devices}/doc2001.c (92%) rename drivers/mtd/{ => devices}/docecc.c (100%) rename drivers/mtd/{ => devices}/docprobe.c (94%) rename drivers/mtd/{ => devices}/mtdram.c (61%) rename drivers/mtd/{ => devices}/pmc551.c (89%) create mode 100644 drivers/mtd/devices/slram.c create mode 100644 drivers/mtd/maps/Config.in create mode 100644 drivers/mtd/maps/Makefile create mode 100644 drivers/mtd/maps/cfi_flagadm.c create mode 100644 drivers/mtd/maps/cstm_mips_ixx.c create mode 100644 drivers/mtd/maps/dbox2-flash.c create mode 100644 drivers/mtd/maps/dc21285.c create mode 100644 drivers/mtd/maps/elan-104nc.c create mode 100644 drivers/mtd/maps/iq80310.c create mode 100644 drivers/mtd/maps/netsc520.c rename drivers/mtd/{ => maps}/nora.c (96%) create mode 100644 drivers/mtd/maps/ocelot.c rename drivers/mtd/{ => maps}/octagon-5066.c (95%) rename drivers/mtd/{ => maps}/physmap.c (84%) rename drivers/mtd/{ => maps}/pnc2000.c (88%) rename drivers/mtd/{ => maps}/rpxlite.c (92%) create mode 100644 drivers/mtd/maps/sa1100-flash.c create mode 100644 drivers/mtd/maps/sbc_gxx.c create mode 100644 drivers/mtd/maps/sc520cdp.c create mode 100644 drivers/mtd/maps/sun_uflash.c rename drivers/mtd/{ => maps}/vmax301.c (94%) delete mode 100644 drivers/mtd/mixmem.c create mode 100644 drivers/mtd/mtdblock_ro.c create mode 100644 drivers/mtd/nand/Config.in create mode 100644 drivers/mtd/nand/Makefile create mode 100644 drivers/mtd/nand/nand.c create mode 100644 drivers/mtd/nand/nand_ecc.c create mode 100644 drivers/mtd/nand/spia.c rename drivers/mtd/{nftl.c => nftlcore.c} (88%) create mode 100644 drivers/mtd/redboot.c delete mode 100644 drivers/mtd/slram.c create mode 100644 include/linux/mtd/cfi_endian.h delete mode 100644 include/linux/mtd/mapped.h create mode 100644 include/linux/mtd/nand_ecc.h diff --git a/CREDITS b/CREDITS index feace6e10d91..5fde33453675 100644 --- a/CREDITS +++ b/CREDITS @@ -2710,9 +2710,7 @@ S: USA N: Marcelo W. Tosatti E: marcelo@conectiva.com.br W: http://bazar.conectiva.com.br/~marcelo/ -D: Miscellaneous kernel hacker -D: Cyclom 2X driver, drbd hacker -D: linuxconf apache & proftpd module maintainer +D: Miscellaneous kernel hacker (mostly VM/MM work) S: Conectiva S.A. S: R. Tocantins, 89 - Cristo Rei S: 80050-430 - Curitiba - Paraná diff --git a/Documentation/Configure.help b/Documentation/Configure.help index ab3d5c49bf1a..c541bb0e39af 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -10182,91 +10182,150 @@ CONFIG_QUOTA Memory Technology Device (MTD) support CONFIG_MTD Memory Technology Devices are flash, RAM and similar chips, often - used for solid state file systems on embedded devices. This option + used for solid state filesystems on embedded devices. This option will provide the generic support for MTD drivers to register themselves with the kernel and for potential users of MTD devices to enumerate the devices which are present and obtain a handle on them. It will also allow you to select individual drivers for - particular hardware and users of MTD device. If unsure, say N. + particular hardware and users of MTD devices. If unsure, say N. MTD debugging support CONFIG_MTD_DEBUG This turns on low-level debugging for the entire MTD sub-system. + Normally, you should say 'N'. + +MTD partitioning support +CONFIG_MTD_PARTITIONS + If you have a device which needs to divide its flash chip(s) up + into multiple 'partitions', each of which appears to the user as + a separate MTD device, you require this option to be enabled. If + unsure, say 'Y'. + + Note, however, that you don't need this option for the DiskOnChip + devices. Partitioning on NFTL 'devices' is a different - that's the + 'normal' form of partitioning used on a block device. + +RedBoot partition table parsing +CONFIG_MTD_REDBOOT_PARTS + RedBoot is a ROM monitor and bootloader which deals with multiple + 'images' in flash devices by putting a table in the last erase + block of the device, similar to a partition table, which gives + the offsets, lengths and names of all the images stored in the + flash. + + If you need code which can detect and parse this table, and register + MTD 'partitions' corresponding to each image in the table, enable + this option. + + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + example. + +Compaq bootldr partition table parsing +CONFIG_MTD_BOOTLDR_PARTS + The Compaq bootldr deals with multiple 'images' in flash devices + by putting a table in one of the first erase blocks of the device, + similar to a partition table, which gives the offsets, lengths and + names of all the images stored in the flash. + + If you need code which can detect and parse this table, and register + MTD 'partitions' corresponding to each image in the table, enable + this option. + + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + example. + +ARM Firmware Suite flash layout / partition parsing +CONFIG_MTD_AFS_PARTS + The ARM Firmware Suite allows the user to divide flash devices into + multiple 'images'. Each such image has a header containing its name + and offset/size etc. + + If you need code which can detect and parse these tables, and register + MTD 'partitions' corresponding to each image detected, enable + this option. + + You will still need the parsing functions to be called by the driver + for your particular device. It won't happen automatically. The + 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. MTD debugging verbosity CONFIG_MTD_DEBUG_VERBOSE Determines the verbosity level of the MTD debugging messages. -M-Systems Disk-On-Chip 1000 support -CONFIG_MTD_DOC1000 - This provides an MTD device driver for the M-Systems DiskOnChip - 1000 devices, which are obsolete so you probably want to say 'N'. -M-Systems Disk-On-Chip 2000 and Millennium support -CONFIG_MTD_DOC2000 - This provides an MTD device driver for the M-Systems DiskOnChip - 2000 and Millennium devices. Originally designed for the DiskOnChip - 2000, it also now includes support for the DiskOnChip Millennium. - If you have problems with this driver and the DiskOnChip Millennium, - you may wish to try the alternative Millennium driver below. To use - the alternative driver, you will need to undefine DOC_SINGLE_DRIVER - in the drivers/mtd/docprobe.c source code. +Direct chardevice access to MTD devices +CONFIG_MTD_CHAR + This provides a character device for each MTD device present in + the system, allowing the user to read and write directly to the + memory chips, and also use ioctl() to obtain information about + the device, or to erase parts of it. - If you use this device, you probably also want to enable the NFTL - 'NAND Flash Translation Layer' option below, which is used to emulate - a block device by using a kind of filesystem on the flash chips. +Caching block device access to MTD devices +CONFIG_MTD_BLOCK + Although most flash chips have an erase size too large to be useful + as block devices, it is possible to use MTD devices which are based + on RAM chips in this manner. This block device is a user of MTD devices + performing that function. -Alternative Disk-On-Chip Millennium support -CONFIG_MTD_DOC2001 - This provides an alternative MTD device driver for the M-Systems - DiskOnChip Millennium devices. Use this if you have problems with - the combined DiskOnChip 2000 and Millennium driver above. To get - the DiskOnChip probe code to load and use this driver instead of - the other one, you will need to undefine DOC_SINGLE_DRIVER near - the beginning of drivers/mtd/docprobe.c + At the moment, it is also required for the Journalling Flash File + System(s) to obtain a handle on the MTD device when it's mounted + (although JFFS and JFFS2 don't actually use any of the functionality + of the mtdblock device). - If you use this device, you probably also want to enable the NFTL - 'NAND Flash Translation Layer' option below, which is used to emulate - a block device by using a kind of filesystem on the flash chips. + Later, it may be extended to perform read/erase/modify/write cycles + on flash chips to emulate a smaller block size. Needless to say, + this is very unsafe, but could be useful for filesystems which are + almost never written to. -Ramix PMC551 PCI Mezzanine ram card support -CONFIG_MTD_PMC551 - This provides a MTD device driver for the Ramix PMC551 RAM PCI card - from Ramix Inc. (http://www.ramix.com/products/memory/pmc551.html). - These devices come in memory configurations from 32M - 1G. If you - have one, you probably want to enable this. + You do not need this option for use with the DiskOnChip devices. For + those, enable NFTL support (CONFIG_NFTL) instead. - If this driver is compiled as a module you get the ability to select the - size of the aperture window pointing into the devices memory. What this - means is that if you have a 1G card, normally the kernel will use a 1G - memory map as it's view of the device. As a module, you can select a - 1M window into the memory and the driver will "slide" the window around - the PMC551's memory. This was particularly useful on the 2.2 kernels - on PPC architectures as there was limited kernel space to deal with. +Readonly block device access to MTD devices +CONFIG_MTD_BLOCK_RO + This allows you to mount read-only filesystems (such as cramfs) from + an MTD device, without the overhead (and danger) of the caching + driver. -Use extra onboard system memory as MTD device -CONFIG_MTD_SLRAM - If your CPU cannot cache all of the physical memory in your machine, - you can still use it for storage or swap by using this driver to - present it to the system as a Memory Technology Device. + You do not need this option for use with the DiskOnChip devices. For + those, enable NFTL support (CONFIG_NFTL) instead. -PMC551 256M DRAM Bugfix -CONFIG_MTD_PMC551_BUGFIX - Some of Ramix's PMC551 boards with 256M configurations have invalid - column and row mux values. This option will fix them, but will break - other memory configurations. If unsure say N. +FTL (Flash Translation Layer) support +CONFIG_FTL + This provides support for the original Flash Translation Layer which + is part of the PCMCIA specification. It uses a kind of pseudo- + filesystem on a flash device to emulate a block device with 512-byte + sectors, on top of which you put a 'normal' filesystem. -PMC551 Debugging -CONFIG_MTD_PMC551_DEBUG - This option makes the PMC551 more verbose during it's operation and is - only really useful if you are developing on this driver or suspect a - possible hardware or driver bug. If unsure say N. + You may find that the algorithms used in this code are patented + unless you live in the Free World where software patents aren't + legal - in the USA you are only permitted to use this on PCMCIA + hardware, although under the terms of the GPL you're obviously + permitted to copy, modify and distribute the code as you wish. Just + not use it. -Debugging RAM test driver -CONFIG_MTD_MTDRAM - This enables a test MTD device driver which uses vmalloc() to - provide storage. You probably want to say 'N' unless you're - testing stuff. +NFTL (NAND Flash Translation Layer) support +CONFIG_NFTL + This provides support for the NAND Flash Translation Layer which is + used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- + filesystem on a flash device to emulate a block device with 512-byte + sectors, on top of which you put a 'normal' filesystem. + + You may find that the algorithms used in this code are patented + unless you live in the Free World where software patents aren't + legal - in the USA you are only permitted to use this on DiskOnChip + hardware, although under the terms of the GPL you're obviously + permitted to copy, modify and distribute the code as you wish. Just + not use it. + +Write support for NFTL (EXPERIMENTAL) +CONFIG_NFTL_RW + If you're lucky, this will actually work. Don't whinge if it doesn't. + Send mail to the MTD mailing list if + you want to help to make it more reliable. Common Flash Interface (CFI) support CONFIG_MTD_CFI @@ -10277,12 +10336,135 @@ CONFIG_MTD_CFI option. Visit (http://www.amd.com/products/nvd/overview/cfi.html) for more information on CFI. -CFI support for Intel/Sharp Extended Command Set chips +CFI Advanced configuration options +CONFIG_MTD_CFI_ADV_OPTIONS + If you need to specify a specific endianness for access to flash + chips, or if you wish to reduce the size of the kernel by including + support for only specific arrangements of flash chips, say 'Y'. This + option does not directly affect the code, but will enable other + configuration options which allow you to do so. + + If unsure, say 'N'. + +Specific CFI Flash geometry selection +CONFIG_MTD_CFI_GEOMETRY + This option does not affect the code directly, but will enable + some other configuration options which would allow you to reduce + the size of the kernel by including support for only certain + arrangements of CFI chips. If unsure, say 'N' and all options + which are supported by the current code will be enabled. + +Support 8-bit buswidth +CONFIG_MTD_CFI_B1 + If you wish to support CFI devices on a physical bus which is + 8 bits wide, say 'Y'. + +Support 16-bit buswidth +CONFIG_MTD_CFI_B2 + If you wish to support CFI devices on a physical bus which is + 16 bits wide, say 'Y'. + +Support 32-bit buswidth +CONFIG_MTD_CFI_B4 + If you wish to support CFI devices on a physical bus which is + 32 bits wide, say 'Y'. + +Support 1-chip flash interleave +CONFIG_MTD_CFI_I1 + If your flash chips are not interleaved - i.e. you only have one + flash chip addressed by each bus cycle, then say 'Y'. + +Support 2-chip flash interleave +CONFIG_MTD_CFI_I2 + If your flash chips are interleaved in pairs - i.e. you have two + flash chips addressed by each bus cycle, then say 'Y'. + +Support 4-chip flash interleave +CONFIG_MTD_CFI_I4 + If your flash chips are interleaved in fours - i.e. you have four + flash chips addressed by each bus cycle, then say 'Y'. + +Flash cmd/query data swapping +CONFIG_MTD_CFI_NOSWAP + This option defines the way in which the CPU attempts to arrange + data bits when writing the 'magic' commands to the chips. Saying + 'NO', which is the default when CONFIG_MTD_CFI_ADV_OPTIONS isn't + enabled, means that the CPU will not do any swapping; the chips + are expected to be wired to the CPU in 'host-endian' form. + Specific arrangements are possible with the BIG_ENDIAN_BYTE and + LITTLE_ENDIAN_BYTE, if the bytes are reversed. + + If you have a LART, on which the data (and address) lines were + connected in a fashion which ensured that the nets were as short + as possible, resulting in a bit-shuffling which seems utterly + random to the untrained eye, you need the LART_ENDIAN_BYTE option. + + Yes, there really exists something sicker than PDP-endian :) + +CFI support for Intel/Sharp Extended Commands CONFIG_MTD_CFI_INTELEXT The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code provides support for one of those command sets, used on Intel - Strataflash and other parts. + StrataFlash and other parts. + +CFI support for AMD/Fujitsu Standard Commands +CONFIG_MTD_CFI_AMDSTD + The Common Flash Interface defines a number of different command + sets which a CFI-compliant chip may claim to implement. This code + provides support for one of those command sets, used on chips + chips including the AMD Am29LV320. + +CFI support for Intel/Sharp Standard Commands +CONFIG_MTD_CFI_INTELSTD + The Common Flash Interface defines a number of different command + sets which a CFI-compliant chip may claim to implement. This code + provides support for one of those command sets. + +pre-CFI Sharp chip support +CONFIG_MTD_SHARP + This option enables support for flash chips using Sharp-compatible + commands, including some which are not CFI-compatible and hence + cannot be used with the CONFIG_MTD_CFI_INTELxxx options. + +AMD compatible flash chip support (non-CFI) +CONFIG_MTD_AMDSTD + This option enables support for flash chips using AMD-compatible + commands, including some which are not CFI-compatible and hence + cannot be used with the CONFIG_MTD_CFI_AMDSTD option. + + It also works on AMD compatible chips that do conform to CFI. + +Support for RAM chips in bus mapping +CONFIG_MTD_RAM + This option enables basic support for RAM chips accessed through + a bus mapping driver. + +Support for ROM chips in bus mapping +CONFIG_MTD_ROM + This option enables basic support for ROM chips accessed through + a bus mapping driver. + +CONFIG_MTD_JEDEC + Enable older older JEDEC flash interface devices for self programming + flash. It is commonly used in older AMD chips. It is only called + JEDEC because the JEDEC association (http://www.jedec.org/) + distributes the identification codes for the chips. WARNING!!!! This + code does not compile and is incomplete as are the specific JEDEC + devices drivers. + +CFI Flash device mapped on StrongARM SA11x0 +CONFIG_MTD_SA1100 + This enables access to the flash chips on most platforms based on the + SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you + have such a board, say 'Y'. + +CONFIG_MTD_SA1100_REDBOOT_PARTITIONS + Enabling this option will cause the kernel to look for a RedBoot + FIS (Flash Image System) table in the last erase block of the flash + chips detected. If you are using RedBoot on your SA11x0-based board + and want Linux to present 'partitions' matching the images which + RedBoot has listed, say 'Y'. Flash chip mapping in physical memory CONFIG_MTD_PHYSMAP @@ -10295,18 +10477,18 @@ CONFIG_MTD_PHYSMAP Physical start location of flash chip mapping CONFIG_MTD_PHYSMAP_START This is the physical memory location at which the flash chips - are mapped on your particular target board. Refer to the + are mapped on your particular target board. Refer to the memory map which should hopefully be in the documentation for your board. Physical length of flash chip mapping CONFIG_MTD_PHYSMAP_LEN This is the total length of the mapping of the flash chips on - your particular board. If there is space, or aliases, in the + your particular board. If there is space, or aliases, in the physical memory map between the chips, this could be larger than the total amount of flash present. Refer to the memory map which should hopefully be in the documentation for your - board. + board. CONFIG_MTD_PHYSMAP_BUSWIDTH This is the total width of the data bus of the flash devices @@ -10314,14 +10496,12 @@ CONFIG_MTD_PHYSMAP_BUSWIDTH bits, you would set the bus width octect value to 4. This is used internally by the CFI drivers. -Flash chip mapping on Mixcom piggyback card -CONFIG_MTD_MIXMEM - This supports the paging arrangement for access to flash chips - on the MixCOM piggyback card, allowing the flash chip drivers - to get on with their job of driving the flash chips without - having to know about the paging. If you have one of these boards, - you probably want to enable this mapping driver. More info is at - (http://www.itc.hu/). +Flash chip mapping on Sun Microsystems boardsets +CONFIG_MTD_SUN_UFLASH + This provides a 'mapping' driver which supports the way in + which user-programmable flash chips are connected on various + Sun Microsystems boardsets. This driver will require CFI support + in the kernel, so if you did not enable CFI previously, do that now. Flash chip mapping on Nora CONFIG_MTD_NORA @@ -10332,13 +10512,6 @@ CONFIG_MTD_PNC2000 PNC-2000 is the name of Network Camera product from PHOTRON Ltd. in Japan. It uses CFI-compliant flash. -Flash chip mapping on Octagon 5066 SBC -CONFIG_MTD_OCTAGON - This provides a 'mapping' driver which supports the way in which - the flash chips are connected in the Octagon-5066 Single Board - Computer. More information on the board is available at - (http://www.octagonsystems.com/Products/5066/5066.html). - Flash chip mapping on RPXlite PPC board CONFIG_MTD_RPXLITE The RPXLite PowerPC board has CFI-compliant chips mapped in @@ -10347,6 +10520,89 @@ CONFIG_MTD_RPXLITE to communicate with the chips on the RPXLite board. More at (http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm). +Flash chip mapping on AMD SC520 CDP board +CONFIG_MTD_SC520CDP + The SC520 CDP board has two banks of CFI-compliant chips and one + Dual-in-line JEDEC chip. This 'mapping' driver supports that + arrangement, implementing three MTD devices. + +Flash chip mapping on Arcom Control Systems' SBC-MediaGX +CONFIG_MTD_SBC_MEDIAGX + This provides a driver for the on-board flash of Arcom Control + Systems' SBC-MediaGX development board. By default the flash + is split into 3 partitions which are accessed as separate MTD + devices. This board utilizes Intel StrataFlash. More info at + (http://www.arcomcontrols.com/products/icp/pc104/processors/). + +Flash chip mapping on Arcom Control Systems' ELAN-104NC +CONFIG_MTD_ELAN_104NC + This provides a driver for the on-board flash of the Arcom Control + System's ELAN-104NC development board. By default the flash + is split into 3 partitions which are accessed as separate MTD + devices. This board utilizes Intel StrataFlash. More info at + (http://www.arcomcontrols.com/products/icp/pc104/processors/). + +Flash chip mapping on Compaq iPAQ/Bitsy +CONFIG_MTD_BITSY + This provides a driver for the on-board flash found in Compaq's + iPAQ Palm PC and their research prototype the Itsy. iPAQ info at + (http://www5.compaq.com/products/handhelds/pocketpc/) and the + Itsy (http://www.research.digital.com/wrl/projects/Itsy/index.html). + +Flash chip mapping on Compaq iPAQ/Bitsy +CONFIG_MTD_DC21285 + This provides a driver for the flash accessed using Intel's + 21285 bridge used with Intel's StrongARM processors. More info at + (http://developer.intel.com/design/bridge/quicklist/dsc-21285.htm). + +Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board +CONFIG_MTD_CSTM_MIPS_IXX + This provides a mapping driver for the Integrated Tecnology + Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference + Board. It provides the necessary addressing, length, buswidth, vpp code + and addition setup of the flash device for these boards. In addition, + this mapping driver can be used for other boards via setting of the + CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH parameters. This mapping + will provide one mtd device using one partition. The start address can + be offset from the beginning of flash and the len can be less than the + total flash device size to allow a window into the flash. Both CFI and + JEDEC probes are called. + +Physical start location of flash mapping +CONFIG_MTD_CSTM_MIPS_IXX_START + This is the physical memory location that the MTD driver will + use for the flash chips on your particular target board. + Refer to the memory map which should hopefully be in the + documentation for your board. + +Physical length of flash mapping +CONFIG_MTD_CSTM_MIPS_IXX_LEN + This is the total length that the MTD driver will use for the + flash chips on your particular board. Refer to the memory + map which should hopefully be in the documentation for your + board. + +Physical bus width of flash mapping +CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH + This is the total bus width of the mapping of the flash chips + on your particular board. + +Flash chip mapping on Mixcom piggyback card +CONFIG_MTD_MIXMEM + This supports the paging arrangement for access to flash chips + on the MixCOM piggyback card, allowing the flash chip drivers + to get on with their job of driving the flash chips without + having to know about the paging. If you have one of these boards, + you probably want to enable this mapping driver. More info is at + (http://www.itc.hu/). + +Flash chip mapping on Octagon 5066 SBC +CONFIG_MTD_OCTAGON + This provides a 'mapping' driver which supports the way in which + the flash chips are connected in the Octagon-5066 Single Board + Computer. More information on the board is available at + (http://www.octagonsystems.com/Products/5066/5066.html). + Flash chip mapping on Tempustech VMAX SBC301 CONFIG_MTD_VMAX This provides a 'mapping' driver which supports the way in which @@ -10354,57 +10610,168 @@ CONFIG_MTD_VMAX Board Computer. More information on the board is available at (http://www.tempustech.com/tt301.htm). -Direct chardevice access to MTD devices -CONFIG_MTD_CHAR - This provides a character device for each MTD device present in - the system, allowing the user to read and write directly to the - memory chips, and also use ioctl() to obtain information about - the device, or to erase parts of it. +Support for NAND flash devices +CONFIG_MTD_NAND + This enables support for accessing all type of NAND flash + devices. + +Support for software ECC algorithm +CONFIG_MTD_NAND_ECC + This enables software-based ECC for use with NAND flash chips. It + can detect and correct 1 bit errors per 256 byte blocks. This + should be used to increase the reliability of the data stored and + read on the device. + +Support for verify read after write +CONFIG_MTD_NAND_VERIFY_WRITE + This adds an extra check when data is written to the flash. The + NAND flash device internally checks only bits transitioning + from 1 to 0. There is a rare possibility that even though the + device thinks the write was successful, a bit could have been + flipped accidentaly due to device wear, gamma rays, whatever. + Enable this if you are really paranoid. + +Support for the SPIA board +CONFIG_MTD_NAND_SPIA + If you had to ask, you don't have one. Say 'N'. -Pseudo-blockdevice access to MTD devices -CONFIG_MTD_BLOCK - Although flash chips have an erase size too large to useful as - block devices, it is possible to use MTD devices which are based - on RAM chips in this manner. This blockdevice user of MTD devices - performs that function. At the moment, it is also required for - the Journalling Flash File System to obtain a handle on the MTD - device when it's mounted - although the JFFS doesn't actually use - any of the functions of the mtdblock device. +M-Systems Disk-On-Chip 1000 support +CONFIG_MTD_DOC1000 + This provides an MTD device driver for the M-Systems DiskOnChip + 1000 devices, which are obsolete so you probably want to say 'N'. - Later, it may be extended to perform read/erase/modify/write cycles - on flash chips to emulate a smaller block size. Needless to say, - this is very unsafe, but could be useful for file systems which are - almost never written to. +M-Systems Disk-On-Chip 2000 and Millennium support +CONFIG_MTD_DOC2000 + This provides an MTD device driver for the M-Systems DiskOnChip + 2000 and Millennium devices. Originally designed for the DiskOnChip + 2000, it also now includes support for the DiskOnChip Millennium. + If you have problems with this driver and the DiskOnChip Millennium, + you may wish to try the alternative Millennium driver below. To use + the alternative driver, you will need to undefine DOC_SINGLE_DRIVER + in the drivers/mtd/devices/docprobe.c source code. -FTL (Flash Translation Layer) support -CONFIG_FTL - This provides support for the original Flash Translation Layer which - is part of the PCMCIA specification. It uses a kind of pseudo- - file system on a flash device to emulate a block device with 512-byte - sectors, on top of which you put a 'normal' file system. You may find - that the algorithms used in this code are patented unless you live - in the Free World where software patents aren't legal - in the USA - you are only permitted to use this on PCMCIA hardware, although - under the terms of the GPL you're obviously permitted to copy, - modify and distribute the code as you wish. Just not use it. + If you use this device, you probably also want to enable the NFTL + 'NAND Flash Translation Layer' option below, which is used to emulate + a block device by using a kind of filesystem on the flash chips. -NFTL (NAND Flash Translation Layer) support -CONFIG_NFTL - This provides support for the NAND Flash Translation Layer which is - used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- - file system on a flash device to emulate a block device with 512-byte - sectors, on top of which you put a 'normal' file system. You may find - that the algorithms used in this code are patented unless you live - in the Free World where software patents aren't legal - in the USA - you are only permitted to use this on DiskOnChip hardware, although - under the terms of the GPL you're obviously permitted to copy, - modify and distribute the code as you wish. Just not use it. +Alternative Disk-On-Chip Millennium support +CONFIG_MTD_DOC2001 + This provides an alternative MTD device driver for the M-Systems + DiskOnChip Millennium devices. Use this if you have problems with + the combined DiskOnChip 2000 and Millennium driver above. To get + the DiskOnChip probe code to load and use this driver instead of + the other one, you will need to undefine DOC_SINGLE_DRIVER near + the beginning of drivers/mtd/devices/docprobe.c -Write support for NFTL (EXPERIMENTAL) -CONFIG_NFTL_RW - If you're lucky, this will actually work. Don't whine if it doesn't. - Contact (dwmw2@infradead.org) if you want to help to make it more - reliable. + If you use this device, you probably also want to enable the NFTL + 'NAND Flash Translation Layer' option below, which is used to emulate + a block device by using a kind of filesystem on the flash chips. + +Probe for DiskOnChip devices +CONFIG_MTD_DOCPROBE + This isn't a real config option, it's derived. + +Advanced detection options for DiskOnChip +CONFIG_MTD_DOCPROBE_ADVANCED + This option allows you to specify nonstandard address at which to + probe for a DiskOnChip, or to change the detection options. You're + unlikely to need any of this unless you're using LinuxBIOS. Say 'N'. + +Probe for 0x55 0xAA BIOS Extension Signature. +CONFIG_MTD_DOCPROBE_55AA + Check for the 0x55 0xAA signature of a DiskOnChip, and do not continue + with probing if it is absent. The signature will always be present for + a DiskOnChip 2000 or a normal DiskOnChip Millennium. Only if you have + overwritten the first block of a DiskOnChip Millennium will it be + absent. Enable this option if you are using LinuxBIOS or if you need + to recover a DiskOnChip Millennium on which you have managed to wipe + the first block. + +Physical address of DiskOnChip +CONFIG_MTD_DOCPROBE_ADDRESS + By default, the probe for DiskOnChip devices will look for a DiskOnChip + at every multiple of 0x2000 between 0xC8000 and 0xEE000. This option + allows you to specify a single address at which to probe for the device, + which is useful if you have other devices in that range which get upset + when they're probed. + + (Note that on PowerPC, the normal probe will only check at 0xE4000000.) + + Normally, you should leave this set to zero, to allow the probe at the + normal addresses. + +Probe high addresses +CONFIG_MTD_DOCPROBE_HIGH + By default, the probe for DiskOnChip devices will look for a DiskOnChip + at every multiple of 0x2000 between 0xC8000 and 0xEE000. This option + changes to make it probe between 0xFFFC8000 and 0xFFFEE000. Unless + you're using LinuxBIOS, this is unlikely to be useful to you. Say 'N'. + +Ramix PMC551 PCI Mezzanine ram card support +CONFIG_MTD_PMC551 + This provides a MTD device driver for the Ramix PMC551 RAM PCI card + from Ramix Inc. (http://www.ramix.com/products/memory/pmc551.html). + These devices come in memory configurations from 32M - 1G. If you + have one, you probably want to enable this. + + If this driver is compiled as a module you get the ability to select the + size of the aperture window pointing into the devices memory. What this + means is that if you have a 1G card, normally the kernel will use a 1G + memory map as it's view of the device. As a module, you can select a + 1M window into the memory and the driver will "slide" the window around + the PMC551's memory. This was particularly useful on the 2.2 kernels + on PPC architectures as there was limited kernel space to deal with. + +PMC551 256M DRAM Bugfix +CONFIG_MTD_PMC551_BUGFIX + Some of Ramix's PMC551 boards with 256M configurations have invalid column + and row mux values. This option will fix them, but will break other memory + configurations. If unsure say N. + +PMC551 Debugging +CONFIG_MTD_PMC551_DEBUG + This option makes the PMC551 more verbose during it's operation and is only + really usefull if you are developing on this driver or suspect a possible + hardware or driver bug. If unsure say N. + +Use extra onboard system memory as MTD device +CONFIG_MTD_SLRAM + If your CPU cannot cache all of the physical memory in your machine, + you can still use it for storage or swap by using this driver to + present it to the system as a Memory Technology Device. + +Debugging RAM test driver +CONFIG_MTD_MTDRAM + This enables a test MTD device driver which uses vmalloc() to + provide storage. You probably want to say 'N' unless you're + testing stuff. + +MTDRAM erase block size in KiB +CONFIG_MTDRAM_ERASE_SIZE + This allows you to configure the size of the erase blocks in the + device emulated by the MTDRAM driver. If the MTDRAM driver is built + as a module, it is also possible to specify this as a parameter when + loading the module. + +MTDRAM device size in KiB +CONFIG_MTDRAM_TOTAL_SIZE + This allows you to configure the total size of the MTD device + emulated by the MTDRAM driver. If the MTDRAM driver is built + as a module, it is also possible to specify this as a parameter when + loading the module. + +SRAM absolute position +CONFIG_MTDRAM_ABS_POS + If you have system RAM accessible by the CPU but not used by Linux + in normal operation, you can give the physical address at which the + available RAM starts, and the MTDRAM driver will use it instead of + allocating space from Linux's available memory. Otherwise, leave + this set to zero. Most people will want to leave this as zero. + +Flash chip mapping on the Flaga Digital Module +CONFIG_MTD_CFI_FLAGADM + Mapping for the Flaga digital module. If you don´t have one, ignore this + setting. Support for USB CONFIG_USB @@ -13243,8 +13610,8 @@ CONFIG_MS_BUSMOUSE Apple Desktop Bus support CONFIG_ADB Apple Desktop Bus (ADB) support is for support of devices which - are connected to the to an ADB port. ADB devices tend to have - 4 pins. If you have an Apple Macintosh prior to the iMac, or a + are connected to an ADB port. ADB devices tend to have 4 pins. + If you have an Apple Macintosh prior to the iMac, or a "Blue and White G3", you probably want to say Y here. Otherwise say N. @@ -14885,17 +15252,20 @@ CONFIG_SOUND_YM3812 If unsure, say Y. -ACI mixer (miroPCM12/PCM20) +ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio) CONFIG_SOUND_ACI_MIXER ACI (Audio Command Interface) is a protocol used to communicate with - the microcontroller on some sound cards produced by miro, e.g. the - miroSOUND PCM12 and PCM20. The main function of the ACI is to - control the mixer and to get a product identification. + the microcontroller on some sound cards produced by miro and Cardinal + Technologies. The main function of the ACI is to control the mixer + and to get a product identification. + + This Voxware ACI driver currently supports the ACI functions on the + miroSOUND PCM1-pro, PCM12 and PCM20 radio. On the PCM20 radio, ACI + also controls the radio tuner. This is supported in the video4linux + miropcm20 driver (say M or Y here and go back to "Multimedia devices" + -> "Radio Adapters"). - This Voxware ACI driver currently only supports the ACI functions on - the miroSOUND PCM12 and PCM20 cards. On the PCM20, ACI also controls - the radio tuner. This is supported in the video4linux - radio-miropcm20 driver. + This driver is also available as a module and will be called aci.o. SB32/AWE support CONFIG_SOUND_AWE32_SYNTH @@ -16713,11 +17083,11 @@ CONFIG_I2C_PARPORT say M here and read Documentation/modules.txt. The module will be called i2c-parport.o. -Miro PCM20 Radio +miroSOUND PCM20 radio CONFIG_RADIO_MIROPCM20 - Choose Y here if you have this FM radio card. You also need to say Y - to "ACI mixer (miroPCM12/PCM20)" (in "additional low level sound - drivers") for this to work. + Choose Y here if you have this sound card. You also need to say Y + to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") + for this to work. In order to control your radio card, you will need to use programs that are compatible with the Video for Linux API. Information on @@ -16727,7 +17097,21 @@ CONFIG_RADIO_MIROPCM20 If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be - called radio-miropcm20.o + called miropcm20.o + +miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL) +CONFIG_RADIO_MIROPCM20_RDS + Choose Y here if you want to see RDS/RBDS information like RadioText, + Programme Service name, Clock Time and date, Programme TYpe and + Traffic Announcement/Programme identification. You also need to say + Y to "miroSOUND PCM20 radio" and devfs! + + It's not possible to read the raw RDS packets from the device, so + the driver cant provide an V4L interface for this. But the + availability of RDS is reported over V4L by the basic driver already. + Here RDS can be read from files in /dev/v4l/rds. + + As module the driver will be called miropcm20-rds.o. GemTek Radio Card CONFIG_RADIO_GEMTEK diff --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog index 3dcd9d7250ab..51595fe1b98c 100644 --- a/Documentation/filesystems/devfs/ChangeLog +++ b/Documentation/filesystems/devfs/ChangeLog @@ -1613,3 +1613,35 @@ Work sponsored by SGI - Updated README from master HTML file - Ported to kernel 2.4.0-test3-pre4 (which had devfs-patch-v174) +=============================================================================== +Changes for patch v177 + +- Updated README from master HTML file + +- Documentation cleanups + +- Ensure terminates string for root entry + Thanks to Tim Jansen + +- Exported to modules + +- Make send events to devfsd + +- Cleaned up option processing in + +- Fixed bugs in handling symlinks: could leak or cause Oops + +- Cleaned up directory handling by separating fops + Thanks to Alexander Viro +=============================================================================== +Changes for patch v178 + +- Fixed handling of inverted options in +=============================================================================== +Changes for patch v179 + +- Adjusted to account for fix +=============================================================================== +Changes for patch v180 + +- Fixed !CONFIG_DEVFS_FS stub declaration of diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README index 0c6fde510600..267ebbc0948c 100644 --- a/Documentation/filesystems/devfs/README +++ b/Documentation/filesystems/devfs/README @@ -3,7 +3,7 @@ Devfs (Device File System) FAQ Linux Devfs (Device File System) FAQ Richard Gooch -3-JUL-2000 +26-APR-2001 ----------------------------------------------------------------------------- @@ -18,7 +18,7 @@ find out more about it at: http://www.atnf.csiro.au/~rgooch/linux/ -NEWFLASH: The official 2.3.46 kernel has +NEWSFLASH: The official 2.3.46 kernel has included the devfs patch. Future patches will be released which build on this. These patches are rolled into Linus' tree from time to time. @@ -54,6 +54,7 @@ All the way with Devfs Other Issues Kernel Naming Scheme Devfsd Naming Scheme +Old Compatibility Names SCSI Host Probing Issues @@ -99,6 +100,7 @@ Also, because the devfs namespace exists without any devfs mounts, you can easily mount the root filesystem by referring to an entry in the devfs namespace. + The cost of devfs is a small increase in kernel code size and memory usage. About 7 pages of code (some of that in __init sections) and 72 bytes for each entry in the namespace. A modest system has only a @@ -157,6 +159,7 @@ usually find a MAKEDEV programme which creates all these (hundreds!) of nodes. This means that changes in the kernel must be reflected by changes in the MAKEDEV programme, or else the system administrator creates device nodes by hand. + The basic problem is that there are two separate databases of major and minor numbers. One is in the kernel and one is in /dev (or in a MAKEDEV programme, if you want to look at it that way). This is @@ -192,6 +195,7 @@ because disc is cheap these days. Embedded systems would care about 256 kBytes of /dev inodes, but you could argue that embedded systems would have hand-tuned /dev directories. I've had to do just that on my embedded systems, but I would rather just leave it to devfs. + Another issue is the time taken to lookup an inode when first referenced. Not only does this take time in scanning through a list in memory, but also the seek times to read the inodes off disc. @@ -212,7 +216,7 @@ also provides this information). Furthermore, such a system would likely be implemented in an ad-hoc fashion, as different drivers will provide their information in different ways. -Devfs is much cleaner, because it (natually) has a uniform mechanism +Devfs is much cleaner, because it (naturally) has a uniform mechanism to provide this information: the device nodes themselves! @@ -253,7 +257,7 @@ Alternatively, you can use hashing to speed up the search. But why do that search at all if you don't have to? Once again, it seems pointless. -Note thate devfs doesn't use the major&minor system. For devfs +Note that devfs doesn't use the major&minor system. For devfs entries, the connection is done when you lookup the /dev entry. When devfs_register() is called, an internal table is appended which has the entry name and the file_operations. If the dentry cache doesn't @@ -274,9 +278,9 @@ driver, in a scalable way. /dev as a system administration tool Right now /dev contains a list of conceivable devices, most of which I -don't have. A devfs would only show those devices available on my -system. This means that listing /dev would be a handy way of checking -what devices were available. +don't have. Devfs only shows those devices available on my +system. This means that listing /dev is a handy way of checking what +devices are available. Major&minor size @@ -289,9 +293,9 @@ disc arrays. With devfs an arbitrary pointer can be associated with each device entry, which can be used to give an effective 32 bit device identifier (i.e. that's like having a 32 bit minor number). Since this is private to the kernel, there are no C library -compatibility which you would have with increasing major and minor -number sizes. See the section on "Allocation of Device Numbers" for -details on maintaining compatibility with userspace. +compatibility issues which you would have with increasing major and +minor number sizes. See the section on "Allocation of Device Numbers" +for details on maintaining compatibility with userspace. Solving this requires a kernel change. @@ -375,9 +379,9 @@ possible to send a message (either synchronously or asynchronously) to devfsd on any event, such as registration/unregistration of device entries, opening and closing devices, looking up inodes, scanning directories and more. This has many possibilities. Some of these are -already implemented. +already implemented. See: + -See: http://www.atnf.csiro.au/~rgooch/linux/ Device entry registration events can be used by devfsd to change @@ -414,6 +418,7 @@ Inode lookup events can be used to authenticate module autoload requests. Instead of using kmod directly, the event is sent to devfsd which can implement an arbitrary authentication before loading the module itself. + Inode lookup events can also be used to construct arbitrary namespaces, without having to resort to populating devfs with symlinks to devices that don't exist. @@ -447,10 +452,10 @@ openings. Who else does it? -FreeBSD has a devfs implementation. Solaris 2 has a pseudo-devfs -(something akin to scsidev but for all devices, with some unspecified -kernel support). BeOS, Plan9 and QNX also have it. SGI's IRIX 6.4 and -above also have a device filesystem. +FreeBSD has a devfs implementation. Solaris and AIX each have a +pseudo-devfs (something akin to scsidev but for all devices, with some +unspecified kernel support). BeOS, Plan9 and QNX also have it. SGI's +IRIX 6.4 and above also have a device filesystem. While we shouldn't just automatically do something because others do it, we should not ignore the work of others either. FreeBSD has a lot @@ -613,6 +618,21 @@ network (a password is still required, though). However, since there are problems with dealing with symlinks, I'm suspicious of the level of security offered in any case. +A better solution is to install util-linux-2.10.h or later, which +fixes a bug with ttyname handling in the login programme. Then append +the following lines to your /etc/securetty file: + +vc/1 +vc/2 +vc/3 +vc/4 +vc/5 +vc/6 +vc/7 +vc/8 + +This will not weaken security. + XFree86 While not essential, it's probably a good idea to upgrade to XFree86 4.0, as patches went in to make it more devfs-friendly. If you don't, @@ -627,17 +647,25 @@ startx. # file classes -- these are regular expressions -=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] -+=tty[0-9][0-9]* [0-9][0-9]* :[0-9]\.[0-9] :[0-9] ++=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] # device classes -- these are shell-style globs =/dev/fd[0-1]* +If the patch does not apply, then change the line: + +=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] + +with: + +=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] + Disable devpts I've had a report of devpts mounted on /dev/pts not working correctly. Since devfs will also manage /dev/pts, there is no need to mount devpts as well. You should either edit your -/etc/fstab so devpts is not mounted, or disable devfs from +/etc/fstab so devpts is not mounted, or disable devpts from your kernel configuration. Unsupported drivers @@ -664,15 +692,23 @@ described above. The Kernel Finally, you need to make sure devfs is compiled into your -kernel. Set CONFIG_DEVFS_FS=y and recompile your kernel. Next, you -need to make sure devfs is mounted. The best solution is to pass -devfs=mount at the kernel boot command line. You can edit -/etc/lilo.conf and add the line: - -append = "devfs=mount" - - -This will make the kernel mount devfs at boot time onto /dev. +kernel. Set CONFIG_DEVFS_FS=y and CONFIG_DEVFS_MOUNT=y and recompile +your kernel. At boot, devfs will be mounted onto /dev. + +If you encounter problems booting (for example if you forgot a +configuration step), you can pass devfs=nomount at the kernel +boot command line. This will prevent the kernel from mounting devfs at +boot time onto /dev. + +In general, a kernel built with CONFIG_DEVFS_FS=y but without mounting +devfs onto /dev is completely safe, and requires no +configuration changes. One exception to take note of is when +LABEL= directives are used in /etc/fstab. In this +case you will be unable to boot properly. This is because the +mount(8) programme uses /proc/partitions as part of +the volume label search process, and the device names it finds are not +available, because setting CONFIG_DEVFS_FS=y changes the names in +/proc/partitions, irrespective of whether devfs is mounted. Now you've finished all the steps required. You're now ready to boot your shiny new kernel. Enjoy. @@ -701,7 +737,7 @@ A much better approach is to use devfsd to save and restore permissions. It may be configured to record changes in permissions and will save them in a database (in fact a directory tree), and restore these upon boot. This is an efficient method and results in immediate -saving of current permissions (unlike the tar approach, which save +saving of current permissions (unlike the tar approach, which saves permissions at some unspecified future time). The default configuration file supplied with devfsd has config entries @@ -745,8 +781,11 @@ devfsd /dev + add the following lines to your /etc/devfsd.conf file: +REGISTER ^pt[sy]/.* IGNORE +CHANGE ^pt[sy]/.* IGNORE REGISTER .* COPY /dev-state/$devname $devpath CHANGE .* COPY $devpath /dev-state/$devname CREATE .* COPY $devpath /dev-state/$devname @@ -758,6 +797,17 @@ reboot. +Permissions database stored in normal directory + +If you are using an older kernel which doesn't support VFS binding, +then you won't be able to have the permissions database in a +mounted-over /dev. However, you can still use a regular +directory to store the database. The sample /etc/devfsd.conf +file above may still be used. You will need to create the +/dev-state directory prior to installing devfsd. If you have +old permissions in /dev, then just copy the device nodes over +to the new directory. + Dealing with drivers without devfs support @@ -910,13 +960,48 @@ As of devfsd-v1.3.10, a generic /etc/modules.devfs configuration file is installed, which is used by the MODLOAD action. This should be sufficient for most configurations. If you require further configuration, edit your /etc/modules.conf -file. +file. The way module autoloading work with devfs is: + + +a process attempts to lookup a device node (e.g. /dev/fred) + + +if that device node does not exist, the full pathname is passed to +devfsd as a string + + +devfsd will pass the string to the modprobe programme (provided the +configuration line shown above is present), and specifies that +/etc/modules.devfs is the configuration file + + +/etc/modules.devfs includes /etc/modules.conf to +access local configurations + +modprobe will search it's configuration files, looking for an alias +that translates the pathname into a module name + + +the translated pathname is then used to load the module. + + +If you wanted a lookup of /dev/fred to load the +mymod module, you would require the following configuration +line in /etc/modules.conf: + +alias /dev/fred mymod + +The /etc/modules.devfs configuration file provides many such +aliases for standard device names. If you look closely at this file, +you will note that some modules require multiple alias configuration +lines. This is required to support module autoloading for old and new +device names. Mounting root off a devfs device If you wish to mount root off a devfs device when you pass the -"devfs=only" boot option, then you need to pass in the "root=" -option to the kernel when booting. If you use LILO, then you must have -this in lilo.conf: +"devfs=only" boot option, then you need to pass in the +"root=" option to the kernel when booting. If you use +LILO, then you must have this in lilo.conf: append = "root=" @@ -926,12 +1011,12 @@ do): root = -then LILO will determine the device number of and will write -that device number into a special place in the kernel image before -starting the kernel, and the kernel will use that device number to -mount the root filesystem. So, using the "append" variety ensures that -LILO passes the root filesystem device as a string, which devfs can -then use. +then LILO will determine the device number of and will +write that device number into a special place in the kernel image +before starting the kernel, and the kernel will use that device number +to mount the root filesystem. So, using the "append" variety ensures +that LILO passes the root filesystem device as a string, which devfs +can then use. Note that this isn't an issue if you don't pass "devfs=only". @@ -1067,7 +1152,8 @@ The tty devices now appear as: -------- -------- ----------- /dev/tts/{0,1,...} /dev/ttyS{0,1,...} Serial ports /dev/cua/{0,1,...} /dev/cua{0,1,...} Call out devices - /dev/vc/{0,1,...} /dev/tty{1...63} Virtual consoles + /dev/vc/0 /dev/tty Current virtual console + /dev/vc/{1,2,...} /dev/tty{1...63} Virtual consoles /dev/vcc/{0,1,...} /dev/vcs{1...63} Virtual consoles /dev/pty/m{0,1,...} /dev/ptyp?? PTY masters /dev/pty/s{0,1,...} /dev/ttyp?? PTY slaves @@ -1109,7 +1195,8 @@ the kernel-supplied namespace. In some cases, the kernel-supplied naming scheme is quite convenient, so devfsd does not provide another naming scheme. The convenience names that devfsd creates are in fact the same names as the original devfs -kernel patch created (before Linus mandated the Big Name Change). +kernel patch created (before Linus mandated the Big Name +Change). These are referred to as "new compatibility entries". In order to configure devfsd to create these convenience names, the following lines should be placed in your /etc/devfsd.conf: @@ -1198,6 +1285,24 @@ All XT discs are placed under /dev/xd. The first XT disc would appear as /dev/xd/c0t0. +Old Compatibility Names + +The old compatibility names are the legacy device names, such as +/dev/hda, /dev/sda, /dev/rtc and so on. +Devfsd can be configured to create compatibility symlinks so that you +may continue to use the old names in your configuration files and so +that old applications will continue to function correctly. + +In order to configure devfsd to create these legacy names, the +following lines should be placed in your /etc/devfsd.conf: + +REGISTER .* MKOLDCOMPAT +UNREGISTER .* RMOLDCOMPAT + +This will cause devfsd to create (and destroy) symbolic links which +point to the kernel-supplied names. + + SCSI Host Probing Issues Devfs allows you to identify SCSI discs based in part on SCSI host @@ -1220,14 +1325,15 @@ of drivers used in the /proc filesystem. For example: means that devices connected to -- first aha1542 controller - will be c0b#t#u# -- first parallel port ZIP - will be c1b#t#u# -- second aha1542 controller - will be c2b#t#u# -- first NCR53C7xx controller - will be c4b#t#u# -- any extra controller - will be c5b#t#u#, c6b#t#u#, etc +- first aha1542 controller - will be /dev/scsi/host0/bus#/target#/lun# +- first parallel port ZIP - will be /dev/scsi/host1/bus#/target#/lun# +- second aha1542 controller - will be /dev/scsi/host2/bus#/target#/lun# +- first NCR53C7xx controller - will be /dev/scsi/host4/bus#/target#/lun# +- any extra controller - will be /dev/scsi/host5/bus#/target#/lun#, + /dev/scsi/host6/bus#/target#/lun#, etc - if any of above controllers will not be found - the reserved names will not be used by any other device. -- c3b#t#u# names will never be used +- /dev/scsi/host3/bus#/target#/lun# names will never be used You can use ',' instead of ':' as the separator character if you @@ -1343,6 +1449,7 @@ Questions and Answers Making things work Alternatives to devfs +What I don't like about devfs @@ -1518,6 +1625,54 @@ proposal above + +What I don't like about devfs + +Here are some common complaints about devfs, and some suggestions and +solutions that may make it more palatable for you. I can't please +everybody, but I do try :-) + +I hate the naming scheme + +First, remember that no naming scheme will please everybody. You hate +the scheme, others love it. Who's to say who's right and who's wrong? +Ultimately, the person who writes the code gets to choose, and what +exists now is a combination of the the choices made by the +devfs author and the +kernel maintainer (Linus). + +However, not all is lost. If you want to create your own naming +scheme, it is a simple matter to write a standalone script, hack +devfsd, or write a script called by devfsd. You can create whatever +naming scheme you like. + +Further, if you want to remove all traces of the devfs naming scheme +from /dev, you can mount devfs elsewhere (say +/devfs) and populate /dev with links into +/devfs. This population can be automated using devfsd if you +wish. + +You can even use the VFS binding facility to make the links, rather +than using symbolic links. This way, you don't even have to see the +"destination" of these symbolic links. + +Devfs puts policy into the kernel + +There's already policy in the kernel. Device numbers are in fact +policy (why should the kernel dictate what device numbers I use?). +Face it, some policy has to be in the kernel. The real difference +between device names as policy and device numbers as policy is that +no one will use device numbers directly, because device +numbers are devoid of meaning to humans and are ugly. At least with +the devfs device names, (even though you can add your own naming +scheme) some people will use the devfs-supplied names directly. This +offends some people :-) + +Devfs is bloatware + +This is not even remotely true. As shown above, +both code and data size are quite modest. + ----------------------------------------------------------------------------- @@ -1552,4 +1707,12 @@ http://johannes.erdfelt.com/hotswap.txt. Johannes has promised a HTML version will follow. +I presented an invited +paper +at the + +2nd Annual Storage Management Workshop held in Miamia, Florida, +U.S.A. in October 2000. + + diff --git a/Documentation/sound/README.OSS b/Documentation/sound/README.OSS index 1895ad6106e8..453babb26d30 100644 --- a/Documentation/sound/README.OSS +++ b/Documentation/sound/README.OSS @@ -17,7 +17,7 @@ they are gone forever. Keeping this in mind and with a grain of salt this document can be still interesting and very helpful. [ File edited 17.01.1999 - Riccardo Facchetti ] -[ Edited miroSOUND section 17.09.2000 - Robert Siemer ] +[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] OSS/Free version 3.8 release notes ---------------------------------- @@ -1327,7 +1327,7 @@ miroSOUND --------- The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used -successfully. This card is based on the MAD16, OPL4, and CS4231A chips +successfully. These cards are based on the MAD16, OPL4, and CS4231A chips and everything said in the section about MAD16 cards applies here, too. The only major difference between the PCMxx and other MAD16 cards is that instead of the mixer in the CS4231 codec a separate mixer @@ -1337,8 +1337,8 @@ protocol that is implemented in a separate lowlevel driver. Make sure you compile this ACI driver together with the normal MAD16 support when you use a miroSOUND PCMxx card. The ACI mixer is controlled by /dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load -time). Only in special cases you want to change something on the CS4231 -mixer. +time). Only in special cases you want to change something regularly on +the CS4231 mixer. The miroSOUND PCM12 and PCM20 radio is capable of full duplex operation (simultaneous PCM replay and recording), which allows you to @@ -1354,10 +1354,9 @@ ACI. This radio tuner is supported by the ACI driver together with the miropcm20.o module. Also the 7-band equalizer is integrated (limited by the OSS-design). Developement has started and maybe finished for the RDS decoder on this card, too. You will be able to -read radio text, the program service name, program type and +read RadioText, the Programme Service name, Programme TYpe and others. Even the v4l radio module benefits from it with a refined -strength value. See aci.c, radio-miropcm20.c and rds-miropcm20.c for -more details. +strength value. See aci.[ch] and miropcm20*.[ch] for more details. The following configuration parameters have worked fine for the PCM12 in Markus Kuhn's system, many other configurations might work, too: diff --git a/MAINTAINERS b/MAINTAINERS index 4be65f488f66..edbd94488052 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -712,16 +712,11 @@ P: Jaroslav Kysela M: perex@suse.cz S: Maintained -ISDN SUBSYSTEM (general) -P: Fritz Elfert -M: fritz@isdn4linux.de -L: isdn4linux@listserv.isdn4linux.de -W: http://www.isdn4linux.de -S: Maintained - -ISDN SUBSYSTEM (card drivers) +ISDN SUBSYSTEM P: Karsten Keil M: kkeil@suse.de +P: Kai Germaschewski +M: kai.germaschewski@gmx.de L: isdn4linux@listserv.isdn4linux.de W: http://www.isdn4linux.de S: Maintained diff --git a/Makefile b/Makefile index 871e98f04ed9..588ee5c26d39 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 6 -EXTRAVERSION =-pre1 +EXTRAVERSION =-pre3 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -87,7 +87,8 @@ export MODLIB CPPFLAGS := -D__KERNEL__ -I$(HPATH) -CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ + -fomit-frame-pointer -fno-strict-aliasing AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 109de59a28ae..54fa122cfa01 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -44,7 +44,6 @@ extern int do_pipe(int *); -extern asmlinkage int sys_swapon(const char *specialfile, int swap_flags); extern asmlinkage unsigned long sys_brk(unsigned long); /* diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c index c2513d3b8b1c..563bfba72115 100644 --- a/arch/alpha/kernel/sys_sable.c +++ b/arch/alpha/kernel/sys_sable.c @@ -96,7 +96,7 @@ static struct static inline void sable_update_irq_hw(unsigned long bit, unsigned long mask) { - int port = 0x536; + int port = 0x537; if (bit >= 16) { port = 0x53d; @@ -121,7 +121,7 @@ sable_ack_irq_hw(unsigned long bit) } else if (bit >= 8) { port = 0x53a; val1 = 0xE0 | (bit - 8); - val2 = 0xE0 | 2; + val2 = 0xE0 | 3; } else { port = 0x536; val1 = 0xE0 | (bit - 0); diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 2917cf581958..dfd16d74c03a 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -102,6 +102,64 @@ CONFIG_PM=y # # CONFIG_MTD is not set +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_CFI_INTELEXT is not set +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_JEDEC is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_SUN_UFLASH is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_PNC2000 is not set +# CONFIG_MTD_RPXLITE is not set +# CONFIG_MTD_SC520CDP is not set +# CONFIG_MTD_NETSC520 is not set +# CONFIG_MTD_SBC_GXX is not set +# CONFIG_MTD_ELAN_104NC is not set +# CONFIG_MTD_SA1100 is not set +# CONFIG_MTD_SA1100_REDBOOT_PARTITIONS is not set +# CONFIG_MTD_SA1100_BOOTLDR_PARTITIONS is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_IQ80310 is not set +# CONFIG_MTD_DBOX2 is not set +# CONFIG_MTD_CSTM_MIPS_IXX is not set +# CONFIG_MTD_CFI_FLAGADM is not set +# CONFIG_MTD_MIXMEM is not set +# CONFIG_MTD_OCTAGON is not set +# CONFIG_MTD_VMAX is not set +# CONFIG_MTD_OCELOT is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_SPIA is not set + # # Parallel port support # diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 1eb3161dac8a..cd04e193fb88 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -246,16 +246,9 @@ badsys: jmp ret_from_sys_call ALIGN -ret_from_exception: - cli - cmpl $0,need_resched(%ebx) - jne reschedule - cmpl $0,sigpending(%ebx) - jne signal_return - jmp restore_all - ENTRY(ret_from_intr) GET_CURRENT(%ebx) +ret_from_exception: movl EFLAGS(%esp),%eax # mix EFLAGS and CS movb CS(%esp),%al testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? @@ -313,14 +306,16 @@ ENTRY(device_not_available) pushl $-1 # mark this as an int SAVE_ALL GET_CURRENT(%ebx) - pushl $ret_from_exception movl %cr0,%eax testl $0x4,%eax # EM (math emulation bit) - je SYMBOL_NAME(math_state_restore) + jne device_not_available_emulate + call SYMBOL_NAME(math_state_restore) + jmp ret_from_exception +device_not_available_emulate: pushl $0 # temporary storage for ORIG_EIP call SYMBOL_NAME(math_emulate) addl $4,%esp - ret + jmp ret_from_exception ENTRY(debug) pushl $0 diff --git a/arch/i386/math-emu/fpu_trig.c b/arch/i386/math-emu/fpu_trig.c index a4ef73098477..f6ceae26cf8d 100644 --- a/arch/i386/math-emu/fpu_trig.c +++ b/arch/i386/math-emu/fpu_trig.c @@ -1543,6 +1543,7 @@ static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag) EXCEPTION(EX_INTERNAL | 0x116); return; #endif /* PARANOID */ + break; } } else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) ) diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S index d02ddf760ee2..7f7134a84543 100644 --- a/arch/ppc/kernel/entry.S +++ b/arch/ppc/kernel/entry.S @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.entry.S 1.12 05/21/01 11:49:59 paulus + * BK Id: SCCS/s.entry.S 1.15 06/09/01 22:16:38 paulus */ /* * PowerPC version @@ -310,25 +310,7 @@ ret_from_intercept: beq restore .globl ret_from_except ret_from_except: - lwz r5,_MSR(r1) - andi. r5,r5,MSR_EE - beq 2f - lis r4,irq_stat@ha /* &softirq_active for cpu 0 */ - addi r4,r4,irq_stat@l -#ifdef CONFIG_SMP - /* get processor # */ - lwz r3,PROCESSOR(r2) - slwi r3,r3,LG_CACHE_LINE_SIZE - add r4,r4,r3 -#endif /* CONFIG_SMP */ - lwz r5,0(r4) /* softirq_active */ - lwz r4,4(r4) /* softirq_mask */ - and. r5,r5,r4 - beq+ 2f - bl do_softirq - .globl do_bottom_half_ret -do_bottom_half_ret: -2: lwz r3,_MSR(r1) /* Returning to user mode? */ + lwz r3,_MSR(r1) /* Returning to user mode? */ andi. r3,r3,MSR_PR beq+ do_signal_ret /* if so, check need_resched and signals */ lwz r3,NEED_RESCHED(r2) diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index bc29362fb54a..87ced4dab22a 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.ppc_ksyms.c 1.31 05/18/01 08:18:10 patch + * BK Id: SCCS/s.ppc_ksyms.c 1.34 06/09/01 22:38:13 paulus */ #include #include @@ -357,7 +357,6 @@ EXPORT_SYMBOL(cpm_free_handler); #endif /* CONFIG_8xx */ EXPORT_SYMBOL(ret_to_user_hook); -EXPORT_SYMBOL(do_softirq); EXPORT_SYMBOL(next_mmu_context); EXPORT_SYMBOL(set_context); EXPORT_SYMBOL(mmu_context_overflow); diff --git a/arch/ppc/xmon/xmon.c b/arch/ppc/xmon/xmon.c index dd6fc824589a..37f80c49c646 100644 --- a/arch/ppc/xmon/xmon.c +++ b/arch/ppc/xmon/xmon.c @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.xmon.c 1.9 05/17/01 18:14:24 cort + * BK Id: SCCS/s.xmon.c 1.12 06/09/01 22:18:05 paulus */ /* * Routines providing a simple monitor for use on the PowerMac. @@ -661,8 +661,7 @@ backtrace(struct pt_regs *excp) unsigned stack[2]; struct pt_regs regs; extern char ret_from_intercept, ret_from_syscall_1, ret_from_syscall_2; - extern char do_bottom_half_ret, do_signal_ret; - extern char ret_from_except; + extern char do_signal_ret, ret_from_except; printf("backtrace:\n"); @@ -680,7 +679,6 @@ backtrace(struct pt_regs *excp) || stack[1] == (unsigned) &ret_from_except || stack[1] == (unsigned) &ret_from_syscall_1 || stack[1] == (unsigned) &ret_from_syscall_2 - || stack[1] == (unsigned) &do_bottom_half_ret || stack[1] == (unsigned) &do_signal_ret) { if (mread(sp+16, ®s, sizeof(regs)) != sizeof(regs)) break; diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S index b4b0b5f7b45f..4e0b8eb98d8b 100644 --- a/arch/sparc/kernel/rtrap.S +++ b/arch/sparc/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.55 2000/08/05 10:48:40 davem Exp $ +/* $Id: rtrap.S,v 1.56 2001/06/05 09:56:06 davem Exp $ * rtrap.S: Return from Sparc trap low-level code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -52,9 +52,8 @@ ret_trap_entry: sll %l3, 5, %l3 sethi %hi(C_LABEL(irq_stat)), %l4 ! &softirq_active add %l4, %l3, %l4 - ld [%l4 + %lo(C_LABEL(irq_stat))], %g5 ! softirq_active - ld [%l4 + %lo(C_LABEL(irq_stat) + 4)], %g4 ! softirq_mask - andcc %g4, %g5, %g0 + ld [%l4 + %lo(C_LABEL(irq_stat))], %g5 ! softirq_pending + cmp %g5, 0 be C_LABEL(ret_trap_lockless_ipi) nop call C_LABEL(do_softirq) diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 12c855e04c6c..3a0a9cc12a9f 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index c1ddf1f9046f..9c98f266e60d 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.61 2001/04/24 05:13:25 davem Exp $ +/* $Id: ebus.c,v 1.63 2001/06/08 02:27:16 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -25,7 +25,6 @@ struct linux_ebus *ebus_chain = 0; #ifdef CONFIG_SUN_AUXIO extern void auxio_probe(void); #endif -extern void rs_init(void); static inline void *ebus_alloc(size_t size) { @@ -269,8 +268,6 @@ probe_interrupts: printk("]"); } -extern void clock_probe(void); -extern void power_init(void); void __init ebus_init(void) { @@ -395,10 +392,7 @@ void __init ebus_init(void) ++num_ebus; } - rs_init(); #ifdef CONFIG_SUN_AUXIO auxio_probe(); #endif - clock_probe(); - power_init(); } diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 66408deb008c..baee9f5d626a 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -1,4 +1,4 @@ -/* $Id: pci.c,v 1.29 2001/05/15 08:54:30 davem Exp $ +/* $Id: pci.c,v 1.32 2001/06/08 06:25:41 davem Exp $ * pci.c: UltraSparc PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com) @@ -177,6 +177,10 @@ static void __init pci_reorder_devs(void) } } +extern void rs_init(void); +extern void clock_probe(void); +extern void power_init(void); + void __init pcibios_init(void) { pci_controller_probe(); @@ -190,6 +194,9 @@ void __init pcibios_init(void) isa_init(); ebus_init(); + rs_init(); + clock_probe(); + power_init(); } struct pci_fixup pcibios_fixups[] = { @@ -198,6 +205,66 @@ struct pci_fixup pcibios_fixups[] = { void pcibios_fixup_bus(struct pci_bus *pbus) { + struct pci_pbm_info *pbm = pbus->sysdata; + + /* Generic PCI bus probing sets these to point at + * &io{port,mem}_resouce which is wrong for us. + */ + pbus->resource[0] = &pbm->io_space; + pbus->resource[1] = &pbm->mem_space; +} + +/* NOTE: This can get called before we've fixed up pdev->sysdata. */ +int pci_claim_resource(struct pci_dev *pdev, int resource) +{ + struct pci_pbm_info *pbm = pci_bus2pbm[pdev->bus->number]; + struct resource *res = &pdev->resource[resource]; + struct resource *root; + + if (!pbm) + return -EINVAL; + + if (res->flags & IORESOURCE_IO) + root = &pbm->io_space; + else + root = &pbm->mem_space; + + pbm->parent->resource_adjust(pdev, res, root); + + return request_resource(root, res); +} + +int pci_assign_resource(struct pci_dev *pdev, int resource) +{ + struct pcidev_cookie *pcp = pdev->sysdata; + struct pci_pbm_info *pbm = pcp->pbm; + struct resource *res = &pdev->resource[resource]; + struct resource *root; + unsigned long min, max, size, align; + int err; + + if (res->flags & IORESOURCE_IO) { + root = &pbm->io_space; + min = root->start + 0x400UL; + max = root->end; + } else { + root = &pbm->mem_space; + min = root->start; + max = min + 0x80000000UL; + } + + size = res->end - res->start; + align = size + 1; + + err = allocate_resource(root, res, size + 1, min, max, align, NULL, NULL); + if (err < 0) { + printk("PCI: Failed to allocate resource %d for %s\n", + resource, pdev->name); + } else { + pbm->parent->base_address_update(pdev, resource); + } + + return err; } void pcibios_update_resource(struct pci_dev *pdev, struct resource *res1, diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index a10b693d722f..6cdef1e5e052 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -1,4 +1,4 @@ -/* $Id: pci_common.c,v 1.18 2001/05/18 23:06:35 davem Exp $ +/* $Id: pci_common.c,v 1.21 2001/06/08 06:57:19 davem Exp $ * pci_common.c: PCI controller common support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -93,12 +93,38 @@ static void pci_device_delete(struct pci_dev *pdev) } /* Older versions of OBP on PCI systems encode 64-bit MEM - * space assignments incorrectly, this fixes them up. + * space assignments incorrectly, this fixes them up. We also + * take the opportunity here to hide other kinds of bogus + * assignments. */ -static void __init fixup_obp_assignments(struct pcidev_cookie *pcp) +static void __init fixup_obp_assignments(struct pci_dev *pdev, + struct pcidev_cookie *pcp) { int i; + if (pdev->vendor == PCI_VENDOR_ID_AL && + (pdev->device == PCI_DEVICE_ID_AL_M7101 || + pdev->device == PCI_DEVICE_ID_AL_M1533)) { + int i; + + /* Zap all of the normal resources, they are + * meaningless and generate bogus resource collision + * messages. This is OpenBoot's ill-fated attempt to + * represent the implicit resources that these devices + * have. + */ + pcp->num_prom_assignments = 0; + for (i = 0; i < 6; i++) { + pdev->resource[i].start = + pdev->resource[i].end = + pdev->resource[i].flags = 0; + } + pdev->resource[PCI_ROM_RESOURCE].start = + pdev->resource[PCI_ROM_RESOURCE].end = + pdev->resource[PCI_ROM_RESOURCE].flags = 0; + return; + } + for (i = 0; i < pcp->num_prom_assignments; i++) { struct linux_prom_pci_registers *ap; int space; @@ -194,7 +220,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, (err / sizeof(pcp->prom_assignments[0])); } - fixup_obp_assignments(pcp); + fixup_obp_assignments(pdev, pcp); pdev->sysdata = pcp; } @@ -316,6 +342,19 @@ __init get_device_resource(struct linux_prom_pci_registers *ap, return res; } +static int __init pdev_resource_collisions_expected(struct pci_dev *pdev) +{ + if (pdev->vendor != PCI_VENDOR_ID_SUN) + return 0; + + if (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS || + pdev->device == PCI_DEVICE_ID_SUN_RIO_1394 || + pdev->device == PCI_DEVICE_ID_SUN_RIO_USB) + return 1; + + return 0; +} + static void __init pdev_record_assignments(struct pci_pbm_info *pbm, struct pci_dev *pdev) { @@ -374,12 +413,17 @@ static void __init pdev_record_assignments(struct pci_pbm_info *pbm, if (request_resource(root, res) < 0) { /* OK, there is some conflict. But this is fine * since we'll reassign it in the fixup pass. - * Nevertheless notify the user that OBP made - * an error. + * + * We notify the user that OBP made an error if it + * is a case we don't expect. */ - printk(KERN_ERR "PCI: Address space collision on region %ld " - "of device %s\n", - (res - &pdev->resource[0]), pdev->name); + if (!pdev_resource_collisions_expected(pdev)) { + printk(KERN_ERR "PCI: Address space collision on region %ld " + "[%016lx:%016lx] of device %s\n", + (res - &pdev->resource[0]), + res->start, res->end, + pdev->name); + } } } } diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index 76dac5695831..4bea4a470e2b 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,4 +1,4 @@ -/* $Id: pci_sabre.c,v 1.33 2001/06/04 23:20:32 ecd Exp $ +/* $Id: pci_sabre.c,v 1.36 2001/06/08 06:25:41 davem Exp $ * pci_sabre.c: Sabre specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -16,6 +16,7 @@ #include #include #include +#include #include "pci_impl.h" @@ -196,7 +197,7 @@ #define SABRE_CONFIGSPACE 0x001000000UL #define SABRE_IOSPACE 0x002000000UL -#define SABRE_IOSPACE_SIZE 0x00000ffffUL +#define SABRE_IOSPACE_SIZE 0x000ffffffUL #define SABRE_MEMSPACE 0x100000000UL #define SABRE_MEMSPACE_SIZE 0x07fffffffUL @@ -1030,8 +1031,8 @@ static void __init sabre_resource_adjust(struct pci_dev *pdev, struct resource *res, struct resource *root) { - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_controller_info *p = pcp->pbm->parent; + struct pci_pbm_info *pbm = pci_bus2pbm[pdev->bus->number]; + struct pci_controller_info *p = pbm->parent; unsigned long base; if (res->flags & IORESOURCE_IO) @@ -1481,7 +1482,7 @@ static void __init sabre_pbm_init(struct pci_controller_info *p, int sabre_node, /* Hack up top-level resources. */ pbm->io_space.start = p->controller_regs + SABRE_IOSPACE; - pbm->io_space.end = pbm->io_space.start + (1UL << 16) - 1UL; + pbm->io_space.end = pbm->io_space.start + (1UL << 24) - 1UL; pbm->io_space.flags = IORESOURCE_IO; pbm->mem_space.start = p->controller_regs + SABRE_MEMSPACE; @@ -1519,8 +1520,19 @@ void __init sabre_init(int pnode, char *model_name) if (prom_getproperty(pnode, "compatible", compat, sizeof(compat)) > 0 && - !strcmp(compat, "pci108e,a001")) + !strcmp(compat, "pci108e,a001")) { hummingbird_p = 1; + } else { + int cpu_node = linux_cpus[0].prom_node; + + /* Of course, Sun has to encode things a thousand + * different ways, inconsistently. + */ + if (prom_getproperty(cpu_node, "name", + compat, sizeof(compat)) > 0 && + !strcmp(compat, "SUNW,UltraSPARC-IIe")) + hummingbird_p = 1; + } } p = kmalloc(sizeof(*p), GFP_ATOMIC); diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index ff111278f55a..35f1cc2d3468 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c @@ -1,4 +1,4 @@ -/* $Id: power.c,v 1.8 2000/07/11 22:41:33 davem Exp $ +/* $Id: power.c,v 1.9 2001/06/08 02:28:22 davem Exp $ * power.c: Power management driver. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -81,6 +81,11 @@ void __init power_init(void) { struct linux_ebus *ebus; struct linux_ebus_device *edev; + static int invoked = 0; + + if (invoked) + return; + invoked = 1; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 952f0caa5a9b..ced3c3482c03 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.54 2001/03/08 22:08:51 davem Exp $ +/* $Id: rtrap.S,v 1.55 2001/06/05 09:56:06 davem Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -88,9 +88,8 @@ rtrap: lduw [%g6 + AOFF_task_processor], %l0 sethi %hi(irq_stat), %l2 ! &softirq_active or %l2, %lo(irq_stat), %l2 ! &softirq_active sllx %l0, 6, %l0 - ldx [%l2 + %l0], %l1 ! softirq_active + softirq_mask - srlx %l1, 32, %l2 - andcc %l1, %l2, %g0 + lduw [%l2 + %l0], %l1 ! softirq_pending + cmp %l1, 0 bne,pn %icc, __handle_softirq ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index f500fc1364f7..d1e750ff4e9f 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.37 2001/04/24 01:09:12 davem Exp $ +/* $Id: time.c,v 1.39 2001/06/08 02:33:37 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -32,6 +32,7 @@ #include #include #include +#include #include extern rwlock_t xtime_lock; @@ -408,7 +409,13 @@ void __init clock_probe(void) unsigned long flags; #ifdef CONFIG_PCI struct linux_ebus *ebus = NULL; + struct isa_bridge *isa_br = NULL; #endif + static int invoked = 0; + + if (invoked) + return; + invoked = 1; if (this_is_starfire) { @@ -433,6 +440,9 @@ void __init clock_probe(void) else if (ebus_chain != NULL) { ebus = ebus_chain; busnd = ebus->prom_node; + } else if (isa_chain != NULL) { + isa_br = isa_chain; + busnd = isa_br->prom_node; } #endif else if (sbus_root != NULL) { @@ -465,6 +475,13 @@ void __init clock_probe(void) node = prom_getchild(busnd); } } + while ((node == 0) && isa_br != NULL) { + isa_br = isa_br->next; + if (isa_br != NULL) { + busnd = isa_br->prom_node; + node = prom_getchild(busnd); + } + } #endif if (node == 0) { prom_printf("clock_probe: Cannot find timer chip\n"); @@ -504,6 +521,22 @@ void __init clock_probe(void) mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; } break; + } else if (isa_chain != NULL) { + struct isa_device *isadev; + + for_each_isadev(isadev, isa_br) + if (isadev->prom_node == node) + break; + if (isadev == NULL) { + prom_printf("%s: Mostek not probed by ISA\n"); + prom_halt(); + } + if (!strcmp(model, "ds1287")) { + ds1287_regs = isadev->resource.start; + } else { + mstk48t59_regs = isadev->resource.start; + mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; + } } #endif else { diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index cf24ef71e68a..e0931afd60a1 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -439,6 +439,7 @@ fore200e_shutdown(struct fore200e* fore200e) case FORE200E_STATE_BLANK: /* nothing to do for that state */ + break; } } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 246c84085ba8..3422c0e563fd 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -859,30 +859,34 @@ end_io: void generic_make_request (int rw, struct buffer_head * bh) { int major = MAJOR(bh->b_rdev); + int minorsize = 0; request_queue_t *q; if (!bh->b_end_io) BUG(); - if (blk_size[major]) { - unsigned long maxsector = (blk_size[major][MINOR(bh->b_rdev)] << 1) + 1; + /* Test device size, when known. */ + if (blk_size[major]) + minorsize = blk_size[major][MINOR(bh->b_rdev)]; + if (minorsize) { + unsigned long maxsector = (minorsize << 1) + 1; unsigned long sector = bh->b_rsector; unsigned int count = bh->b_size >> 9; if (maxsector < count || maxsector - count < sector) { + /* Yecch */ bh->b_state &= (1 << BH_Lock) | (1 << BH_Mapped); - if (blk_size[major][MINOR(bh->b_rdev)]) { - - /* This may well happen - the kernel calls bread() - without checking the size of the device, e.g., - when mounting a device. */ - printk(KERN_INFO - "attempt to access beyond end of device\n"); - printk(KERN_INFO "%s: rw=%d, want=%ld, limit=%d\n", - kdevname(bh->b_rdev), rw, - (sector + count)>>1, - blk_size[major][MINOR(bh->b_rdev)]); - } + + /* This may well happen - the kernel calls bread() + without checking the size of the device, e.g., + when mounting a device. */ + printk(KERN_INFO + "attempt to access beyond end of device\n"); + printk(KERN_INFO "%s: rw=%d, want=%ld, limit=%d\n", + kdevname(bh->b_rdev), rw, + (sector + count)>>1, minorsize); + + /* Yecch again */ bh->b_end_io(bh, 0); return; } @@ -900,7 +904,8 @@ void generic_make_request (int rw, struct buffer_head * bh) q = blk_get_queue(bh->b_rdev); if (!q) { printk(KERN_ERR - "generic_make_request: Trying to access nonexistent block-device %s (%ld)\n", + "generic_make_request: Trying to access " + "nonexistent block-device %s (%ld)\n", kdevname(bh->b_rdev), bh->b_rsector); buffer_IO_error(bh); break; diff --git a/drivers/char/console.c b/drivers/char/console.c index 2b68ebbbeb4e..691e8e017167 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -2682,7 +2682,7 @@ static void blank_screen(unsigned long dummy) void poke_blanked_console(void) { del_timer(&console_timer); /* Can't use _sync here: called from tasklet */ - if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) + if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; if (console_blanked) { console_timer.function = unblank_screen_t; diff --git a/drivers/char/random.c b/drivers/char/random.c index 8558dbc4637a..5737e797daf0 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1793,7 +1793,7 @@ static int uuid_strategy(ctl_table *table, int *name, int nlen, void *newval, size_t newlen, void **context) { unsigned char tmp_uuid[16], *uuid; - int len; + unsigned int len; if (!oldval || !oldlenp) return 1; @@ -1810,7 +1810,7 @@ static int uuid_strategy(ctl_table *table, int *name, int nlen, if (len) { if (len > 16) len = 16; - if (copy_to_user(oldval, table->data, len)) + if (copy_to_user(oldval, uuid, len)) return -EFAULT; if (put_user(len, oldlenp)) return -EFAULT; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index ebbe1ea4afbf..3178bec38647 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -73,6 +73,9 @@ #ifdef __sparc__ #include +#ifdef __sparc_v9__ +#include +#endif static unsigned long rtc_port; static int rtc_irq; @@ -622,22 +625,37 @@ static int __init rtc_init(void) #ifdef __sparc__ struct linux_ebus *ebus; struct linux_ebus_device *edev; +#ifdef __sparc_v9__ + struct isa_bridge *isa_br; + struct isa_device *isa_dev; +#endif #endif #ifdef __sparc__ for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { if(strcmp(edev->prom_name, "rtc") == 0) { + rtc_port = edev->resource[0].start; + rtc_irq = edev->irqs[0]; goto found; } } } +#ifdef __sparc_v9__ + for_each_isa(isa_br) { + for_each_isadev(isa_dev, isa_br) { + if (strcmp(isa_dev->prom_name, "rtc") == 0) { + rtc_port = isa_dev->resource.start; + rtc_irq = isa_dev->irq; + goto found; + } + } + } +#endif printk(KERN_ERR "rtc_init: no PC rtc found\n"); return -EIO; found: - rtc_port = edev->resource[0].start; - rtc_irq = edev->irqs[0]; /* * XXX Interrupt pin #7 in Espresso is shared between RTC and * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index fe48bac47eef..63fb9f4ec934 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -4788,7 +4788,7 @@ static ssize_t idetape_chrdev_write (struct file *file, const char *buf, if (tape->onstream) { if (count != tape->tape_block_size) { - printk(KERN_ERR "ide-tape: %s: chrdev_write: use %d bytes as block size (%d used)\n", + printk(KERN_ERR "ide-tape: %s: chrdev_write: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count); return -EINVAL; } diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c index ab28603340d4..2ccbd29bb440 100644 --- a/drivers/isdn/avmb1/b1.c +++ b/drivers/isdn/avmb1/b1.c @@ -1,114 +1,9 @@ /* - * $Id: b1.c,v 1.20.6.4 2001/04/20 02:41:59 keil Exp $ + * $Id: b1.c,v 1.20.6.6 2001/05/17 21:15:33 kai Exp $ * * Common module for AVM B1 cards. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) - * - * $Log: b1.c,v $ - * Revision 1.20.6.4 2001/04/20 02:41:59 keil - * changes from mainstream - * - * Revision 1.20.6.3 2001/03/21 08:52:20 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.20.6.2 2001/03/15 15:11:23 kai - * *** empty log message *** - * - * Revision 1.20.6.1 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.20 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.19 2000/11/19 17:02:47 kai - * compatibility cleanup - part 3 - * - * Revision 1.18 2000/11/19 17:01:53 kai - * compatibility cleanup - part 2 - * - * Revision 1.17 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.16 2000/08/04 15:36:31 calle - * copied wrong from file to file :-( - * - * Revision 1.15 2000/08/04 12:20:08 calle - * - Fix unsigned/signed warning in the right way ... - * - * Revision 1.14 2000/06/19 16:51:53 keil - * don't free skb in irq context - * - * Revision 1.13 2000/01/25 14:33:38 calle - * - Added Support AVM B1 PCI V4.0 (tested with prototype) - * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 - * - support for revision register - * - * Revision 1.12 1999/11/05 16:38:01 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.11 1999/10/11 22:04:12 keil - * COMPAT_NEED_UACCESS (no include in isdn_compat.h) - * - * Revision 1.10 1999/09/15 08:16:03 calle - * Implementation of 64Bit extention complete. - * - * Revision 1.9 1999/09/07 09:02:53 calle - * SETDATA removed. Now inside the kernel the datapart of DATA_B3_REQ and - * DATA_B3_IND is always directly after the CAPI message. The "Data" member - * ist never used inside the kernel. - * - * Revision 1.8 1999/08/22 20:26:22 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.7 1999/08/04 10:10:09 calle - * Bugfix: corrected /proc functions, added structure for new AVM cards. - * - * Revision 1.6 1999/07/23 08:51:04 calle - * small fix and typo in checkin before. - * - * Revision 1.5 1999/07/23 08:41:48 calle - * prepared for new AVM cards. - * - * Revision 1.4 1999/07/09 15:05:38 keil - * compat.h is now isdn_compat.h - * - * Revision 1.3 1999/07/06 07:41:59 calle - * - changes in /proc interface - * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. - * - * Revision 1.2 1999/07/05 15:09:47 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.1 1999/07/01 15:26:23 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * * */ diff --git a/drivers/isdn/avmb1/b1dma.c b/drivers/isdn/avmb1/b1dma.c index 6460d068b1cf..521c79cb7e96 100644 --- a/drivers/isdn/avmb1/b1dma.c +++ b/drivers/isdn/avmb1/b1dma.c @@ -1,59 +1,10 @@ /* - * $Id: b1dma.c,v 1.11.6.4 2001/04/20 02:41:59 keil Exp $ + * $Id: b1dma.c,v 1.11.6.6 2001/05/17 21:15:33 kai Exp $ * * Common module for AVM B1 cards that support dma with AMCC * * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: b1dma.c,v $ - * Revision 1.11.6.4 2001/04/20 02:41:59 keil - * changes from mainstream - * - * Revision 1.11.6.3 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.11.6.2 2001/03/15 15:11:23 kai - * *** empty log message *** - * - * Revision 1.11.6.1 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.11 2000/11/19 17:02:47 kai - * compatibility cleanup - part 3 - * - * Revision 1.10 2000/11/19 17:01:53 kai - * compatibility cleanup - part 2 - * - * Revision 1.9 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.8 2000/10/10 17:44:19 kai - * changes from/for 2.2.18 - * - * Revision 1.7 2000/08/04 12:20:08 calle - * - Fix unsigned/signed warning in the right way ... - * - * Revision 1.6 2000/06/29 13:59:06 calle - * Bugfix: reinit txdma without interrupt will confuse some AMCC chips. - * - * Revision 1.5 2000/06/19 16:51:53 keil - * don't free skb in irq context - * - * Revision 1.4 2000/04/03 16:38:05 calle - * made suppress_pollack static. - * - * Revision 1.3 2000/02/26 01:00:53 keil - * changes from 2.3.47 - * - * Revision 1.2 2000/01/25 14:44:47 calle - * typo in b1pciv4_detect(). - * - * Revision 1.1 2000/01/25 14:36:43 calle - * common function for T1 PCI and B1 PCI V4. - * - * */ #include diff --git a/drivers/isdn/avmb1/b1isa.c b/drivers/isdn/avmb1/b1isa.c index edb37a532ab1..bd241e648128 100644 --- a/drivers/isdn/avmb1/b1isa.c +++ b/drivers/isdn/avmb1/b1isa.c @@ -1,83 +1,10 @@ /* - * $Id: b1isa.c,v 1.10.6.4 2001/03/21 08:52:21 kai Exp $ + * $Id: b1isa.c,v 1.10.6.5 2001/05/17 20:41:51 kai Exp $ * * Module for AVM B1 ISA-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: b1isa.c,v $ - * Revision 1.10.6.4 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.10.6.3 2001/03/15 15:11:23 kai - * *** empty log message *** - * - * Revision 1.10.6.2 2001/02/16 16:43:23 kai - * Changes from -ac16, little bug fixes, typos and the like - * - * Revision 1.10.6.1 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.10 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.9 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.8 2000/04/03 13:29:24 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.7 2000/02/02 18:36:03 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.6 2000/01/25 14:37:39 calle - * new message after successful detection including card revision and - * used resources. - * - * Revision 1.5 1999/11/05 16:38:01 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.4 1999/08/22 20:26:24 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.3 1999/07/09 15:05:40 keil - * compat.h is now isdn_compat.h - * - * Revision 1.2 1999/07/05 15:09:49 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.1 1999/07/01 15:26:27 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * - * */ #include diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index 5576f69b39bc..34d1a630f2f3 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,111 +1,10 @@ /* - * $Id: b1pci.c,v 1.29.6.3 2001/04/20 02:41:59 keil Exp $ + * $Id: b1pci.c,v 1.29.6.4 2001/05/17 20:41:51 kai Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: b1pci.c,v $ - * Revision 1.29.6.3 2001/04/20 02:41:59 keil - * changes from mainstream - * - * Revision 1.29.6.2 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.29.6.1 2000/11/28 12:02:45 kai - * MODULE_DEVICE_TABLE for 2.4 - * - * Revision 1.29.2.2 2000/11/26 17:47:53 kai - * added PCI_DEV_TABLE for 2.4 - * - * Revision 1.29.2.1 2000/11/26 17:14:19 kai - * fix device ids - * also needs patches to include/linux/pci_ids.h - * - * Revision 1.29 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.28 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.27 2000/08/08 09:24:19 calle - * calls to pci_enable_device surounded by #ifndef COMPAT_HAS_2_2_PCI - * - * Revision 1.26 2000/07/20 10:21:21 calle - * Bugfix: driver will not be unregistered, if not cards were detected. - * this result in an oops in kcapi.c - * - * Revision 1.25 2000/05/29 12:29:18 keil - * make pci_enable_dev compatible to 2.2 kernel versions - * - * Revision 1.24 2000/05/19 15:43:22 calle - * added calls to pci_device_start(). - * - * Revision 1.23 2000/05/06 00:52:36 kai - * merged changes from kernel tree - * fixed timer and net_device->name breakage - * - * Revision 1.22 2000/04/21 13:01:33 calle - * Revision in b1pciv4 driver was missing. - * - * Revision 1.21 2000/04/03 13:29:24 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.20 2000/02/02 18:36:03 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.19 2000/01/25 14:33:38 calle - * - Added Support AVM B1 PCI V4.0 (tested with prototype) - * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 - * - support for revision register - * - * Revision 1.18 1999/11/05 16:38:01 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.17 1999/10/05 06:50:07 calle - * Forgot SA_SHIRQ as argument to request_irq. - * - * Revision 1.16 1999/08/11 21:01:07 keil - * new PCI codefix - * - * Revision 1.15 1999/08/10 16:02:27 calle - * struct pci_dev changed in 2.3.13. Made the necessary changes. - * - * Revision 1.14 1999/07/09 15:05:41 keil - * compat.h is now isdn_compat.h - * - * Revision 1.13 1999/07/05 15:09:50 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.12 1999/07/01 15:26:29 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * - * */ #include diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c index f13e367abe9f..09b33ed32e7c 100644 --- a/drivers/isdn/avmb1/b1pcmcia.c +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -1,89 +1,10 @@ /* - * $Id: b1pcmcia.c,v 1.12.6.3 2001/03/21 08:52:21 kai Exp $ + * $Id: b1pcmcia.c,v 1.12.6.4 2001/05/17 20:41:51 kai Exp $ * * Module for AVM B1/M1/M2 PCMCIA-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: b1pcmcia.c,v $ - * Revision 1.12.6.3 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.12.6.2 2001/02/16 16:43:23 kai - * Changes from -ac16, little bug fixes, typos and the like - * - * Revision 1.12.6.1 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.12 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.11 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.10 2000/05/06 00:52:36 kai - * merged changes from kernel tree - * fixed timer and net_device->name breakage - * - * Revision 1.9 2000/04/03 13:29:24 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.8 2000/03/06 18:00:23 calle - * - Middleware extention now working with 2.3.49 (capifs). - * - Fixed typos in debug section of capi.c - * - Bugfix: Makefile corrected for b1pcmcia.c - * - * Revision 1.7 2000/02/02 18:36:03 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.6 2000/01/25 14:37:39 calle - * new message after successful detection including card revision and - * used resources. - * - * Revision 1.5 1999/11/05 16:38:01 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.4 1999/08/22 20:26:26 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.3 1999/07/09 15:05:41 keil - * compat.h is now isdn_compat.h - * - * Revision 1.2 1999/07/05 15:09:51 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.1 1999/07/01 15:26:30 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * - * */ #include diff --git a/drivers/isdn/avmb1/c4.c b/drivers/isdn/avmb1/c4.c index 4f1ce0fd1615..2bd93d450a31 100644 --- a/drivers/isdn/avmb1/c4.c +++ b/drivers/isdn/avmb1/c4.c @@ -1,104 +1,9 @@ /* - * $Id: c4.c,v 1.20.6.6 2001/04/20 02:41:59 keil Exp $ + * $Id: c4.c,v 1.20.6.8 2001/05/17 21:15:33 kai Exp $ * - * Module for AVM C4 card. + * Module for AVM C4 & C2 card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) - * - * $Log: c4.c,v $ - * Revision 1.20.6.6 2001/04/20 02:41:59 keil - * changes from mainstream - * - * Revision 1.20.6.5 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.20.6.4 2001/03/15 15:11:23 kai - * *** empty log message *** - * - * Revision 1.20.6.3 2001/02/16 16:43:23 kai - * Changes from -ac16, little bug fixes, typos and the like - * - * Revision 1.20.6.2 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.20.6.1 2000/11/28 12:02:45 kai - * MODULE_DEVICE_TABLE for 2.4 - * - * Revision 1.20.2.2 2000/11/26 17:47:53 kai - * added PCI_DEV_TABLE for 2.4 - * - * Revision 1.20.2.1 2000/11/26 17:14:19 kai - * fix device ids - * also needs patches to include/linux/pci_ids.h - * - * Revision 1.20 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.19 2000/11/19 17:02:47 kai - * compatibility cleanup - part 3 - * - * Revision 1.18 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.17 2000/10/10 17:44:19 kai - * changes from/for 2.2.18 - * - * Revision 1.16 2000/08/20 07:30:13 keil - * changes for 2.4 - * - * Revision 1.15 2000/08/08 09:24:19 calle - * calls to pci_enable_device surounded by #ifndef COMPAT_HAS_2_2_PCI - * - * Revision 1.14 2000/08/04 12:20:08 calle - * - Fix unsigned/signed warning in the right way ... - * - * Revision 1.13 2000/07/20 10:21:21 calle - * Bugfix: driver will not be unregistered, if not cards were detected. - * this result in an oops in kcapi.c - * - * Revision 1.12 2000/06/19 16:51:53 keil - * don't free skb in irq context - * - * Revision 1.11 2000/06/19 15:11:24 keil - * avoid use of freed structs - * changes from 2.4.0-ac21 - * - * Revision 1.10 2000/05/29 12:29:18 keil - * make pci_enable_dev compatible to 2.2 kernel versions - * - * Revision 1.9 2000/05/19 15:43:22 calle - * added calls to pci_device_start(). - * - * Revision 1.8 2000/04/03 16:38:05 calle - * made suppress_pollack static. - * - * Revision 1.7 2000/04/03 13:29:24 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.6 2000/03/17 12:21:08 calle - * send patchvalues now working. - * - * Revision 1.5 2000/03/16 15:21:03 calle - * Bugfix in c4_remove: loop 5 times instead of 4 :-( - * - * Revision 1.4 2000/02/02 18:36:03 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.3 2000/01/25 14:37:39 calle - * new message after successful detection including card revision and - * used resources. - * - * Revision 1.2 2000/01/21 20:52:58 keil - * pci_find_subsys as local function for 2.2.X kernel - * - * Revision 1.1 2000/01/20 10:51:37 calle - * Added driver for C4. - * * */ @@ -122,15 +27,11 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.20.6.8 $"; +static char *revision = "$Revision: 1.20.6.9 $"; #undef CONFIG_C4_DEBUG #undef CONFIG_C4_POLLDEBUG -/* ------------------------------------------------------------- */ -#ifndef PCI_DEVICE_ID_AVM_C2 -#define PCI_DEVICE_ID_AVM_C2 0x1100 -#endif /* ------------------------------------------------------------- */ static int suppress_pollack; diff --git a/drivers/isdn/avmb1/capicmd.h b/drivers/isdn/avmb1/capicmd.h index 970365f7a168..d9566f10d603 100644 --- a/drivers/isdn/avmb1/capicmd.h +++ b/drivers/isdn/avmb1/capicmd.h @@ -1,32 +1,10 @@ /* - * $Id: capicmd.h,v 1.2 2000/03/03 15:50:42 calle Exp $ + * $Id: capicmd.h,v 1.2.6.1 2001/05/17 20:41:51 kai Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: capicmd.h,v $ - * Revision 1.2 2000/03/03 15:50:42 calle - * - kernel CAPI: - * - Changed parameter "param" in capi_signal from __u32 to void *. - * - rewrote notifier handling in kcapi.c - * - new notifier NCCI_UP and NCCI_DOWN - * - User CAPI: - * - /dev/capi20 is now a cloning device. - * - middleware extentions prepared. - * - capidrv.c - * - locking of list operations and module count updates. - * - * Revision 1.1 1997/03/04 21:50:30 calle - * Frirst version in isdn4linux - * - * Revision 2.2 1997/02/12 09:31:39 calle - * new version - * - * Revision 1.1 1997/01/31 10:32:20 calle - * Initial revision - * - * */ #ifndef __CAPICMD_H__ #define __CAPICMD_H__ diff --git a/drivers/isdn/avmb1/capifs.c b/drivers/isdn/avmb1/capifs.c index e3079b400a73..4e611ab62b3a 100644 --- a/drivers/isdn/avmb1/capifs.c +++ b/drivers/isdn/avmb1/capifs.c @@ -1,10 +1,10 @@ /* * $Id: capifs.c,v 1.14.6.7 2001/05/24 08:29:08 kai Exp $ - * + * * (c) Copyright 2000 by Carsten Paeth (calle@calle.de) * * Heavily based on devpts filesystem from H. Peter Anvin - * + * */ #include diff --git a/drivers/isdn/avmb1/capifs.h b/drivers/isdn/avmb1/capifs.h index f6b8072ed3e4..8ee489e3ac9a 100644 --- a/drivers/isdn/avmb1/capifs.h +++ b/drivers/isdn/avmb1/capifs.h @@ -1,24 +1,8 @@ /* - * $Id: capifs.h,v 1.2 2000/03/08 17:06:33 calle Exp $ + * $Id: capifs.h,v 1.2.6.1 2001/05/17 20:41:51 kai Exp $ * * (c) Copyright 2000 by Carsten Paeth (calle@calle.de) * - * $Log: capifs.h,v $ - * Revision 1.2 2000/03/08 17:06:33 calle - * - changes for devfs and 2.3.49 - * - capifs now configurable (no need with devfs) - * - New Middleware ioctl CAPI_NCCI_GETUNIT - * - Middleware again tested with 2.2.14 and 2.3.49 (with and without devfs) - * - * Revision 1.1 2000/03/03 16:48:38 calle - * - Added CAPI2.0 Middleware support (CONFIG_ISDN_CAPI) - * It is now possible to create a connection with a CAPI2.0 applikation - * and than to handle the data connection from /dev/capi/ (capifs) and also - * using async or sync PPP on this connection. - * The two major device number 190 and 191 are not confirmed yet, - * but I want to save the code in cvs, before I go on. - * - * */ void capifs_new_ncci(char type, unsigned int num, kdev_t device); diff --git a/drivers/isdn/avmb1/capiutil.h b/drivers/isdn/avmb1/capiutil.h index 8435ede452c9..ab7f1ceb72dc 100644 --- a/drivers/isdn/avmb1/capiutil.h +++ b/drivers/isdn/avmb1/capiutil.h @@ -1,46 +1,11 @@ /* - * $Id: capiutil.h,v 1.5 2000/03/03 15:50:42 calle Exp $ + * $Id: capiutil.h,v 1.5.6.1 2001/05/17 20:41:51 kai Exp $ * * CAPI 2.0 defines & types * * From CAPI 2.0 Development Kit AVM 1995 (capi20.h) * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: capiutil.h,v $ - * Revision 1.5 2000/03/03 15:50:42 calle - * - kernel CAPI: - * - Changed parameter "param" in capi_signal from __u32 to void *. - * - rewrote notifier handling in kcapi.c - * - new notifier NCCI_UP and NCCI_DOWN - * - User CAPI: - * - /dev/capi20 is now a cloning device. - * - middleware extentions prepared. - * - capidrv.c - * - locking of list operations and module count updates. - * - * Revision 1.4 1999/09/15 08:16:03 calle - * Implementation of 64Bit extention complete. - * - * Revision 1.3 1999/09/07 09:02:53 calle - * SETDATA removed. Now inside the kernel the datapart of DATA_B3_REQ and - * DATA_B3_IND is always directly after the CAPI message. The "Data" member - * ist never used inside the kernel. - * - * Revision 1.2 1997/05/18 09:24:19 calle - * added verbose disconnect reason reporting to avmb1. - * some fixes in capi20 interface. - * changed info messages for B1-PCI - * - * Revision 1.1 1997/03/04 21:50:35 calle - * Frirst version in isdn4linux - * - * Revision 2.2 1997/02/12 09:31:39 calle - * new version - * - * Revision 1.1 1997/01/31 10:32:20 calle - * Initial revision - * - * */ #ifndef __CAPIUTIL_H__ #define __CAPIUTIL_H__ diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c index 544e9939b944..704b7d347c56 100644 --- a/drivers/isdn/avmb1/kcapi.c +++ b/drivers/isdn/avmb1/kcapi.c @@ -1,128 +1,10 @@ /* - * $Id: kcapi.c,v 1.21.6.5 2001/03/21 08:52:21 kai Exp $ + * $Id: kcapi.c,v 1.21.6.6 2001/05/17 20:41:51 kai Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: kcapi.c,v $ - * Revision 1.21.6.5 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.21.6.4 2001/03/15 15:11:24 kai - * *** empty log message *** - * - * Revision 1.21.6.3 2001/03/13 16:17:08 kai - * spelling fixes from 2.4.3-pre - * - * Revision 1.21.6.2 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.21.6.1 2000/12/10 23:39:19 kai - * in 2.4 we don't have tq_scheduler anymore. - * also add one supported card to hfc_pci.c - * (from main branch) - * - * Revision 1.21 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.20 2000/11/19 17:01:53 kai - * compatibility cleanup - part 2 - * - * Revision 1.19 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.18 2000/07/20 10:22:27 calle - * - Made procfs function cleaner and removed variable "begin". - * - * Revision 1.17 2000/04/21 13:00:56 calle - * Bugfix: driver_proc_info was also wrong. - * - * Revision 1.16 2000/04/21 12:38:42 calle - * Bugfix: error in proc_ functions, begin-off => off-begin - * - * Revision 1.15 2000/04/06 15:01:25 calle - * Bugfix: crash in capidrv.c when reseting a capi controller. - * - changed code order on remove of controller. - * - using tq_schedule for notifier in kcapi.c. - * - now using spin_lock_irqsave() and spin_unlock_irqrestore(). - * strange: sometimes even MP hang on unload of isdn.o ... - * - * Revision 1.14 2000/04/03 13:29:25 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.13 2000/03/03 15:50:42 calle - * - kernel CAPI: - * - Changed parameter "param" in capi_signal from __u32 to void *. - * - rewrote notifier handling in kcapi.c - * - new notifier NCCI_UP and NCCI_DOWN - * - User CAPI: - * - /dev/capi20 is now a cloning device. - * - middleware extentions prepared. - * - capidrv.c - * - locking of list operations and module count updates. - * - * Revision 1.12 2000/01/28 16:45:39 calle - * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard), - * will search named driver and call the add_card function if one exist. - * - * Revision 1.11 1999/11/23 13:29:29 calle - * Bugfix: incoming capi message were never traced. - * - * Revision 1.10 1999/10/26 15:30:32 calle - * Generate error message if user want to add card, but driver module is - * not loaded. - * - * Revision 1.9 1999/10/11 22:04:12 keil - * COMPAT_NEED_UACCESS (no include in isdn_compat.h) - * - * Revision 1.8 1999/09/10 17:24:18 calle - * Changes for proposed standard for CAPI2.0: - * - AK148 "Linux Exention" - * - * Revision 1.7 1999/09/04 06:20:05 keil - * Changes from kernel set_current_state() - * - * Revision 1.6 1999/07/20 06:41:49 calle - * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even - * compile, if not selected as modules. - * - * Revision 1.5 1999/07/09 15:05:48 keil - * compat.h is now isdn_compat.h - * - * Revision 1.4 1999/07/08 14:15:17 calle - * Forgot to count down ncards in drivercb_detach_ctr. - * - * Revision 1.3 1999/07/06 07:42:02 calle - * - changes in /proc interface - * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. - * - * Revision 1.2 1999/07/05 15:09:52 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.1 1999/07/01 15:26:42 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * */ #define CONFIG_AVMB1_COMPAT diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c index 9004e4df50f2..a655763a617d 100644 --- a/drivers/isdn/avmb1/t1isa.c +++ b/drivers/isdn/avmb1/t1isa.c @@ -1,104 +1,10 @@ /* - * $Id: t1isa.c,v 1.16.6.4 2001/03/21 08:52:21 kai Exp $ + * $Id: t1isa.c,v 1.16.6.6 2001/05/17 21:15:33 kai Exp $ * * Module for AVM T1 HEMA-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: t1isa.c,v $ - * Revision 1.16.6.4 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.16.6.3 2001/03/15 15:11:24 kai - * *** empty log message *** - * - * Revision 1.16.6.2 2001/02/16 16:43:24 kai - * Changes from -ac16, little bug fixes, typos and the like - * - * Revision 1.16.6.1 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.16 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.15 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.14 2000/10/10 17:44:19 kai - * changes from/for 2.2.18 - * - * Revision 1.13 2000/08/04 15:36:31 calle - * copied wrong from file to file :-( - * - * Revision 1.12 2000/08/04 12:20:08 calle - * - Fix unsigned/signed warning in the right way ... - * - * Revision 1.11 2000/04/03 13:29:25 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.10 2000/02/02 18:36:04 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.9 2000/01/25 14:37:39 calle - * new message after successful detection including card revision and - * used resources. - * - * Revision 1.8 1999/11/05 16:38:01 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.7 1999/09/15 08:16:03 calle - * Implementation of 64Bit extention complete. - * - * Revision 1.6 1999/09/07 09:02:53 calle - * SETDATA removed. Now inside the kernel the datapart of DATA_B3_REQ and - * DATA_B3_IND is always directly after the CAPI message. The "Data" member - * ist never used inside the kernel. - * - * Revision 1.5 1999/08/22 20:26:28 calle - * backported changes from kernel 2.3.14: - * - several #include "config.h" gone, others come. - * - "struct device" changed to "struct net_device" in 2.3.14, added a - * define in isdn_compat.h for older kernel versions. - * - * Revision 1.4 1999/07/09 15:05:50 keil - * compat.h is now isdn_compat.h - * - * Revision 1.3 1999/07/06 07:42:04 calle - * - changes in /proc interface - * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. - * - * Revision 1.2 1999/07/05 15:09:54 calle - * - renamed "appl_release" to "appl_released". - * - version und profile data now cleared on controller reset - * - extended /proc interface, to allow driver and controller specific - * informations to include by driver hackers. - * - * Revision 1.1 1999/07/01 15:26:44 calle - * complete new version (I love it): - * + new hardware independed "capi_driver" interface that will make it easy to: - * - support other controllers with CAPI-2.0 (i.e. USB Controller) - * - write a CAPI-2.0 for the passive cards - * - support serial link CAPI-2.0 boxes. - * + wrote "capi_driver" for all supported cards. - * + "capi_driver" (supported cards) now have to be configured with - * make menuconfig, in the past all supported cards where included - * at once. - * + new and better informations in /proc/capi/ - * + new ioctl to switch trace of capi messages per controller - * using "avmcapictrl trace [contr] on|off|...." - * + complete testcircle with all supported cards and also the - * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. - * - * */ #include diff --git a/drivers/isdn/avmb1/t1pci.c b/drivers/isdn/avmb1/t1pci.c index ea46151abdc8..71fa25326447 100644 --- a/drivers/isdn/avmb1/t1pci.c +++ b/drivers/isdn/avmb1/t1pci.c @@ -1,80 +1,10 @@ /* - * $Id: t1pci.c,v 1.13.6.3 2001/03/21 08:52:21 kai Exp $ + * $Id: t1pci.c,v 1.13.6.5 2001/05/17 20:41:51 kai Exp $ * * Module for AVM T1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * - * $Log: t1pci.c,v $ - * Revision 1.13.6.3 2001/03/21 08:52:21 kai - * merge from main branch: fix buffer for revision string (calle) - * - * Revision 1.13.6.2 2001/02/13 11:43:29 kai - * more compatility changes for 2.2.19 - * - * Revision 1.13.6.1 2000/11/28 12:02:45 kai - * MODULE_DEVICE_TABLE for 2.4 - * - * Revision 1.13.2.2 2000/11/26 17:47:53 kai - * added PCI_DEV_TABLE for 2.4 - * - * Revision 1.13.2.1 2000/11/26 17:14:19 kai - * fix device ids - * also needs patches to include/linux/pci_ids.h - * - * Revision 1.13 2000/11/23 20:45:14 kai - * fixed module_init/exit stuff - * Note: compiled-in kernel doesn't work pre 2.2.18 anymore. - * - * Revision 1.12 2000/11/01 14:05:02 calle - * - use module_init/module_exit from linux/init.h. - * - all static struct variables are initialized with "membername:" now. - * - avm_cs.c, let it work with newer pcmcia-cs. - * - * Revision 1.11 2000/08/08 09:24:19 calle - * calls to pci_enable_device surounded by #ifndef COMPAT_HAS_2_2_PCI - * - * Revision 1.10 2000/07/20 10:21:21 calle - * Bugfix: driver will not be unregistered, if not cards were detected. - * this result in an oops in kcapi.c - * - * Revision 1.9 2000/05/19 15:43:22 calle - * added calls to pci_device_start(). - * - * Revision 1.8 2000/05/06 00:52:36 kai - * merged changes from kernel tree - * fixed timer and net_device->name breakage - * - * Revision 1.7 2000/04/07 15:26:55 calle - * better error message if cabel not connected or T1 has no power. - * - * Revision 1.6 2000/04/03 13:29:25 calle - * make Tim Waugh happy (module unload races in 2.3.99-pre3). - * no real problem there, but now it is much cleaner ... - * - * Revision 1.5 2000/02/02 18:36:04 calle - * - Modules are now locked while init_module is running - * - fixed problem with memory mapping if address is not aligned - * - * Revision 1.4 2000/01/25 14:33:38 calle - * - Added Support AVM B1 PCI V4.0 (tested with prototype) - * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 - * - support for revision register - * - * Revision 1.3 1999/11/13 21:27:16 keil - * remove KERNELVERSION - * - * Revision 1.2 1999/11/05 16:38:02 calle - * Cleanups before kernel 2.4: - * - Changed all messages to use card->name or driver->name instead of - * constant string. - * - Moved some data from struct avmcard into new struct avmctrl_info. - * Changed all lowlevel capi driver to match the new structur. - * - * Revision 1.1 1999/10/26 15:31:42 calle - * Added driver for T1-PCI card. - * - * */ #include diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index c499b6f8dc07..d3de5bd4eaa9 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.51.6.2 2001/03/13 16:17:08 kai Exp $ +/* $Id: callc.c,v 2.51.6.3 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -20,7 +20,7 @@ #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.51.6.2 $"; +const char *lli_revision = "$Revision: 2.51.6.3 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -850,14 +850,14 @@ static struct FsmNode fnlist[] __initdata = #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) -void __init +int __init CallcNew(void) { callcfsm.state_count = STATE_COUNT; callcfsm.event_count = EVENT_COUNT; callcfsm.strEvent = strEvent; callcfsm.strState = strState; - FsmNew(&callcfsm, fnlist, FNCOUNT); + return FsmNew(&callcfsm, fnlist, FNCOUNT); } void diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 744f93b8ca2e..9a8d573d3848 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,4 +1,4 @@ -/* $Id: config.c,v 2.57.6.13 2001/04/08 19:41:28 kai Exp $ +/* $Id: config.c,v 2.57.6.14 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -1332,15 +1332,28 @@ HiSax_reportcard(int cardnr, int sel) static int __init HiSax_init(void) { - int i,j; + int i, retval; +#ifdef MODULE + int j; int nzproto = 0; +#endif HiSaxVersion(); - CallcNew(); - Isdnl3New(); - Isdnl2New(); - TeiNew(); - Isdnl1New(); + retval = CallcNew(); + if (retval) + goto out; + retval = Isdnl3New(); + if (retval) + goto out_callc; + retval = Isdnl2New(); + if (retval) + goto out_isdnl3; + retval = TeiNew(); + if (retval) + goto out_isdnl2; + retval = Isdnl1New(); + if (retval) + goto out_tei; #ifdef MODULE if (!type[0]) { @@ -1487,17 +1500,26 @@ static int __init HiSax_init(void) printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", nrcards, (nrcards > 1) ? "s" : ""); - if (HiSax_inithardware(NULL)) { - /* Install only, if at least one card found */ - return (0); - } else { - Isdnl1Free(); - TeiFree(); - Isdnl2Free(); - Isdnl3Free(); - CallcFree(); - return -EIO; + /* Install only, if at least one card found */ + if (!HiSax_inithardware(NULL)) { + retval = -EIO; + goto out_isdnl1; } + + return 0; + + out_isdnl1: + Isdnl1Free(); + out_tei: + TeiFree(); + out_isdnl2: + Isdnl2Free(); + out_isdnl3: + Isdnl3Free(); + out_callc: + CallcFree(); + out: + return retval; } static void __exit HiSax_exit(void) diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index 7b830ff72f59..d1f385ecaf50 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,4 +1,4 @@ -/* $Id: fsm.c,v 1.14.6.1 2001/02/16 16:43:26 kai Exp $ +/* $Id: fsm.c,v 1.14.6.2 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -15,13 +15,16 @@ #define FSM_TIMER_DEBUG 0 -void __init +int __init FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) { int i; fsm->jumpmatrix = (FSMFNPTR *) kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + if (!fsm->jumpmatrix) + return -ENOMEM; + memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); for (i = 0; i < fncount; i++) @@ -32,6 +35,7 @@ FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) } else fsm->jumpmatrix[fsm->state_count * fnlist[i].event + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; + return 0; } void diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index 72b28e666dc7..ef41f52393d0 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -1,4 +1,4 @@ -/* $Id: gazel.c,v 2.11.6.4 2001/02/16 16:43:26 kai Exp $ +/* $Id: gazel.c,v 2.11.6.6 2001/06/08 08:48:46 kai Exp $ * * gazel.c low level stuff for Gazel isdn cards * @@ -19,7 +19,7 @@ #include extern const char *CardType[]; -const char *gazel_revision = "$Revision: 2.11.6.4 $"; +const char *gazel_revision = "$Revision: 2.11.6.6 $"; #define R647 1 #define R685 2 @@ -439,10 +439,6 @@ static int reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs) { unsigned int i, base = 0, adr = 0, len = 0; - long flags; - - save_flags(flags); - cli(); switch (cs->subtyp) { case R647: @@ -487,17 +483,15 @@ reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs) break; } - restore_flags(flags); return 0; error: - restore_flags(flags); printk(KERN_WARNING "Gazel: %s io ports 0x%x-0x%x already in use\n", CardType[cs->typ], adr, adr + len); return 1; } -static int +static int __init setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) { printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n"); @@ -546,7 +540,7 @@ setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) static struct pci_dev *dev_tel __initdata = NULL; -static int +static int __init setup_gazelpci(struct IsdnCardState *cs) { u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0; diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index a5d06751b05c..f8e61f1fef2f 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,4 +1,4 @@ -/* $Id: hisax.h,v 2.52.6.4 2001/04/08 19:32:26 kai Exp $ +/* $Id: hisax.h,v 2.52.6.5 2001/05/26 15:19:57 kai Exp $ * * Basic declarations, defines and prototypes * @@ -1304,7 +1304,7 @@ u_char *findie(u_char * p, int size, u_char ie, int wanted_set); int getcallref(u_char * p); int newcallref(void); -void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); +int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); void FsmFree(struct Fsm *fsm); int FsmEvent(struct FsmInst *fi, int event, void *arg); void FsmChangeState(struct FsmInst *fi, int newstate); @@ -1335,19 +1335,19 @@ void setstack_isac(struct PStack *st, struct IsdnCardState *cs); int ll_run(struct IsdnCardState *cs, int addfeatures); void ll_stop(struct IsdnCardState *cs); -void CallcNew(void); +int CallcNew(void); void CallcFree(void); int CallcNewChan(struct IsdnCardState *cs); void CallcFreeChan(struct IsdnCardState *cs); -void Isdnl1New(void); +int Isdnl1New(void); void Isdnl1Free(void); -void Isdnl2New(void); +int Isdnl2New(void); void Isdnl2Free(void); -void Isdnl3New(void); +int Isdnl3New(void); void Isdnl3Free(void); void init_tei(struct IsdnCardState *cs, int protocol); void release_tei(struct IsdnCardState *cs); char *HiSax_getrev(const char *revision); -void TeiNew(void); +int TeiNew(void); void TeiFree(void); int certification_check(int output); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 3d02ba76c42b..3e3427575f11 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 2.41.6.2 2001/02/16 16:43:27 kai Exp $ +/* $Id: isdnl1.c,v 2.41.6.3 2001/05/26 15:19:57 kai Exp $ * * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -15,7 +15,7 @@ * */ -const char *l1_revision = "$Revision: 2.41.6.2 $"; +const char *l1_revision = "$Revision: 2.41.6.3 $"; #define __NO_VERSION__ #include @@ -736,26 +736,41 @@ static struct FsmNode L1BFnList[] __initdata = #define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode)) -void __init +int __init Isdnl1New(void) { -#ifdef HISAX_UINTERFACE - l1fsm_u.state_count = L1U_STATE_COUNT; - l1fsm_u.event_count = L1_EVENT_COUNT; - l1fsm_u.strEvent = strL1Event; - l1fsm_u.strState = strL1UState; - FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT); -#endif + int retval; + l1fsm_s.state_count = L1S_STATE_COUNT; l1fsm_s.event_count = L1_EVENT_COUNT; l1fsm_s.strEvent = strL1Event; l1fsm_s.strState = strL1SState; - FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT); + retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT); + if (retval) + return retval; + l1fsm_b.state_count = L1B_STATE_COUNT; l1fsm_b.event_count = L1_EVENT_COUNT; l1fsm_b.strEvent = strL1Event; l1fsm_b.strState = strL1BState; - FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT); + retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT); + if (retval) { + FsmFree(&l1fsm_s); + return retval; + } +#ifdef HISAX_UINTERFACE + l1fsm_u.state_count = L1U_STATE_COUNT; + l1fsm_u.event_count = L1_EVENT_COUNT; + l1fsm_u.strEvent = strL1Event; + l1fsm_u.strState = strL1UState; + retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT); + if (retval) { + FsmFree(&l1fsm_s); + FsmFree(&l1fsm_b); + return retval; + } +#endif + return 0; } void Isdnl1Free(void) diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index a586b3b4eee4..cee558c4f575 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 2.25.6.1 2001/02/16 16:43:27 kai Exp $ +/* $Id: isdnl2.c,v 2.25.6.2 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -16,7 +16,7 @@ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 2.25.6.1 $"; +const char *l2_revision = "$Revision: 2.25.6.2 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); @@ -1831,14 +1831,14 @@ releasestack_transl2(struct PStack *st) { } -void __init +int __init Isdnl2New(void) { l2fsm.state_count = L2_STATE_COUNT; l2fsm.event_count = L2_EVENT_COUNT; l2fsm.strEvent = strL2Event; l2fsm.strState = strL2State; - FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); + return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); } void diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index d3e5f7965b11..0ecd9cfb33be 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 2.17.6.2 2001/02/16 16:43:27 kai Exp $ +/* $Id: isdnl3.c,v 2.17.6.3 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -18,7 +18,7 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 2.17.6.2 $"; +const char *l3_revision = "$Revision: 2.17.6.3 $"; static struct Fsm l3fsm; @@ -591,14 +591,14 @@ l3_msg(struct PStack *st, int pr, void *arg) } } -void __init +int __init Isdnl3New(void) { l3fsm.state_count = L3_STATE_COUNT; l3fsm.event_count = L3_EVENT_COUNT; l3fsm.strEvent = strL3Event; l3fsm.strState = strL3State; - FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); + return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); } void diff --git a/drivers/isdn/hisax/md5sums.asc b/drivers/isdn/hisax/md5sums.asc index b1ee8e4f000b..b7990826edb5 100644 --- a/drivers/isdn/hisax/md5sums.asc +++ b/drivers/isdn/hisax/md5sums.asc @@ -8,11 +8,11 @@ # Read ../../../Documentation/isdn/HiSax.cert for more informations. # 9663cc9f4374c361197d394f6d27c459 isac.c -9666c672c0fa0e65d5cc5b322f10a18c isdnl1.c -9250f15b932dfc36855aa120b896ed0b isdnl2.c -0cc2ef892bdb4a2be473e00eb1398950 isdnl3.c -cac9c32fff889c57ff50d59823053019 tei.c -665044a72334336533ac79da1a831b17 callc.c +13c3eed869f5139f44c563e3a8fea1f5 isdnl1.c +64dcc220ae42fe9e4bbc664c9525bd0a isdnl2.c +700aa997e04f5e7e0d32f0d60c8e7fa9 isdnl3.c +51c603829b6cc4f8421f744ad657ceff tei.c +144d646162d83bb5b99095917d131865 callc.c e592db58630c1f1029cc064110108156 cert.c fadeb3b85bb23bc1ac48470c0848d6fa l3dss1.c cf7dec9fac6283716904d26b99188476 l3_1tr6.c @@ -22,12 +22,12 @@ b4cf8a4dceed9ea6dcba65a85b4eecc7 diva.c # end of md5sums -----BEGIN PGP SIGNATURE----- -Version: 2.6.3i +Version: 2.6.3in Charset: noconv -iQCVAwUBOt+j/jpxHvX/mS9tAQGXwAP/U4voKzXAcTfo9CqJhHN92GRxunj6mlvn -H+1pxSe0GdtC7BlrPhrokB5dNSwewk89Z5t7kTD76kx2FFuTcXBJxbgH7LZVF3ga -JX92bOWQekHMH7Hk12Qc7zpeTmPzY02pvVc37Eo614BCvJMCk02cpQyo8a5wWRKH -8vpQkQKiSyY= -=FFLG +iQCVAwUBOxFX6zpxHvX/mS9tAQE+bgP7B5FFxU+RQ3l8fBvZoMbu/Mslo9+XgtLg +gK8Xwnp4Ij909fa2i6i2bvFPkzHULMqp2PRdtxBTn9CdhwSZyRByhCvZvDHitsv2 +ZEIRn2Bd1jhhgWcr5KCbjKUaIwcFY7RdSQJw/yCyGsodg1fLI+g+QrnMkICI/RTa +AtYLLWgwEqo= +=3IMV -----END PGP SIGNATURE----- diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index d4bab23fe94b..b40cb12a2275 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 2.17.6.1 2001/02/16 16:43:29 kai Exp $ +/* $Id: tei.c,v 2.17.6.2 2001/05/26 15:19:57 kai Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -17,7 +17,7 @@ #include #include -const char *tei_revision = "$Revision: 2.17.6.1 $"; +const char *tei_revision = "$Revision: 2.17.6.2 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 @@ -446,14 +446,14 @@ static struct FsmNode TeiFnList[] __initdata = #define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) -void __init +int __init TeiNew(void) { teifsm.state_count = TEI_STATE_COUNT; teifsm.event_count = TEI_EVENT_COUNT; teifsm.strEvent = strTeiEvent; teifsm.strState = strTeiState; - FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); + return FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); } void diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c index d8ff7837a44a..9c2e3f4ca4ee 100644 --- a/drivers/isdn/hysdn/hycapi.c +++ b/drivers/isdn/hysdn/hycapi.c @@ -1,4 +1,4 @@ -/* $Id: hycapi.c,v 1.8.6.2 2001/04/20 02:42:00 keil Exp $ +/* $Id: hycapi.c,v 1.8.6.3 2001/05/26 15:19:58 kai Exp $ * * Linux driver for HYSDN cards, CAPI2.0-Interface. * written by Ulrich Albrecht (u.albrecht@hypercope.de) for Hypercope GmbH @@ -41,7 +41,7 @@ #include "hysdn_defs.h" #include -static char hycapi_revision[]="$Revision: 1.8.6.2 $"; +static char hycapi_revision[]="$Revision: 1.8.6.3 $"; unsigned int hycapi_enable = 0xffffffff; MODULE_PARM(hycapi_enable, "i"); @@ -734,7 +734,7 @@ hycapi_cleanup(void) struct capi_driver *driver; driver = &hycapi_driver; if (!hy_di) { - printk(KERN_ERR "HYSDN: no capi-driver to detach (???)\n"); + printk(KERN_ERR "HYSDN: no capi-driver to detach (?)\n"); return; } printk(KERN_NOTICE "HYSDN: Detaching capi-driver\n"); diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 7bf131b2efc4..5f1783b9bbb1 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -1,4 +1,4 @@ -/* $Id: hysdn_net.c,v 1.8.6.2 2001/04/20 02:42:00 keil Exp $ +/* $Id: hysdn_net.c,v 1.8.6.3 2001/06/05 19:45:37 kai Exp $ * Linux driver for HYSDN cards, net (ethernet type) handling routines. * @@ -41,7 +41,7 @@ unsigned int hynet_enable = 0xffffffff; MODULE_PARM(hynet_enable, "i"); /* store the actual version for log reporting */ -char *hysdn_net_revision = "$Revision: 1.8.6.2 $"; +char *hysdn_net_revision = "$Revision: 1.8.6.3 $"; #define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */ @@ -304,8 +304,7 @@ hysdn_net_create(hysdn_card * card) hysdn_net_release(card); /* release an existing net device */ if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) { printk(KERN_WARNING "HYSDN: unable to allocate mem\n"); - if (card->debug_flags & LOG_NET_INIT) - return (-ENOMEM); + return (-ENOMEM); } memset(dev, 0, sizeof(struct net_local)); /* clean the structure */ diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c index 19f55c070e52..f540196e7d61 100644 --- a/drivers/isdn/hysdn/hysdn_proclog.c +++ b/drivers/isdn/hysdn/hysdn_proclog.c @@ -1,4 +1,4 @@ -/* $Id: hysdn_proclog.c,v 1.9 2000/11/25 17:01:01 kai Exp $ +/* $Id: hysdn_proclog.c,v 1.9.6.1 2001/05/26 15:19:58 kai Exp $ * Linux driver for HYSDN cards, /proc/net filesystem log functions. * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH @@ -228,7 +228,7 @@ hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off) struct log_data *inf; int len; word ino; - struct procdata *pd; + struct procdata *pd = NULL; hysdn_card *card; if (!*((struct log_data **) file->private_data)) { @@ -271,7 +271,7 @@ static int hysdn_log_open(struct inode *ino, struct file *filep) { hysdn_card *card; - struct procdata *pd; + struct procdata *pd = NULL; ulong flags; lock_kernel(); @@ -381,7 +381,7 @@ hysdn_log_poll(struct file *file, poll_table * wait) unsigned int mask = 0; word ino; hysdn_card *card; - struct procdata *pd; + struct procdata *pd = NULL; if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) return (mask); /* no polling for write supported */ diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 87534dd42216..53467f8e2aec 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.85.6.4 2001/04/08 18:53:07 kai Exp $ +/* $Id: isdn_ppp.c,v 1.85.6.5 2001/05/26 15:19:56 kai Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -83,7 +83,7 @@ static void isdn_ppp_mp_cleanup( isdn_net_local * lp ); static int isdn_ppp_bundle(struct ippp_struct *, int unit); #endif /* CONFIG_ISDN_MPP */ -char *isdn_ppp_revision = "$Revision: 1.85.6.4 $"; +char *isdn_ppp_revision = "$Revision: 1.85.6.5 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; @@ -785,7 +785,10 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) } skb_reserve(skb, hl); if (copy_from_user(skb_put(skb, count), buf, count)) + { + kfree_skb(skb); return -EFAULT; + } if (is->debug & 0x40) { printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot); diff --git a/drivers/media/radio/Config.in b/drivers/media/radio/Config.in index a6d3a13c4dab..b5fb0c2c53a0 100644 --- a/drivers/media/radio/Config.in +++ b/drivers/media/radio/Config.in @@ -24,6 +24,7 @@ fi dep_tristate ' Guillemot MAXI Radio FM 2000 radio' CONFIG_RADIO_MAXIRADIO $CONFIG_VIDEO_DEV dep_tristate ' Maestro on board radio' CONFIG_RADIO_MAESTRO $CONFIG_VIDEO_DEV dep_tristate ' miroSOUND PCM20 radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV $CONFIG_SOUND_ACI_MIXER +dep_tristate ' miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)' CONFIG_RADIO_MIROPCM20_RDS $CONFIG_RADIO_MIROPCM20 $CONFIG_EXPERIMENTAL dep_tristate ' SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then hex ' SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 63fb57106d7a..fef1dbfb0d13 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -21,11 +21,11 @@ O_TARGET := radio.o # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := +export-objs := miropcm20-rds-core.o list-multi := miropcm20.o -miropcm20-objs := radio-miropcm20.o rds-miropcm20.o +miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o @@ -37,6 +37,7 @@ obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o +obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/miropcm20-radio.c similarity index 89% rename from drivers/media/radio/radio-miropcm20.c rename to drivers/media/radio/miropcm20-radio.c index e30aed436d5a..d4670b0889f5 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/miropcm20-radio.c @@ -22,19 +22,14 @@ #include #include #include -#include -#include - -char * aci_radio_name; - #include "../../sound/aci.h" +#include "miropcm20-rds-core.h" static int users = 0; static int radio_nr = -1; MODULE_PARM(radio_nr, "i"); -struct pcm20_device -{ +struct pcm20_device { unsigned long freq; int muted; int stereo; @@ -44,13 +39,13 @@ struct pcm20_device static int pcm20_mute(struct pcm20_device *dev, unsigned char mute) { dev->muted = mute; - return aci_write_cmd(0xa3, mute); + return aci_write_cmd(ACI_SET_TUNERMUTE, mute); } static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo) { dev->stereo = stereo; - return aci_write_cmd(0xa4, !stereo); + return aci_write_cmd(ACI_SET_TUNERMONO, !stereo); } static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq) @@ -70,7 +65,7 @@ static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq) aci_rds_cmd(RDS_RESET, 0, 0); pcm20_stereo(dev, 1); - return aci_rw_cmd(0xa7, freql, freqh); /* Tune to frequency */ + return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh); } static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) @@ -79,7 +74,7 @@ static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) int i; unsigned char buf; - if ((i=aci_rw_cmd(0xa9, -1, -1))<0) + if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0) return i; #if DEBUG printk("check_sig: 0x%x\n", i); @@ -92,7 +87,7 @@ static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) } else *signal=0xffff; - if ((i=aci_rw_cmd(0xa8, -1, -1))<0) + if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0) return i; if (i & 0x40) { *flags=0; @@ -117,7 +112,7 @@ static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) printk("rds-signal: %d\n", buf); #endif if (buf > 15) { - printk("rds-miropcm20: RX strengths unexpected high...\n"); + printk("miropcm20-radio: RX strengths unexpected high...\n"); buf=15; } /* refine signal */ @@ -231,23 +226,23 @@ static int pcm20_open(struct video_device *dev, int flags) if(users) return -EBUSY; users++; + MOD_INC_USE_COUNT; return 0; } static void pcm20_close(struct video_device *dev) { users--; + MOD_DEC_USE_COUNT; } -static struct pcm20_device pcm20_unit= -{ +static struct pcm20_device pcm20_unit = { freq: 87*16000, muted: 1, stereo: 0 }; -static struct video_device pcm20_radio= -{ +static struct video_device pcm20_radio = { owner: THIS_MODULE, name: "Miro PCM 20 radio", type: VID_TYPE_TUNER, @@ -261,18 +256,19 @@ static struct video_device pcm20_radio= static int __init pcm20_init(void) { if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1) - return -EINVAL; + goto video_register_device; - if(attach_aci_rds()<0) { - video_unregister_device(&pcm20_radio); - return -EINVAL; - } -#if DEBUG - printk("v4l-name: %s\n", devfs_get_name(pcm20_radio.devfs_handle, 0)); -#endif + if(attach_aci_rds()<0) + goto attach_aci_rds; + printk(KERN_INFO "Miro PCM20 radio card driver.\n"); return 0; + + attach_aci_rds: + video_unregister_device(&pcm20_radio); + video_register_device: + return -EINVAL; } MODULE_AUTHOR("Ruurd Reitsma"); diff --git a/drivers/media/radio/rds-miropcm20.c b/drivers/media/radio/miropcm20-rds-core.c similarity index 70% rename from drivers/media/radio/rds-miropcm20.c rename to drivers/media/radio/miropcm20-rds-core.c index 821de6f77153..4cc0db1db958 100644 --- a/drivers/media/radio/rds-miropcm20.c +++ b/drivers/media/radio/miropcm20-rds-core.c @@ -22,25 +22,27 @@ #include #include #include "../../sound/aci.h" - -#define WATCHMASK 0352 /* 11101010 */ +#include "miropcm20-rds-core.h" #define DEBUG 0 static struct semaphore aci_rds_sem; - +#define RDS_DATASHIFT 2 /* Bit 2 */ +#define RDS_DATAMASK (1 << RDS_DATASHIFT) #define RDS_BUSYMASK 0x10 /* Bit 4 */ #define RDS_CLOCKMASK 0x08 /* Bit 3 */ -#define RDS_DATAMASK 0x04 /* Bit 2 */ + +#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) +#if DEBUG static void print_matrix(char array[], unsigned int length) { int i, j; for (i=0; i=0; j--) { printk("%d", (array[i] >> j) & 0x1); } @@ -50,6 +52,7 @@ static void print_matrix(char array[], unsigned int length) printk("\n"); } } +#endif /* DEBUG */ static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size) { @@ -64,33 +67,6 @@ static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size) return 0; } -static int trans2byte(unsigned char buffer[], int size) -{ - int i; - unsigned char byte=0; - - if (size != 8) - return -1; - for (i = 7; i >= 0; i--) - byte |= ((buffer[7-i] & RDS_DATAMASK) ? 1 : 0) << i; - - return byte; -} - -static int trans2data(unsigned char readbuffer[], int readsize, unsigned char data[], int datasize) -{ - int i,j; - - if (readsize != datasize*8) - return -1; - for (i = 0; i < datasize; i++) - if ((j=trans2byte(&readbuffer[i*8], 8)) < 0) - return -1; - else - data[i]=j; - return 0; -} - static int rds_waitread(void) { unsigned char byte; @@ -98,20 +74,18 @@ static int rds_waitread(void) do { byte=inb(RDS_REGISTER); - if ((byte & WATCHMASK) != WATCHMASK) - printk("aci-rds: Hidden information discoverd!\n"); i--; } while ((byte & RDS_BUSYMASK) && i); if (i) { -#if DEBUG - printk("rds_waitread()"); + #if DEBUG + printk(KERN_DEBUG "rds_waitread()"); print_matrix(&byte, 1); -#endif + #endif return (byte); } else { - printk("aci-rds: rds_waitread() timeout...\n"); + printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n"); return -1; } } @@ -120,10 +94,10 @@ static int rds_waitread(void) static inline void rds_rawwrite_nowait(unsigned char byte) { -#if DEBUG - printk("rds_rawwrite()"); + #if DEBUG + printk(KERN_DEBUG "rds_rawwrite()"); print_matrix(&byte, 1); -#endif + #endif outb(byte, RDS_REGISTER); } @@ -166,41 +140,27 @@ static int rds_readcycle(void) static int rds_read(unsigned char databuffer[], int datasize) { - -#define READSIZE (8*datasize) + #define READSIZE (8*datasize) int i,j; - unsigned char* readbuffer; - if (!datasize) /* nothing to read */ + if (datasize < 1) /* nothing to read */ return 0; /* to be able to use rds_readcycle_nowait() - I have to readwait() here */ + I have to waitread() here */ if (rds_waitread() < 0) return -1; - if ((readbuffer=kmalloc(READSIZE, GFP_KERNEL)) == 0) { - printk("aci-rds: Out of memory...\n"); - return -ENOMEM; - } else { - if (signal_pending(current)) { - kfree(readbuffer); - return -EINTR; - } - } - + memset(databuffer, 0, datasize); + for (i=0; i< READSIZE; i++) if((j=rds_readcycle_nowait()) < 0) { - kfree(readbuffer); return -1; - } else - readbuffer[i]=j; - if (trans2data(readbuffer, READSIZE, databuffer, datasize) < 0) { - kfree(readbuffer); - return -1; - } - kfree(readbuffer); + } else { + databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8))); + } + return 0; } @@ -213,6 +173,7 @@ static int rds_ack(void) if (i & RDS_DATAMASK) { return 0; /* ACK */ } else { + printk(KERN_DEBUG "aci-rds: NACK\n"); return 1; /* NACK */ } } @@ -224,8 +185,7 @@ int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize) if (down_interruptible(&aci_rds_sem)) return -EINTR; - if (rds_write(cmd)) - ret = -2; + rds_write(cmd); /* RDS_RESET doesn't need further processing */ if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize))) @@ -237,6 +197,7 @@ int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize) return ret; } +EXPORT_SYMBOL(aci_rds_cmd); int __init attach_aci_rds(void) { diff --git a/drivers/media/radio/miropcm20-rds-core.h b/drivers/media/radio/miropcm20-rds-core.h new file mode 100644 index 000000000000..aeb5761f0469 --- /dev/null +++ b/drivers/media/radio/miropcm20-rds-core.h @@ -0,0 +1,19 @@ +#ifndef _MIROPCM20_RDS_CORE_H_ +#define _MIROPCM20_RDS_CORE_H_ + +extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize); + +#define RDS_STATUS 0x01 +#define RDS_STATIONNAME 0x02 +#define RDS_TEXT 0x03 +#define RDS_ALTFREQ 0x04 +#define RDS_TIMEDATE 0x05 +#define RDS_PI_CODE 0x06 +#define RDS_PTYTATP 0x07 +#define RDS_RESET 0x08 +#define RDS_RXVALUE 0x09 + +extern void __exit unload_aci_rds(void); +extern int __init attach_aci_rds(void); + +#endif /* _MIROPCM20_RDS_CORE_H_ */ diff --git a/drivers/media/radio/miropcm20-rds.c b/drivers/media/radio/miropcm20-rds.c new file mode 100644 index 000000000000..661fae7e597a --- /dev/null +++ b/drivers/media/radio/miropcm20-rds.c @@ -0,0 +1,140 @@ +/* MiroSOUND PCM20 radio rds interface driver + * (c) 2001 Robert Siemer + * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information. + */ + +/* Revision history: + * + * 2001-04-18 Robert Siemer + * separate file for user interface driver + */ + +#include +#include +#include +#include +#include +#include "miropcm20-rds-core.h" + +devfs_handle_t dfsh; +char * text_buffer; +static int rds_users = 0; + + +static int rds_f_open(struct inode *in, struct file *fi) +{ + if(rds_users) + return -EBUSY; + + if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) { + printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n"); + return -ENOMEM; + } + + rds_users++; + MOD_INC_USE_COUNT; + return 0; +} + +static int rds_f_release(struct inode *in, struct file *fi) +{ + kfree(text_buffer); + + rds_users--; + MOD_DEC_USE_COUNT; + return 0; +} + +static void print_matrix(char *ch, char out[]) +{ + int j; + + for (j=7; j>=0; j--) { + out[7-j] = ((*ch >> j) & 0x1) + '0'; + } +} + +static ssize_t rds_f_read(struct file *file, char *buffer, size_t length, loff_t *offset) +{ +// i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset); + + char c; + char bits[8]; + + current->state=TASK_UNINTERRUPTIBLE; + schedule_timeout(2*HZ); + aci_rds_cmd(RDS_STATUS, &c, 1); + print_matrix(&c, bits); + if (copy_to_user(buffer, bits, 8)) + return -EFAULT; + +/* if ((c >> 3) & 1) { + aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8); + text_buffer[0] = ' ' ; + text_buffer[9] = '\n'; + return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18; + } +*/ +/* if ((c >> 6) & 1) { + aci_rds_cmd(RDS_PTYTATP, &c, 1); + if ( c & 1) + sprintf(text_buffer, " M"); + else + sprintf(text_buffer, " S"); + if ((c >> 1) & 1) + sprintf(text_buffer+2, " TA"); + else + sprintf(text_buffer+2, " --"); + if ((c >> 7) & 1) + sprintf(text_buffer+5, " TP"); + else + sprintf(text_buffer+5, " --"); + sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f); + return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20; + } +*/ + + if ((c >> 4) & 1) { + aci_rds_cmd(RDS_TEXT, text_buffer, 65); + text_buffer[0] = ' ' ; + text_buffer[65] = '\n'; + return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8; + } else { + put_user('\n', buffer+8); + return 9; + } +} + +static struct file_operations rds_f_ops = { + read: rds_f_read, + open: rds_f_open, + release: rds_f_release +}; + + +static int __init miropcm20_rds_init(void) +{ + if ((dfsh = devfs_register(NULL, "v4l/rds/radiotext", + DEVFS_FL_DEFAULT | DEVFS_FL_AUTO_DEVNUM, + 0, 0, S_IRUGO | S_IFCHR, &rds_f_ops, NULL)) + == NULL) + goto devfs_register; + + printk("miropcm20-rds: userinterface driver loaded.\n"); +#if DEBUG + printk("v4l-name: %s\n", devfs_get_name(pcm20_radio.devfs_handle, 0)); +#endif + + return 0; + + devfs_register: + return -EINVAL; +} + +static void __exit miropcm20_rds_cleanup(void) +{ + devfs_unregister(dfsh); +} + +module_init(miropcm20_rds_init); +module_exit(miropcm20_rds_cleanup); diff --git a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c index 971908fe3059..308b3f1dc22d 100644 --- a/drivers/media/video/tuner.c +++ b/drivers/media/video/tuner.c @@ -558,6 +558,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) #endif default: /* nothing */ + break; } return 0; diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in index 87bf2e9b6857..866625c98130 100644 --- a/drivers/mtd/Config.in +++ b/drivers/mtd/Config.in @@ -1,5 +1,5 @@ -# $Id: No. :) $ +# $Id: Config.in,v 1.66 2001/05/07 21:00:43 dwmw2 Exp $ mainmenu_option next_comment comment 'Memory Technology Devices (MTD)' @@ -11,67 +11,16 @@ if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then if [ "$CONFIG_MTD_DEBUG" = "y" ]; then int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 fi - -comment 'Disk-On-Chip Device Drivers' - dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD - dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD - dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver' CONFIG_MTD_DOC2001 $CONFIG_MTD - if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then - define_tristate CONFIG_MTD_DOCPROBE y - else - if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then - define_tristate CONFIG_MTD_DOCPROBE m - else - define_tristate CONFIG_MTD_DOCPROBE n - fi - fi - if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then - hex ' Physical address of DiskOnChip' CONFIG_MTD_DOCPROBE_ADDRESS 0x0000 - bool ' Probe high addresses' CONFIG_MTD_DOCPROBE_HIGH - bool ' Probe for 0x55 0xAA BIOS Extension Signature' CONFIG_MTD_DOCPROBE_55AA - fi - -comment 'RAM/ROM Device Drivers' - dep_tristate ' Use extra onboard system memory as MTD device' CONFIG_MTD_SLRAM $CONFIG_MTD - dep_tristate ' Ramix PMC551 PCI Mezzanine ram card support' CONFIG_MTD_PMC551 $CONFIG_MTD $CONFIG_PCI - if [ "$CONFIG_MTD_PMC551" != "n" ]; then - bool ' PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX - bool ' PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG + dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD + dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS + dep_tristate ' Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS + +comment 'User Modules And Translation Layers' + dep_tristate ' Direct char device access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD + dep_tristate ' Caching block device access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD + if [ "$CONFIG_MTD_BLOCK" = "n" -o "$CONFIG_MTD_BLOCK" = "m" ]; then + dep_tristate ' Readonly block device access to MTD devices' CONFIG_MTD_BLOCK_RO $CONFIG_MTD fi - dep_tristate ' Debugging RAM test driver' CONFIG_MTD_MTDRAM $CONFIG_MTD - if [ "$CONFIG_MTD_MTDRAM" != "n" ]; then - int 'Device size in kB' CONFIG_MTDRAM_TOTAL_SIZE 4096 - int 'Size of the erase sectors in kB' CONFIG_MTDRAM_ERASE_SIZE 128 - fi - -comment 'Linearly Mapped Flash Device Drivers' - dep_tristate ' Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD - dep_tristate ' CFI support for Intel/Sharp Extended Command Set chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI - dep_tristate ' CFI support for AMD/Fujitsu Standard Command Set chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI - dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD - dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD - -# These will later become config-options -define_bool CONFIG_MTD_JEDEC n - - dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI - if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then - hex ' Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 - hex ' Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 - int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 - fi - -comment 'Drivers for chip mappings' - dep_tristate ' Flash chip mapping on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC - dep_tristate ' Flash chip mapping on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI - dep_tristate ' Flash chip mapping on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC - dep_tristate ' Flash chip mapping on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI - dep_tristate ' Flash chip mapping on RPXLite PPC board' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI - dep_tristate ' Flash chip mapping on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC - -comment 'User modules and translation layers for MTD devices' - dep_tristate ' Direct chardevice access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD - dep_tristate ' Caching blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD dep_tristate ' FTL (Flash Translation Layer) support' CONFIG_FTL $CONFIG_MTD dep_tristate ' NFTL (NAND Flash Translation Layer) support' CONFIG_NFTL $CONFIG_MTD if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then @@ -79,4 +28,12 @@ comment 'User modules and translation layers for MTD devices' fi fi +source drivers/mtd/chips/Config.in + +source drivers/mtd/maps/Config.in + +source drivers/mtd/devices/Config.in + +source drivers/mtd/nand/Config.in + endmenu diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 005baaab6bb6..887c858c3b3a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -8,54 +8,57 @@ # Note 2! The CFLAGS definitions are now inherited from the # parent makes.. # -# $Id: Makefile,v 1.22 2000/07/14 08:10:52 dwmw2 Exp $ +# $Id: Makefile,v 1.60 2001/05/31 20:43:18 dwmw2 Exp $ -# Object file lists. -obj-y := +obj-y += chips/chipslink.o maps/mapslink.o \ + devices/devlink.o nand/nandlink.o obj-m := obj-n := obj- := O_TARGET := mtdlink.o -SUB_DIRS := -ALL_SUB_DIRS := -MOD_SUB_DIRS := -export-objs := mtdcore.o mtdpart.o jedec.o -list-multi := +export-objs := mtdcore.o mtdpart.o redboot.o bootldr.o +list-multi := nftl.o -# MTD devices +mod-subdirs := +subdir-y := chips maps devices nand +subdir-m := $(subdir-y) + +# *** BIG UGLY NOTE *** +# +# The shiny new inter_module_xxx has introduced yet another ugly link +# order dependency, which I'd previously taken great care to avoid. +# We now have to ensure that the chip drivers are initialised before the +# map drivers, and that the doc200[01] drivers are initialised before +# docprobe. +# +# We'll hopefully merge the doc200[01] drivers and docprobe back into +# a single driver some time soon, but the CFI drivers are going to have +# to stay like that. +# +# Urgh. +# +# dwmw2 21/11/0 + +# Core functionality. obj-$(CONFIG_MTD) += mtdcore.o -obj-$(CONFIG_MTD_DOC1000) += doc1000.o -obj-$(CONFIG_MTD_DOC2000) += doc2000.o -obj-$(CONFIG_MTD_DOC2001) += doc2001.o -obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o -obj-$(CONFIG_MTD_SLRAM) += slram.o -obj-$(CONFIG_MTD_PMC551) += pmc551.o -obj-$(CONFIG_MTD_MTDRAM) += mtdram.o - -# Chip drivers -obj-$(CONFIG_MTD_JEDEC) += jedec.o -obj-$(CONFIG_MTD_RAM) += map_ram.o -obj-$(CONFIG_MTD_ROM) += map_rom.o -obj-$(CONFIG_MTD_CFI) += cfi_probe.o -obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o -obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o - -# Chip mappings -obj-$(CONFIG_MTD_PHYSMAP) += physmap.o -obj-$(CONFIG_MTD_MIXMEM) += mixmem.o -obj-$(CONFIG_MTD_NORA) += nora.o -obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o -obj-$(CONFIG_MTD_PNC2000) += pnc2000.o mtdpart.o -obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o -obj-$(CONFIG_MTD_VMAX) += vmax301.o - -# Users +obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o +obj-$(CONFIG_MTD_BOOTLDR_PARTS) += bootldr.o + +# 'Users' - code which presents functionality to userspace. obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o obj-$(CONFIG_FTL) += ftl.o -obj-$(CONFIG_NFTL) += nftl.o nftlmount.o +obj-$(CONFIG_NFTL) += nftl.o + +nftl-objs := nftlcore.o nftlmount.o include $(TOPDIR)/Rules.make + +nftl.o: $(nftl-objs) + $(LD) -r -o $@ $(nftl-objs) + diff --git a/drivers/mtd/bootldr.c b/drivers/mtd/bootldr.c new file mode 100644 index 000000000000..793ed6ab7db6 --- /dev/null +++ b/drivers/mtd/bootldr.c @@ -0,0 +1,149 @@ +/* + * Read flash partition table from Compaq Bootloader + * + * Copyright 2001 Compaq Computer Corporation. + * + * $Id: bootldr.c,v 1.4 2001/06/02 18:24:27 nico Exp $ + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + */ + +/* + * Maintainer: Jamey Hicks (jamey.hicks@compaq.com) + */ + +#include +#include + +#include +#include + +#define FLASH_PARTITION_NAMELEN 32 +enum LFR_FLAGS { + LFR_SIZE_PREFIX = 1, /* prefix data with 4-byte size */ + LFR_PATCH_BOOTLDR = 2, /* patch bootloader's 0th instruction */ + LFR_KERNEL = 4, /* add BOOTIMG_MAGIC, imgsize and VKERNEL_BASE to head of programmed region (see bootldr.c) */ + LFR_EXPAND = 8 /* expand partition size to fit rest of flash */ +}; + +typedef struct FlashRegion { + char name[FLASH_PARTITION_NAMELEN]; + unsigned long base; + unsigned long size; + enum LFR_FLAGS flags; +} FlashRegion; + +typedef struct BootldrFlashPartitionTable { + int magic; /* should be filled with 0x646c7470 (btlp) BOOTLDR_PARTITION_MAGIC */ + int npartitions; + struct FlashRegion partition[0]; +} BootldrFlashPartitionTable; + +#define BOOTLDR_MAGIC 0x646c7462 /* btld: marks a valid bootldr image */ +#define BOOTLDR_PARTITION_MAGIC 0x646c7470 /* btlp: marks a valid bootldr partition table in params sector */ + +#define BOOTLDR_MAGIC_OFFSET 0x20 /* offset 0x20 into the bootldr */ +#define BOOTCAP_OFFSET 0X30 /* offset 0x30 into the bootldr */ + +#define BOOTCAP_WAKEUP (1<<0) +#define BOOTCAP_PARTITIONS (1<<1) /* partition table stored in params sector */ +#define BOOTCAP_PARAMS_AFTER_BOOTLDR (1<<2) /* params sector right after bootldr sector(s), else in last sector */ + +int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + struct mtd_partition *parts; + int ret, retlen, i; + int npartitions = 0; + long partition_table_offset; + long bootmagic = 0; + long bootcap = 0; + int namelen = 0; + struct BootldrFlashPartitionTable *partition_table = NULL; + char *names; + + /* verify bootldr magic */ + ret = master->read(master, BOOTLDR_MAGIC_OFFSET, sizeof(long), &retlen, (void *)&bootmagic); + if (ret) + goto out; + if (bootmagic != BOOTLDR_MAGIC) + goto out; + /* see if bootldr supports partition tables and where to find the partition table */ + ret = master->read(master, BOOTCAP_OFFSET, sizeof(long), &retlen, (void *)&bootcap); + if (ret) + goto out; + + if (!(bootcap & BOOTCAP_PARTITIONS)) + goto out; + if (bootcap & BOOTCAP_PARAMS_AFTER_BOOTLDR) + partition_table_offset = master->erasesize; + else + partition_table_offset = master->size - master->erasesize; + + printk(__FUNCTION__ ": partition_table_offset=%#lx\n", partition_table_offset); + + /* Read the partition table */ + partition_table = (struct BootldrFlashPartitionTable *)kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!partition_table) + return -ENOMEM; + ret = master->read(master, partition_table_offset, + PAGE_SIZE, &retlen, (void *)partition_table); + if (ret) + goto out; + + printk(__FUNCTION__ ": magic=%#x\n", partition_table->magic); + + /* check for partition table magic number */ + if (partition_table->magic != BOOTLDR_PARTITION_MAGIC) + goto out; + npartitions = partition_table->npartitions; + + printk(__FUNCTION__ ": npartitions=%#x\n", npartitions); + + for (i = 0; i < npartitions; i++) { + namelen += strlen(partition_table->partition[i].name) + 1; + } + + parts = kmalloc(sizeof(*parts)*npartitions + namelen, GFP_KERNEL); + if (!parts) { + ret = -ENOMEM; + goto out; + } + names = (char *)&parts[npartitions]; + memset(parts, 0, sizeof(*parts)*npartitions + namelen); + + for (i = 0; i < npartitions; i++) { + struct FlashRegion *partition = &partition_table->partition[i]; + const char *name = partition->name; + parts[i].name = names; + names += strlen(name) + 1; + strcpy(parts[i].name, name); + + if (partition->flags & LFR_EXPAND) + parts[i].size = MTDPART_SIZ_FULL; + else + parts[i].size = partition->size; + parts[i].offset = partition->base; + parts[i].mask_flags = 0; + + printk(" partition %s o=%x s=%x\n", + parts[i].name, parts[i].offset, parts[i].size); + + } + + ret = npartitions; + *pparts = parts; + + out: + if (partition_table) + kfree(partition_table); + return ret; +} + +EXPORT_SYMBOL(parse_bootldr_partitions); diff --git a/drivers/mtd/cfi_cmdset_0001.c b/drivers/mtd/cfi_cmdset_0001.c deleted file mode 100644 index 56e5a394afc5..000000000000 --- a/drivers/mtd/cfi_cmdset_0001.c +++ /dev/null @@ -1,891 +0,0 @@ -/* - * Common Flash Interface support: - * Intel Extended Vendor Command Set (ID 0x0001) - * - * (C) 2000 Red Hat. GPL'd - * - * $Id: cfi_cmdset_0001.c,v 1.21 2000/07/13 10:36:14 dwmw2 Exp $ - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE < 0x20300 -#define set_current_state(x) current->state = (x); -#endif -static int cfi_intelext_read_1_by_16 (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_write_1_by_16(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -static int cfi_intelext_erase_1_by_16 (struct mtd_info *, struct erase_info *); -static void cfi_intelext_sync (struct mtd_info *); -static int cfi_intelext_suspend (struct mtd_info *); -static void cfi_intelext_resume (struct mtd_info *); - -static void cfi_intelext_destroy(struct mtd_info *); - -static void cfi_cmdset_0001(struct map_info *, int, unsigned long); - -static struct mtd_info *cfi_intelext_setup (struct map_info *); - -static const char im_name[] = "cfi_cmdset_0001"; - -/* This routine is made available to other mtd code via - * inter_module_register. It must only be accessed through - * inter_module_get which will bump the use count of this module. The - * addresses passed back in cfi are valid as long as the use count of - * this module is non-zero, i.e. between inter_module_get and - * inter_module_put. Keith Owens 29 Oct 2000. - */ -static void cfi_cmdset_0001(struct map_info *map, int primary, unsigned long base) -{ - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct cfi_pri_intelext *extp; - - __u16 adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; - - printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); - - if (!adr) - return; - - /* Switch it into Query Mode */ - switch(map->buswidth) { - case 1: - map->write8(map, 0x98, 0x55); - break; - case 2: - map->write16(map, 0x9898, 0xaa); - break; - case 4: - map->write32(map, 0x98989898, 0x154); - break; - } - - extp = kmalloc(sizeof(*extp), GFP_KERNEL); - if (!extp) { - printk("Failed to allocate memory\n"); - return; - } - - /* Read in the Extended Query Table */ - for (i=0; iread8(map, (base+((adr+i)*map->buswidth))); - } - - if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { - printk(" Unknown IntelExt Extended Query version %c.%c.\n", - extp->MajorVersion, extp->MinorVersion); - kfree(extp); - return; - } - - /* Do some byteswapping if necessary */ - extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); - extp->BlkStatusRegMask = le32_to_cpu(extp->BlkStatusRegMask); - - - /* Tell the user about it in lots of lovely detail */ -#if 0 - printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); - printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); - printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); - printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); - printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); - printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); - printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); - printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); - printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); - printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); - for (i=9; i<32; i++) { - if (extp->FeatureSupport & (1<SuspendCmdSupport); - printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); - for (i=1; i<8; i++) { - if (extp->SuspendCmdSupport & (1<BlkStatusRegMask); - printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); - printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); - for (i=2; i<16; i++) { - if (extp->BlkStatusRegMask & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); - if (extp->VppOptimal) - printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", - extp->VppOptimal >> 8, extp->VppOptimal & 0xf); -#endif - /* OK. We like it. Take over the control of it. */ - - /* Switch it into Read Mode */ - switch(map->buswidth) { - case 1: - map->write8(map, 0xff, 0x55); - break; - case 2: - map->write16(map, 0xffff, 0xaa); - break; - case 4: - map->write32(map, 0xffffffff, 0x154); - break; - } - - - /* If there was an old setup function, decrease its use count */ - if (cfi->cmdset_setup) - inter_module_put(cfi->im_name); - if (cfi->cmdset_priv) - kfree(cfi->cmdset_priv); - - for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 128; - cfi->chips[i].buffer_write_time = 128; - cfi->chips[i].erase_time = 1024; - } - - - cfi->cmdset_setup = cfi_intelext_setup; - cfi->im_name = im_name; - cfi->cmdset_priv = extp; - - return; -} - -static struct mtd_info *cfi_intelext_setup(struct map_info *map) -{ - struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; - - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); - printk("number of CFI chips: %d\n", cfi->numchips); - - if (!mtd) { - printk("Failed to allocate memory for MTD device\n"); - kfree(cfi->cmdset_priv); - return NULL; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; - mtd->erasesize = 0x20000; /* FIXME */ - /* Also select the correct geometry setup too */ - mtd->size = (1 << cfi->cfiq.DevSize) * cfi->numchips; - mtd->erase = cfi_intelext_erase_1_by_16; - mtd->read = cfi_intelext_read_1_by_16; - mtd->write = cfi_intelext_write_1_by_16; - mtd->sync = cfi_intelext_sync; - mtd->suspend = cfi_intelext_suspend; - mtd->resume = cfi_intelext_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv_destroy = cfi_intelext_destroy; - mtd->name = map->name; - return mtd; -} - -static inline int do_read_1_by_16_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) -{ - __u16 status; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - - adr += chip->start; - - retry: - spin_lock_bh(chip->mutex); - - /* Check that the chip's ready to talk to us. - * Later, we can actually think about interrupting it - * if it's in FL_ERASING or FL_WRITING state. - * Not just yet, though. - */ - switch (chip->state) { -#if 0 - case FL_ERASING: - case FL_WRITING: - /* Suspend the operation, set state to FL_xxx_SUSPENDED */ -#endif - - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - case FL_READY: - map->write16(map, cpu_to_le16(0x0070), adr); - chip->state = FL_STATUS; - - case FL_STATUS: - status = le16_to_cpu(map->read16(map, adr)); - - if (!(status & (1<<7))) { - static int z=0; - /* Urgh. Chip not yet ready to talk to us. */ - if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); - printk("waiting for chip to be ready timed out in read"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet before read. looping\n"); - - udelay(1); - - goto retry; - } - break; - - default: - printk("Waiting for chip, status = %d\n", chip->state); - - /* Stick ourselves on a wait queue to be woken when - someone changes the status */ - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - - timeo = jiffies + HZ; - - goto retry; - } - - map->write16(map, cpu_to_le16(0x00ff), adr); - chip->state = FL_READY; - - map->copy_from(map, buf, adr, len); - - if (chip->state == FL_ERASE_SUSPENDED || - chip->state == FL_WRITE_SUSPENDED) { - printk("Who in hell suspended the pending operation? I didn't write that code yet!\n"); - /* Restart it and set the state accordingly */ - } - - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return 0; -} - -static int cfi_intelext_read_1_by_16 (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long ofs; - int chipnum; - int ret = 0; - - /* ofs: offset within the first chip that the first read should start */ - chipnum = (from >> cfi->chipshift); - ofs = from - (chipnum << cfi->chipshift); - - *retlen = 0; - - while (len) { - unsigned long thislen; - - if (chipnum >= cfi->numchips) - break; - - if ((len + ofs -1) >> cfi->chipshift) - thislen = (1<chipshift) - ofs; - else - thislen = len; - - ret = do_read_1_by_16_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); - if (ret) - break; - - *retlen += thislen; - len -= thislen; - buf += thislen; - - ofs = 0; - chipnum++; - } - return ret; -} - -static inline int do_write_1_by_16_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u16 datum) -{ - __u16 status; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - int z = 0; - adr += chip->start; - - retry: - spin_lock_bh(chip->mutex); - - /* Check that the chip's ready to talk to us. - * Later, we can actually think about interrupting it - * if it's in FL_ERASING state. - * Not just yet, though. - */ - switch (chip->state) { - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - case FL_READY: - map->write16(map, cpu_to_le16(0x0070), adr); - chip->state = FL_STATUS; - timeo = jiffies + HZ; - - case FL_STATUS: - status = le16_to_cpu(map->read16(map, adr)); - - if (!(status & (1<<7))) { - - /* Urgh. Chip not yet ready to talk to us. */ - if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); - printk("waiting for chip to be ready timed out in read"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet before write. looping\n"); - - udelay(1); - - goto retry; - } - break; - - default: - printk("Waiting for chip, status = %d\n", chip->state); - - /* Stick ourselves on a wait queue to be woken when - someone changes the status */ - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - map->write16(map, cpu_to_le16(0x0040), adr); - map->write16(map, datum, adr); - chip->state = FL_WRITING; - - timeo = jiffies + (HZ/2); - - spin_unlock_bh(chip->mutex); - udelay(chip->word_write_time); - spin_lock_bh(chip->mutex); - - z = 0; - while ( !( (status = le16_to_cpu(map->read16(map, adr))) & 0x80 ) ) { - - if (chip->state != FL_WRITING) { - /* Someone's suspended the write. Sleep */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if (signal_pending(current)) - return -EINTR; - - timeo = jiffies + (HZ / 2); /* FIXME */ - - spin_lock_bh(chip->mutex); - continue; - } - - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); - printk("waiting for chip to be ready timed out in read"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet after write. looping\n"); - - udelay(1); - - spin_lock_bh(chip->mutex); - continue; - } - if (!z) { - chip->word_write_time--; - if (!chip->word_write_time) - chip->word_write_time++; - } - if (z > 1) - chip->word_write_time++; - - /* Done and happy. */ - chip->state = FL_STATUS; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - // printk("write ret OK at %lx\n", adr); - return 0; -} - - -/* This version only uses the 'word write' instruction. We should update it - * to write using 'buffer write' if it's available - */ -static int cfi_intelext_write_1_by_16 (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; - int chipnum; - unsigned long ofs; - - *retlen = 0; - chipnum = to >> cfi->chipshift; - ofs = to - (chipnum << cfi->chipshift); - - /* If it's not word-aligned, do the first byte write */ - if (ofs & 1) { -#if defined(__LITTLE_ENDIAN) - ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], - ofs, 0xFF | (*buf << 8)); -#elif defined(__BIG_ENDIAN) - ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], - ofs, 0xFF00 | (*buf)); -#else -#error define a sensible endianness -#endif - if (ret) - return ret; - - ofs++; - buf++; - (*retlen)++; - len--; - - if (ofs >> cfi->chipshift) { - chipnum ++; - ofs = 0; - if (chipnum == cfi->numchips) - return 0; - } - } - - while(len > 1) { - ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], - ofs, *(__u16 *)buf); - if (ret) - return ret; - - ofs += 2; - buf += 2; - (*retlen) += 2; - len -= 2; - - if (ofs >> cfi->chipshift) { - chipnum ++; - ofs = 0; - if (chipnum == cfi->numchips) - return 0; - } - } - - if (len) { - /* Final byte to write */ -#if defined(__LITTLE_ENDIAN) - ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], - ofs, 0xFF00 | (*buf)); -#elif defined(__BIG_ENDIAN) - ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], - ofs, 0xFF | (*buf << 8)); -#else -#error define a sensible endianness -#endif - if (ret) - return ret; - - (*retlen)++; - } - - return 0; -} - - -static inline int do_erase_1_by_16_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) -{ - __u16 status; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - - adr += chip->start; - - retry: - spin_lock_bh(chip->mutex); - - /* Check that the chip's ready to talk to us. */ - switch (chip->state) { - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - case FL_READY: - map->write16(map, cpu_to_le16(0x0070), adr); - chip->state = FL_STATUS; - timeo = jiffies + HZ; - - case FL_STATUS: - status = le16_to_cpu(map->read16(map, adr)); - - if (!(status & (1<<7))) { - static int z=0; - /* Urgh. Chip not yet ready to talk to us. */ - if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); - printk("waiting for chip to be ready timed out in erase"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet before erase. looping\n"); - - udelay(1); - - goto retry; - } - break; - - default: - printk("Waiting for chip, status = %d\n", chip->state); - - /* Stick ourselves on a wait queue to be woken when - someone changes the status */ - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - map->write16(map, cpu_to_le16(0x0020), adr); - map->write16(map, cpu_to_le16(0x00D0), adr); - - chip->state = FL_ERASING; - - timeo = jiffies + (HZ*2); - spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); - spin_lock_bh(chip->mutex); - - /* FIXME. Use a timer to check this, and return immediately. */ - /* Once the state machine's known to be working I'll do that */ - - while ( !( (status = le16_to_cpu(map->read16(map, adr))) & 0x80 ) ) { - static int z=0; - - if (chip->state != FL_ERASING) { - /* Someone's suspended the erase. Sleep */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - printk("erase suspended. Sleeping\n"); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if (signal_pending(current)) - return -EINTR; - - timeo = jiffies + (HZ*2); /* FIXME */ - spin_lock_bh(chip->mutex); - continue; - } - - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); - printk("waiting for erase to complete timed out."); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet after erase. looping\n"); - - udelay(1); - - spin_lock_bh(chip->mutex); - continue; - } - - /* Done and happy. */ - chip->state = FL_STATUS; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - //printk("erase ret OK\n"); - return 0; -} - -static int cfi_intelext_erase_1_by_16 (struct mtd_info *mtd, struct erase_info *instr) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; - int chipnum, ret = 0; - - if (instr->addr & (mtd->erasesize - 1)) - return -EINVAL; - - if (instr->len & (mtd->erasesize -1)) - return -EINVAL; - - if ((instr->len + instr->addr) > mtd->size) - return -EINVAL; - - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); - len = instr->len; - - while(len) { - ret = do_erase_1_by_16_oneblock(map, &cfi->chips[chipnum], adr); - - if (ret) - return ret; - - adr += mtd->erasesize; - len -= mtd->erasesize; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - if (instr->callback) - instr->callback(instr); - - return 0; -} - - - -static void cfi_intelext_sync (struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; - int ret = 0; - DECLARE_WAITQUEUE(wait, current); - - for (i=0; !ret && inumchips; i++) { - chip = &cfi->chips[i]; - - retry: - spin_lock_bh(chip->mutex); - - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_SYNCING; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - spin_unlock_bh(chip->mutex); - break; - - default: - /* Not an idle state */ - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - schedule(); - - remove_wait_queue(&chip->wq, &wait); - - goto retry; - } - } - - /* Unlock the chips again */ - - for (i--; i >=0; i--) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_SYNCING) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - spin_unlock_bh(chip->mutex); - } -} - - -static int cfi_intelext_suspend(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; - int ret = 0; - - for (i=0; !ret && inumchips; i++) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_PM_SUSPENDED; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - break; - - default: - ret = -EAGAIN; - break; - } - spin_unlock_bh(chip->mutex); - } - - /* Unlock the chips again */ - - for (i--; i >=0; i--) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_PM_SUSPENDED) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - spin_unlock_bh(chip->mutex); - } - - return ret; -} - -static void cfi_intelext_resume(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; - - for (i=0; inumchips; i++) { - - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_PM_SUSPENDED) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - else - printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - - spin_unlock_bh(chip->mutex); - } -} - -static void cfi_intelext_destroy(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - kfree(cfi->cmdset_priv); - inter_module_put(cfi->im_name); - kfree(cfi); -} - - -static int __init cfi_intelext_init(void) -{ - inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0001); - return 0; -} - -static void __exit cfi_intelext_exit(void) -{ - inter_module_unregister(im_name); -} - -module_init(cfi_intelext_init); -module_exit(cfi_intelext_exit); diff --git a/drivers/mtd/cfi_cmdset_0002.c b/drivers/mtd/cfi_cmdset_0002.c deleted file mode 100644 index 3b0d9b7af896..000000000000 --- a/drivers/mtd/cfi_cmdset_0002.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Common Flash Interface support: - * AMD & Fujitsu Extended Vendor Command Set (ID 0x0002) - * - * Copyright (C) 2000 Crossnet Co. - * - * This code is GPL - * - * $Id: cfi_cmdset_0002.c,v 1.1 2000/07/11 12:32:09 dwmw2 Exp $ - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE < 0x20300 -#define set_current_state(x) current->state = (x); -#endif - -static int cfi_amdext_read_2_by_16 (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_amdext_write_2_by_16(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -static int cfi_amdext_erase_2_by_16 (struct mtd_info *, struct erase_info *); -static void cfi_amdext_sync (struct mtd_info *); -static int cfi_amdext_suspend (struct mtd_info *); -static void cfi_amdext_resume (struct mtd_info *); - -static void cfi_amdext_destroy(struct mtd_info *); - -static void cfi_cmdset_0002(struct map_info *, int, unsigned long); - -static struct mtd_info *cfi_amdext_setup (struct map_info *); - -static const char im_name[] = "cfi_cmdset_0002"; - -static void cfi_cmdset_0002(struct map_info *map, int primary, unsigned long base) -{ - struct cfi_private *cfi = map->fldrv_priv; - int i; -// struct cfi_pri_intelext *extp; - - __u16 adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; - - printk(" Amd/Fujitsu Extended Query Table at 0x%4.4X\n", adr); - - - /* If there was an old setup function, decrease its use count */ - if (cfi->cmdset_setup) - inter_module_put(cfi->im_name); - if (cfi->cmdset_priv) - kfree(cfi->cmdset_priv); - - for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 128; - cfi->chips[i].buffer_write_time = 128; - cfi->chips[i].erase_time = 1024; - } - - - cfi->cmdset_setup = cfi_amdext_setup; - cfi->im_name = im_name; -// cfi->cmdset_priv = extp; - - return; -} - -static struct mtd_info *cfi_amdext_setup(struct map_info *map) -{ - struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; - - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); - printk("number of CFI chips: %d\n", cfi->numchips); - - if (!mtd) { - printk("Failed to allocate memory for MTD device\n"); - kfree(cfi->cmdset_priv); - return NULL; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; - mtd->erasesize = 0x20000; /* FIXME */ - /* Also select the correct geometry setup too */ - mtd->size = (1 << cfi->cfiq.DevSize) * cfi->numchips * cfi->interleave; - mtd->erase = cfi_amdext_erase_2_by_16; - mtd->read = cfi_amdext_read_2_by_16; - mtd->write = cfi_amdext_write_2_by_16; - mtd->sync = cfi_amdext_sync; - mtd->suspend = cfi_amdext_suspend; - mtd->resume = cfi_amdext_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv_destroy = cfi_amdext_destroy; - mtd->name = map->name; - return mtd; -} - -static inline int do_read_2_by_16_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long timeo = jiffies + HZ; - - retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ -printk("Waiting for chip to read, status = %d\n", chip->state); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - adr += chip->start; - -// map->write32(map, cpu_to_le32(0x00F000F0), adr); - - chip->state = FL_READY; - - map->copy_from(map, buf, adr, len); - - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return 0; -} - -static int cfi_amdext_read_2_by_16 (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long ofs; - int chipnum; - int ret = 0; - - /* ofs: offset within the first chip that the first read should start */ - - chipnum = (from >> cfi->chipshift); - chipnum /= (cfi->interleave); - ofs = from - (chipnum << cfi->chipshift) * (cfi->interleave); - - *retlen = 0; - - while (len) { - unsigned long thislen; - - if (chipnum >= cfi->numchips) - break; - - if (((len + ofs -1) >> cfi->chipshift) / (cfi->interleave)) - thislen = (1<chipshift) * (cfi->interleave) - ofs; - else - thislen = len; - - ret = do_read_2_by_16_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); - if (ret) - break; - - *retlen += thislen; - len -= thislen; - buf += thislen; - - ofs = 0; - chipnum++; - } - return ret; -} - -static inline int do_write_2_by_16_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum) -{ - unsigned long timeo = jiffies + HZ; - unsigned int Last[4]; - unsigned long Count = 0; - DECLARE_WAITQUEUE(wait, current); - int ret = 0; - - retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ -printk("Waiting for chip to write, status = %d\n", chip->state); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); -printk("Wake up to write:\n"); - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - chip->state = FL_WRITING; - - adr += chip->start; - - map->write32(map, cpu_to_le32(0x00AA00AA), 0x555 *4); - map->write32(map, cpu_to_le32(0x00550055), 0x2AA *4); - map->write32(map, cpu_to_le32(0x00A000A0), 0x555 *4); - map->write32(map, cpu_to_le32(datum), adr); - - spin_unlock_bh(chip->mutex); - udelay(chip->word_write_time); - spin_lock_bh(chip->mutex); - - Last[0] = map->read32(map, adr); - Last[1] = map->read32(map, adr); - Last[2] = map->read32(map, adr); - - for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){ - udelay(10); - - Last[Count % 4] = map->read32(map, adr); - } - - if (Last[(Count - 1) % 4] != datum){ - map->write32(map, cpu_to_le32(0x00F000F0), adr); - ret = -EIO; - } - - chip->state = FL_READY; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - - return ret; -} - - -static int cfi_amdext_write_2_by_16 (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; - int chipnum; - unsigned long ofs; - - *retlen = 0; - - chipnum = (to >> cfi->chipshift); - chipnum /= cfi->interleave; - ofs = to - (chipnum << cfi->chipshift) * cfi->interleave; - - while(len > 3) { - - ret = do_write_2_by_16_oneword(map, &cfi->chips[chipnum], - ofs, *(__u32 *)buf); - if (ret) - return ret; - - ofs += 4; - buf += 4; - (*retlen) += 4; - len -= 4; - - if ((ofs >> cfi->chipshift) / cfi->interleave) { - chipnum ++; - ofs = 0; - if (chipnum == cfi->numchips) - return 0; - } - } - - if (len) { - unsigned int tmp; - - /* Final byte to write */ -#if defined(__LITTLE_ENDIAN) - tmp = map->read32(map, ofs); - - tmp = 0xffffffff >> (len*8); - tmp = tmp << (len*8); - - tmp |= *(__u32 *)(buf); - - ret = do_write_2_by_16_oneword(map, &cfi->chips[chipnum], - ofs, tmp); - -#elif defined(__BIG_ENDIAN) -#error not support big endian yet -#else -#error define a sensible endianness -#endif - - if (ret) - return ret; - - (*retlen)+=len; - } - - return 0; -} - - -static inline int do_erase_2_by_16_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) -{ - unsigned int status; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - - retry: - spin_lock_bh(chip->mutex); - - if (chip->state != FL_READY){ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if(signal_pending(current)) - return -EINTR; - - timeo = jiffies + HZ; - - goto retry; - } - - chip->state = FL_ERASING; - - adr += chip->start; - - map->write32(map, cpu_to_le32(0x00AA00AA), 0x555 *4); - map->write32(map, cpu_to_le32(0x00550055), 0x2AA *4); - map->write32(map, cpu_to_le32(0x00800080), 0x555 *4); - map->write32(map, cpu_to_le32(0x00AA00AA), 0x555 *4); - map->write32(map, cpu_to_le32(0x00550055), 0x2AA *4); - map->write32(map, cpu_to_le32(0x00300030), adr); - - - timeo = jiffies + (HZ*20); - - spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); - spin_lock_bh(chip->mutex); - - /* FIXME. Use a timer to check this, and return immediately. */ - /* Once the state machine's known to be working I'll do that */ - - while ( ( (status = le32_to_cpu(map->read32(map, 0x00))) & 0x80808080 ) != 0x80808080 ) { - static int z=0; - - if (chip->state != FL_ERASING) { - /* Someone's suspended the erase. Sleep */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - printk("erase suspended. Sleeping\n"); - - schedule(); - remove_wait_queue(&chip->wq, &wait); - - if (signal_pending(current)) - return -EINTR; - - timeo = jiffies + (HZ*2); /* FIXME */ - spin_lock_bh(chip->mutex); - continue; - } - - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - chip->state = FL_READY; - spin_unlock_bh(chip->mutex); - printk("waiting for erase to complete timed out."); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - - z++; - if ( 0 && !(z % 100 )) - printk("chip not ready yet after erase. looping\n"); - - udelay(1); - - spin_lock_bh(chip->mutex); - continue; - } - - /* Done and happy. */ - chip->state = FL_READY; - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - printk("erase ret OK\n"); - return 0; -} - -static int cfi_amdext_erase_2_by_16 (struct mtd_info *mtd, struct erase_info *instr) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; - int chipnum, ret = 0; - -//printk("erase : 0x%x 0x%x 0x%x\n", instr->addr, instr->len, mtd->size); - - if (instr->addr & (mtd->erasesize - 1)) - return -EINVAL; - - if (instr->len & (mtd->erasesize -1)) - return -EINVAL; - - if ((instr->len + instr->addr) > mtd->size) - return -EINVAL; - - chipnum = instr->addr >> cfi->chipshift; - chipnum /= cfi->interleave; - adr = instr->addr - (chipnum << cfi->chipshift) * (cfi->interleave); - len = instr->len; - - printk("erase : 0x%lx 0x%lx 0x%x 0x%lx\n", adr, len, chipnum, mtd->size); - - while(len) { -//printk("erase : 0x%x 0x%x 0x%x 0x%x\n", chipnum, adr, len, cfi->chipshift); - ret = do_erase_2_by_16_oneblock(map, &cfi->chips[chipnum], adr); - - if (ret) - return ret; - - adr += mtd->erasesize; - len -= mtd->erasesize; - - if ((adr >> cfi->chipshift) / (cfi->interleave)) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - if (instr->callback) - instr->callback(instr); - - return 0; -} - - - -static void cfi_amdext_sync (struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; - int ret = 0; - DECLARE_WAITQUEUE(wait, current); -printk("sync\n"); - - for (i=0; !ret && inumchips; i++) { - chip = &cfi->chips[i]; - - retry: - spin_lock_bh(chip->mutex); - - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_SYNCING; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - spin_unlock_bh(chip->mutex); - break; - - default: - /* Not an idle state */ - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - - schedule(); - - remove_wait_queue(&chip->wq, &wait); - - goto retry; - } - } - - /* Unlock the chips again */ - - for (i--; i >=0; i--) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_SYNCING) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - spin_unlock_bh(chip->mutex); - } -printk("sync end\n"); -} - - -static int cfi_amdext_suspend(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; - int ret = 0; -//printk("suspend\n"); - - for (i=0; !ret && inumchips; i++) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - chip->oldstate = chip->state; - chip->state = FL_PM_SUSPENDED; - /* No need to wake_up() on this state change - - * as the whole point is that nobody can do anything - * with the chip now anyway. - */ - break; - - default: - ret = -EAGAIN; - break; - } - spin_unlock_bh(chip->mutex); - } - - /* Unlock the chips again */ - - for (i--; i >=0; i--) { - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_PM_SUSPENDED) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - spin_unlock_bh(chip->mutex); - } - - return ret; -} - -static void cfi_amdext_resume(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - int i; - struct flchip *chip; -//printk("resume\n"); - - for (i=0; inumchips; i++) { - - chip = &cfi->chips[i]; - - spin_lock_bh(chip->mutex); - - if (chip->state == FL_PM_SUSPENDED) { - chip->state = chip->oldstate; - wake_up(&chip->wq); - } - else - printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - - spin_unlock_bh(chip->mutex); - } -} - -static void cfi_amdext_destroy(struct mtd_info *mtd) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - kfree(cfi->cmdset_priv); - inter_module_put(cfi->im_name); - kfree(cfi); -} - - -static int __init cfi_amdext_init(void) -{ - inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); - return 0; -} - -static void __exit cfi_amdext_exit(void) -{ - inter_module_unregister(im_name); -} - -module_init(cfi_amdext_init); -module_exit(cfi_amdext_exit); diff --git a/drivers/mtd/cfi_probe.c b/drivers/mtd/cfi_probe.c deleted file mode 100644 index f44ba58173ee..000000000000 --- a/drivers/mtd/cfi_probe.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - Common Flash Interface probe code. - (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.12 2000/07/03 13:29:16 dwmw2 Exp $ -*/ - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -struct mtd_info *cfi_probe(struct map_info *); - -static void print_cfi_ident(struct cfi_ident *); -static void check_cmd_set(struct map_info *, int, unsigned long); -static struct cfi_private *cfi_cfi_probe(struct map_info *); - -static const char im_name[] = "cfi_probe"; - -/* This routine is made available to other mtd code via - * inter_module_register. It must only be accessed through - * inter_module_get which will bump the use count of this module. The - * addresses passed back in mtd are valid as long as the use count of - * this module is non-zero, i.e. between inter_module_get and - * inter_module_put. Keith Owens 29 Oct 2000. - */ -struct mtd_info *cfi_probe(struct map_info *map) -{ - struct mtd_info *mtd = NULL; - struct cfi_private *cfi; - /* First probe the map to see if we have CFI stuff there. */ - cfi = cfi_cfi_probe(map); - - if (!cfi) - return NULL; - - map->fldrv_priv = cfi; - map->im_name = im_name; - - /* OK we liked it. Now find a driver for the command set it talks */ - - check_cmd_set(map, 1, cfi->chips[0].start); /* First the primary cmdset */ - check_cmd_set(map, 0, cfi->chips[0].start); /* Then the secondary */ - - /* check_cmd_set() will have used inter_module_get to increase - the use count of the module which provides the command set - driver. If we're quitting, we have to decrease it again. - */ - - if(cfi->cmdset_setup) { - mtd = cfi->cmdset_setup(map); - - if (mtd) - return mtd; - inter_module_put(cfi->im_name); - } - printk("No supported Vendor Command Set found\n"); - - kfree(cfi); - map->fldrv_priv = NULL; - return NULL; - -} - -static int cfi_probe_new_chip(struct map_info *map, unsigned long base, - struct flchip *chips, struct cfi_private *cfi) -{ - switch (map->buswidth) { - - case 1: { - unsigned char tmp = map->read8(map, base + 0x55); - - /* If there's a device there, put it in Query Mode */ - map->write8(map, 0x98, base+0x55); - - if (map->read8(map,base+0x10) == 'Q' && - map->read8(map,base+0x11) == 'R' && - map->read8(map,base+0x12) == 'Y') { - printk("%s: Found a CFI device at 0x%lx in 8 bit mode\n", map->name, base); - if (chips) { - /* Check previous chips for aliases */ - printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); - /* Put it back into Read Mode */ - map->write8(map, 0x98, base+0x55); - } - return 1; - } else { - if (map->read8(map, base + 0x55) == 0x98) { - /* It looks like RAM. Put back the stuff we overwrote */ - map->write8(map, tmp, base+0x55); - } - return 0; - } - } - - case 2: { - __u16 tmp = map->read16(map, base + 0xaa); - - /* If there's a device there, put it into Query Mode */ - map->write16(map, 0x9898, base+0xAA); - - if (map->read16(map, base+0x20) == cpu_to_le16(0x0051) && - map->read16(map, base+0x22) == cpu_to_le16(0x0052) && - map->read16(map, base+0x24) == cpu_to_le16(0x0059)) { - printk("%s: Found a CFI device at 0x%lx in 16 bit mode\n", map->name, base); - if (chips) { - /* Check previous chips for aliases */ - int i; - - for (i=0; i < cfi->numchips; i++) { - /* This chip should be in read mode if it's one - we've already touched. */ - if (map->read16(map, chips[i].start+0x20) == cpu_to_le16(0x0051) && - map->read16(map, chips[i].start+0x22) == cpu_to_le16(0x0052) && - map->read16(map, chips[i].start+0x24) == cpu_to_le16(0x0059)){ - /* Either the old chip has got 'Q''R''Y' in a most - unfortunate place, or it's an alias of the new - chip. Double-check that it's in read mode, and check. */ - map->write16(map, 0xffff, chips[i].start+0x20); - if (map->read16(map, chips[i].start+0x20) == cpu_to_le16(0x0051) && - map->read16(map, chips[i].start+0x22) == cpu_to_le16(0x0052) && - map->read16(map, chips[i].start+0x24) == cpu_to_le16(0x0059)) { - /* Yes it's got QRY for data. Most unfortunate. - Stick the old one in read mode too. */ - map->write16(map, 0xffff, base); - if (map->read16(map, base+0x20) == cpu_to_le16(0x0051) && - map->read16(map, base+0x22) == cpu_to_le16(0x0052) && - map->read16(map, base+0x24) == cpu_to_le16(0x0059)) { - /* OK, so has the new one. Assume it's an alias */ - printk("T'was probably an alias for the chip at 0x%lx\n", chips[i].start); - return 1; - } /* else no, they're different, fall through. */ - } else { - /* No the old one hasn't got QRY for data. Therefore, - it's an alias of the new one. */ - map->write16(map, 0xffff, base+0xaa); - /* Just to be paranoid. */ - map->write16(map, 0xffff, chips[i].start+0xaa); - printk("T'was an alias for the chip at 0x%lx\n", chips[i].start); - return 1; - } - } - /* No, the old one didn't look like it's in query mode. Next. */ - } - - /* OK, if we got to here, then none of the previous chips appear to - be aliases for the current one. */ - if (cfi->numchips == MAX_CFI_CHIPS) { - printk("%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); - /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ - return 1; - } - printk("Not an alias. Adding\n"); - chips[cfi->numchips].start = base; - chips[cfi->numchips].state = FL_READY; - chips[cfi->numchips].mutex = &chips[cfi->numchips]._spinlock; - cfi->numchips++; - - /* Put it back into Read Mode */ - map->write16(map, 0xffff, base+0xaa); - } - - return 1; - } - else if (map->read16(map, base+0x20) == 0x5151 && - map->read16(map, base+0x22) == 0x5252 && - map->read16(map, base+0x24) == 0x5959) { - printk("%s: Found a coupled pair of CFI devices at %lx in 8 bit mode\n", - map->name, base); - if (chips) { - /* Check previous chips for aliases */ - printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); - - /* Put it back into Read Mode */ - map->write16(map, 0xffff, base+0xaa); - } - - return 2; - } else { - if (map->read16(map, base+0xaa) == 0x9898) { - /* It looks like RAM. Put back the stuff we overwrote */ - map->write16(map, tmp, base+0xaa); - } - return 0; - } - } - - - case 4: { - __u32 tmp = map->read32(map, base+0x154); - - /* If there's a device there, put it into Query Mode */ - map->write32(map, 0x98989898, base+0x154); - - if (map->read32(map, base+0x40) == cpu_to_le32(0x00000051) && - map->read32(map, base+0x44) == cpu_to_le32(0x00000052) && - map->read32(map, base+0x48) == cpu_to_le32(0x00000059)) { - /* This isn't actually in the CFI spec - only x8 and x16 are. */ - printk("%s: Found a CFI device at %lx in 32 bit mode\n", map->name, base); - if (chips) { - /* Check previous chips for aliases */ - printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); - - /* Put it back into read mode */ - map->write32(map, 0xffffffff, base+0x154); - } - return 1; - } - else if (map->read32(map, base+0x40) == cpu_to_le32(0x00510051) && - map->read32(map, base+0x44) == cpu_to_le32(0x00520052) && - map->read32(map, base+0x48) == cpu_to_le32(0x00590059)) { - printk("%s: Found a coupled pair of CFI devices at location %lx in 16 bit mode\n", map->name, base); - if (chips) { - /* Check previous chips for aliases */ - printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); - - /* Put it back into read mode */ - map->write32(map, 0xffffffff, base+0x154); - } - return 2; - } - else if (map->read32(map, base+0x40) == 0x51515151 && - map->read32(map, base+0x44) == 0x52525252 && - map->read32(map, base+0x48) == 0x59595959) { - printk("%s: Found four side-by-side CFI devices at location %lx in 8 bit mode\n", map->name, base); - if (chips) { - /* Check previous chips for aliases */ - printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); - - /* Put it back into read mode */ - map->write32(map, 0xffffffff, base+0x154); - } - return 4; - } else { - if (map->read32(map, base+0x154) == 0x98989898) { - /* It looks like RAM. Put back the stuff we overwrote */ - map->write32(map, tmp, base+0x154); - } - return 0; - } - } - default: - printk(KERN_WARNING "cfi_probe called with strange buswidth %d\n", map->buswidth); - return 0; - } -} - -static struct cfi_private *cfi_cfi_probe(struct map_info *map) -{ - unsigned long base=0; - struct cfi_private cfi; - struct cfi_private *retcfi; - struct flchip chip[MAX_CFI_CHIPS]; - int i; - - memset(&cfi, 0, sizeof(cfi)); - - /* The first invocation (with chips == NULL) leaves the device in Query Mode */ - cfi.interleave = cfi_probe_new_chip(map, 0, NULL, NULL); - - if (!cfi.interleave) { - printk("%s: Found no CFI device at location zero\n", map->name); - /* Doesn't appear to be CFI-compliant at all */ - return NULL; - } - - /* Read the Basic Query Structure from the device */ - - for (i=0; iread8(map,base + ((0x10 + i)*map->buswidth)); - } - - /* Do any necessary byteswapping */ - cfi.cfiq.P_ID = le16_to_cpu(cfi.cfiq.P_ID); - cfi.cfiq.P_ADR = le16_to_cpu(cfi.cfiq.P_ADR); - cfi.cfiq.A_ID = le16_to_cpu(cfi.cfiq.A_ID); - cfi.cfiq.A_ADR = le16_to_cpu(cfi.cfiq.A_ADR); - cfi.cfiq.InterfaceDesc = le16_to_cpu(cfi.cfiq.InterfaceDesc); - cfi.cfiq.MaxBufWriteSize = le16_to_cpu(cfi.cfiq.MaxBufWriteSize); - -#if 1 - /* Dump the information therein */ - print_cfi_ident(&cfi.cfiq); - - for (i=0; iread8(map,base + ((0x2d + (4*i))*map->buswidth))) + - (((map->read8(map,(0x2e + (4*i))*map->buswidth)) << 8)); - __u16 EraseRegionInfoSize = (map->read8(map, base + ((0x2f + (4*i))*map->buswidth))) + - (map->read8(map, base + ((0x30 + (4*i))*map->buswidth)) << 8); - - printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks\n", - i, EraseRegionInfoSize * 256, EraseRegionInfoNum+1); - } - - printk("\n"); -#endif - - /* Switch the chip back into Read Mode, to make the alias detection work */ - switch(map->buswidth) { - case 1: - map->write8(map, 0xff, 0x55); - break; - case 2: - map->write16(map, 0xffff, 0xaa); - break; - case 4: - map->write32(map, 0xffffffff, 0x154); - break; - } - - /* OK. We've worked out what it is and we're happy with it. Now see if there are others */ - - chip[0].start = 0; - chip[0].state = FL_READY; - chip[0].mutex = &chip[0]._spinlock; - - cfi.chipshift = cfi.cfiq.DevSize; - cfi.numchips = 1; - - if (!cfi.chipshift) { - printk("cfi.chipsize is zero. This is bad. cfi.cfiq.DevSize is %d\n", cfi.cfiq.DevSize); - return NULL; - } - - for (base = (1<size; base += (1<chips[0], chip, sizeof(struct flchip) * cfi.numchips); - for (i=0; i< retcfi->numchips; i++) { - init_waitqueue_head(&retcfi->chips[i].wq); - spin_lock_init(&retcfi->chips[i]._spinlock); - } - return retcfi; -} - -static char *vendorname(__u16 vendor) -{ - switch (vendor) { - case P_ID_NONE: - return "None"; - - case P_ID_INTEL_EXT: - return "Intel/Sharp Extended"; - - case P_ID_AMD_STD: - return "AMD/Fujitsu Standard"; - - case P_ID_INTEL_STD: - return "Intel/Sharp Standard"; - - case P_ID_AMD_EXT: - return "AMD/Fujitsu Extended"; - - case P_ID_MITSUBISHI_STD: - return "Mitsubishi Standard"; - - case P_ID_MITSUBISHI_EXT: - return "Mitsubishi Extended"; - - case P_ID_RESERVED: - return "Not Allowed / Reserved for Future Use"; - - default: - return "Unknown"; - } -} - - -static void print_cfi_ident(struct cfi_ident *cfip) -{ - if (cfip->qry[0] != 'Q' || cfip->qry[1] != 'R' || cfip->qry[2] != 'Y') { - printk("Invalid CFI ident structure.\n"); - return; - } - - printk("Primary Vendor Command Set: %4.4X (%s)\n", cfip->P_ID, vendorname(cfip->P_ID)); - if (cfip->P_ADR) - printk("Primary Algorithm Table at %4.4X\n", cfip->P_ADR); - else - printk("No Primary Algorithm Table\n"); - - printk("Alternative Vendor Command Set: %4.4X (%s)\n", cfip->A_ID, vendorname(cfip->A_ID)); - if (cfip->A_ADR) - printk("Alternate Algorithm Table at %4.4X\n", cfip->A_ADR); - else - printk("No Alternate Algorithm Table\n"); - - - printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); - printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); - if (cfip->VppMin) { - printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); - printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); - } - else - printk("No Vpp line\n"); - - printk("Typical byte/word write timeout: %d µs\n", 1<WordWriteTimeoutTyp); - printk("Maximum byte/word write timeout: %d µs\n", (1<WordWriteTimeoutMax) * (1<WordWriteTimeoutTyp)); - - if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) { - printk("Typical full buffer write timeout: %d µs\n", 1<BufWriteTimeoutTyp); - printk("Maximum full buffer write timeout: %d µs\n", (1<BufWriteTimeoutMax) * (1<BufWriteTimeoutTyp)); - } - else - printk("Full buffer write not supported\n"); - - printk("Typical block erase timeout: %d µs\n", 1<BlockEraseTimeoutTyp); - printk("Maximum block erase timeout: %d µs\n", (1<BlockEraseTimeoutMax) * (1<BlockEraseTimeoutTyp)); - if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) { - printk("Typical chip erase timeout: %d µs\n", 1<ChipEraseTimeoutTyp); - printk("Maximum chip erase timeout: %d µs\n", (1<ChipEraseTimeoutMax) * (1<ChipEraseTimeoutTyp)); - } - else - printk("Chip erase not supported\n"); - - printk("Device size: 0x%X bytes (%d Mb)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20)); - printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc); - switch(cfip->InterfaceDesc) { - case 0: - printk(" - x8-only asynchronous interface\n"); - break; - - case 1: - printk(" - x16-only asynchronous interface\n"); - break; - - case 2: - printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n"); - break; - - case 3: - printk(" - x32-only asynchronous interface\n"); - break; - - case 65535: - printk(" - Not Allowed / Reserved\n"); - break; - - default: - printk(" - Unknown\n"); - break; - } - - printk("Max. bytes in buffer write: 0x%x\n", 1<< cfip->MaxBufWriteSize); - printk("Number of Erase Block Regions: %d\n", cfip->NumEraseRegions); - -} - -static void check_cmd_set(struct map_info *map, int primary, unsigned long base) -{ - __u16 adr; - struct cfi_private *cfi = map->fldrv_priv; - __u16 type = primary?cfi->cfiq.P_ID:cfi->cfiq.A_ID; - char probename[32]; - void (*probe_function)(struct map_info *, int, unsigned long); - - if (type == P_ID_NONE || type == P_ID_RESERVED) - return; - - sprintf(probename, "cfi_cmdset_%4.4X", type); - - probe_function = inter_module_get_request(probename, probename); - if (probe_function) { - (*probe_function)(map, primary, base); - return; - } - - /* This was a command set we don't know about. Print only the basic info */ - adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; - - if (!adr) { - printk(" No Extended Query Table\n"); - } - else if (map->read8(map,base+(adr*map->buswidth)) != (primary?'P':'A') || - map->read8(map,base+((adr+1)*map->buswidth)) != (primary?'R':'L') || - map->read8(map,base+((adr+2)*map->buswidth)) != (primary?'I':'T')) { - printk ("Invalid Extended Query Table at %4.4X: %2.2X %2.2X %2.2X\n", - adr, - map->read8(map,base+(adr*map->buswidth)), - map->read8(map,base+((adr+1)*map->buswidth)), - map->read8(map,base+((adr+2)*map->buswidth))); - } - else { - printk(" Extended Query Table version %c.%c\n", - map->read8(map,base+((adr+3)*map->buswidth)), - map->read8(map,base+((adr+4)*map->buswidth))); - } -} - -static int __init cfi_probe_init(void) -{ - inter_module_register(im_name, THIS_MODULE, &cfi_probe); - return 0; -} - -static void __exit cfi_probe_exit(void) -{ - inter_module_unregister(im_name); -} - -module_init(cfi_probe_init); -module_exit(cfi_probe_exit); diff --git a/drivers/mtd/chips/Config.in b/drivers/mtd/chips/Config.in new file mode 100644 index 000000000000..0dae3e740ec3 --- /dev/null +++ b/drivers/mtd/chips/Config.in @@ -0,0 +1,42 @@ +# drivers/mtd/chips/Config.in + +# $Id: Config.in,v 1.4 2001/05/14 09:48:12 dwmw2 Exp $ + +mainmenu_option next_comment + +comment 'RAM/ROM/Flash chip drivers' + +dep_tristate ' Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD +if [ "$CONFIG_MTD_CFI" = "y" -o "$CONFIG_MTD_CFI" = "m" ]; then + bool ' CFI Virtual erase regions (EXPERIMENTAL)' CONFIG_MTD_CFI_VIRTUAL_ER + bool ' CFI Advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS + if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then + choice 'Flash cmd/query data swapping' \ + "NO CONFIG_MTD_CFI_NOSWAP \ + BIG_ENDIAN_BYTE CONFIG_MTD_CFI_BE_BYTE_SWAP \ + LITTLE_ENDIAN_BYTE CONFIG_MTD_CFI_LE_BYTE_SWAP \ + LART_ENDIAN_BYTE CONFIG_MTD_CFI_LART_BIT_SWAP" NO + bool ' Specific CFI Flash geometry selection' CONFIG_MTD_CFI_GEOMETRY + if [ "$CONFIG_MTD_CFI_GEOMETRY" = "y" ]; then + bool ' Support 8-bit buswidth' CONFIG_MTD_CFI_B1 + bool ' Support 16-bit buswidth' CONFIG_MTD_CFI_B2 + bool ' Support 32-bit buswidth' CONFIG_MTD_CFI_B4 + if [ "$CONFIG_MTD_CFI_B1" = "y" ]; then + define_bool CONFIG_MTD_CFI_I1 y + else + bool ' Support 1-chip flash interleave' CONFIG_MTD_CFI_I1 + fi + bool ' Support 2-chip flash interleave' CONFIG_MTD_CFI_I2 + bool ' Support 4-chip flash interleave' CONFIG_MTD_CFI_I4 + fi + fi +fi +dep_tristate ' CFI support for Intel/Sharp Basic/Extended Commands' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI +dep_tristate ' CFI support for AMD/Fujitsu Standard Commands' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI +dep_tristate ' AMD compatible flash chip support (non-CFI)' CONFIG_MTD_AMDSTD $CONFIG_MTD +dep_tristate ' pre-CFI Sharp chip support' CONFIG_MTD_SHARP $CONFIG_MTD +dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD +dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD +dep_tristate ' JEDEC device support' CONFIG_MTD_JEDEC $CONFIG_MTD + +endmenu diff --git a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile new file mode 100644 index 000000000000..8dcb4a67fa59 --- /dev/null +++ b/drivers/mtd/chips/Makefile @@ -0,0 +1,27 @@ +# +# linux/drivers/chips/Makefile +# +# $Id: Makefile,v 1.4 2001/06/09 19:57:57 dwmw2 Exp $ + +O_TARGET := chipslink.o + +export-objs := chipreg.o + +# *** BIG UGLY NOTE *** +# +# The removal of get_module_symbol() and replacement with +# inter_module_register() et al has introduced a link order dependency +# here where previously there was none. We now have to ensure that +# the CFI command set drivers are linked before cfi_probe.o + +obj-$(CONFIG_MTD) += chipreg.o +obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o +obj-$(CONFIG_MTD_CFI) += cfi_probe.o cfi_jedec.o +obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o +obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o +obj-$(CONFIG_MTD_JEDEC) += jedec.o +obj-$(CONFIG_MTD_RAM) += map_ram.o +obj-$(CONFIG_MTD_ROM) += map_rom.o +obj-$(CONFIG_MTD_SHARP) += sharp.o + +include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c new file mode 100644 index 000000000000..b2b127c3af70 --- /dev/null +++ b/drivers/mtd/chips/amd_flash.c @@ -0,0 +1,1251 @@ +/* + * MTD map driver for AMD compatible flash chips (non-CFI) + * + * Author: Jonas Holmberg + * + * $Id: amd_flash.c,v 1.8 2001/06/02 14:47:16 dwmw2 Exp $ + * + * Copyright (c) 2001 Axis Communications AB + * + * This file is under GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* There's no limit. It exists only to avoid realloc. */ +#define MAX_AMD_CHIPS 8 + +#define DEVICE_TYPE_X8 (8 / 8) +#define DEVICE_TYPE_X16 (16 / 8) +#define DEVICE_TYPE_X32 (32 / 8) + +/* Addresses */ +#define ADDR_MANUFACTURER 0x0000 +#define ADDR_DEVICE_ID 0x0001 +#define ADDR_UNLOCK_1 0x0555 +#define ADDR_UNLOCK_2 0x02AA + +/* Commands */ +#define CMD_UNLOCK_DATA_1 0x00AA +#define CMD_UNLOCK_DATA_2 0x0055 +#define CMD_MANUFACTURER_UNLOCK_DATA 0x0090 +#define CMD_UNLOCK_BYPASS_MODE 0x0020 +#define CMD_PROGRAM_UNLOCK_DATA 0x00A0 +#define CMD_RESET_DATA 0x00F0 +#define CMD_SECTOR_ERASE_UNLOCK_DATA 0x0080 +#define CMD_SECTOR_ERASE_UNLOCK_DATA_2 0x0030 + +/* Manufacturers */ +#define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_FUJITSU 0x0004 +#define MANUFACTURER_ST 0x0020 +#define MANUFACTURER_SST 0x00BF +#define MANUFACTURER_TOSHIBA 0x0098 + +/* AMD */ +#define AM29F800BB 0x2258 +#define AM29F800BT 0x22D6 +#define AM29LV800BB 0x225B +#define AM29LV800BT 0x22DA +#define AM29LV160DT 0x22C4 +#define AM29LV160DB 0x2249 + +/* Fujitsu */ +#define MBM29LV160TE 0x22C4 +#define MBM29LV160BE 0x2249 + +/* ST - www.st.com */ +#define M29W800T 0x00D7 +#define M29W160DT 0x22C4 +#define M29W160DB 0x2249 + +/* SST */ +#define SST39LF800 0x2781 +#define SST39LF160 0x2782 + +/* Toshiba */ +#define TC58FVT160 0x00C2 +#define TC58FVB160 0x0043 + +#define D6_MASK 0x40 + +struct amd_flash_private { + int device_type; + int interleave; + int numchips; + unsigned long chipshift; +// const char *im_name; + struct flchip chips[0]; +}; + +struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const u_long size; + const int numeraseregions; + const struct mtd_erase_region_info regions[4]; +}; + + + +static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *, + u_char *); +static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *, + const u_char *); +static int amd_flash_erase(struct mtd_info *, struct erase_info *); +static void amd_flash_sync(struct mtd_info *); +static int amd_flash_suspend(struct mtd_info *); +static void amd_flash_resume(struct mtd_info *); +static void amd_flash_destroy(struct mtd_info *); +static struct mtd_info *amd_flash_probe(struct map_info *map); + + +static struct mtd_chip_driver amd_flash_chipdrv = { + probe: amd_flash_probe, + destroy: amd_flash_destroy, + name: "amd_flash", + module: THIS_MODULE +}; + + + +static const char im_name[] = "amd_flash"; + + + +static inline __u32 wide_read(struct map_info *map, __u32 addr) +{ + if (map->buswidth == 1) { + return map->read8(map, addr); + } else if (map->buswidth == 2) { + return map->read16(map, addr); + } else if (map->buswidth == 4) { + return map->read32(map, addr); + } + + return 0; +} + +static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) +{ + if (map->buswidth == 1) { + map->write8(map, val, addr); + } else if (map->buswidth == 2) { + map->write16(map, val, addr); + } else if (map->buswidth == 4) { + map->write32(map, val, addr); + } +} + +static inline __u32 make_cmd(struct map_info *map, __u32 cmd) +{ + const struct amd_flash_private *private = map->fldrv_priv; + if ((private->interleave == 2) && + (private->device_type == DEVICE_TYPE_X16)) { + cmd |= (cmd << 16); + } + + return cmd; +} + +static inline void send_unlock(struct map_info *map, unsigned long base) +{ + wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1, + base + (map->buswidth * ADDR_UNLOCK_1)); + wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2, + base + (map->buswidth * ADDR_UNLOCK_2)); +} + +static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd) +{ + send_unlock(map, base); + wide_write(map, make_cmd(map, cmd), + base + (map->buswidth * ADDR_UNLOCK_1)); +} + +static inline void send_cmd_to_addr(struct map_info *map, unsigned long base, + __u32 cmd, unsigned long addr) +{ + send_unlock(map, base); + wide_write(map, make_cmd(map, cmd), addr); +} + +static inline int flash_is_busy(struct map_info *map, unsigned long addr, + int interleave) +{ + + if ((interleave == 2) && (map->buswidth == 4)) { + __u32 read1, read2; + + read1 = wide_read(map, addr); + read2 = wide_read(map, addr); + + return (((read1 >> 16) & D6_MASK) != + ((read2 >> 16) & D6_MASK)) || + (((read1 & 0xffff) & D6_MASK) != + ((read2 & 0xffff) & D6_MASK)); + } + + return ((wide_read(map, addr) & D6_MASK) != + (wide_read(map, addr) & D6_MASK)); +} + + + +/* + * Reads JEDEC manufacturer ID and device ID and returns the index of the first + * matching table entry (-1 if not found or alias for already found chip). + */ +static int probe_new_chip(struct mtd_info *mtd, __u32 base, + struct flchip *chips, + struct amd_flash_private *private, + const struct amd_flash_info *table, int table_size) +{ + __u32 mfr_id, dev_id; + struct map_info *map = mtd->priv; + struct amd_flash_private temp; + int i; + + temp.device_type = DEVICE_TYPE_X16; // Assume X16 (FIXME) + temp.interleave = 2; + map->fldrv_priv = &temp; + + /* Enter autoselect mode. */ + send_cmd(map, base, CMD_RESET_DATA); + send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA); + + mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER)); + dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID)); + + if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) && + ((dev_id >> 16) == (dev_id & 0xffff))) { + mfr_id = mfr_id & 0xffff; + dev_id = dev_id & 0xffff; + } else { + temp.interleave = 1; + } + + for (i = 0; i < table_size; i++) { + if ((mfr_id == table[i].mfr_id) && + (dev_id == table[i].dev_id)) { + if (chips) { + int j; + + /* Is this an alias for an already found chip? + * In that case that chip should be in + * autoselect mode now. + */ + for (j = 0; j < private->numchips; j++) { + if ((wide_read(map, chips[j].start + + (map->buswidth * + ADDR_MANUFACTURER)) + == mfr_id) + && + (wide_read(map, chips[j].start + + (map->buswidth * + ADDR_DEVICE_ID)) + == dev_id)) { + + /* Exit autoselect mode. */ + send_cmd(map, base, + CMD_RESET_DATA); + + return -1; + } + } + + if (private->numchips == MAX_AMD_CHIPS) { + printk(KERN_WARNING + "%s: Too many flash chips " + "detected. Increase " + "MAX_AMD_CHIPS from %d.\n", + map->name, MAX_AMD_CHIPS); + + return -1; + } + + chips[private->numchips].start = base; + chips[private->numchips].state = FL_READY; + chips[private->numchips].mutex = + &chips[private->numchips]._spinlock; + private->numchips++; + } + + printk("%s: Found %d x %ldMiB %s at 0x%x\n", map->name, + temp.interleave, (table[i].size)/(1024*1024), + table[i].name, base); + + mtd->size += table[i].size * temp.interleave; + mtd->numeraseregions += table[i].numeraseregions; + + break; + } + } + + /* Exit autoselect mode. */ + send_cmd(map, base, CMD_RESET_DATA); + + if (i == table_size) { + printk(KERN_DEBUG "%s: unknown flash device at 0x%x, " + "mfr id 0x%x, dev id 0x%x\n", map->name, + base, mfr_id, dev_id); + map->fldrv_priv = NULL; + + return -1; + } + + private->device_type = temp.device_type; + private->interleave = temp.interleave; + + return i; +} + + + +static struct mtd_info *amd_flash_probe(struct map_info *map) +{ + /* Keep this table on the stack so that it gets deallocated after the + * probe is done. + */ + const struct amd_flash_info table[] = { + { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV160DT, + name: "AMD AM29LV160DT", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, + { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV160DB, + name: "AMD AM29LV160DB", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } + } + }, { + mfr_id: MANUFACTURER_TOSHIBA, + dev_id: TC58FVT160, + name: "Toshiba TC58FVT160", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, + { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_FUJITSU, + dev_id: MBM29LV160TE, + name: "Fujitsu MBM29LV160TE", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, + { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_TOSHIBA, + dev_id: TC58FVB160, + name: "Toshiba TC58FVB160", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } + } + }, { + mfr_id: MANUFACTURER_FUJITSU, + dev_id: MBM29LV160BE, + name: "Fujitsu MBM29LV160BE", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BB, + name: "AMD AM29LV800BB", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29F800BB, + name: "AMD AM29F800BB", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BT, + name: "AMD AM29LV800BT", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, + { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29F800BT, + name: "AMD AM29F800BT", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, + { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BB, + name: "AMD AM29LV800BB", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, + { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W800T, + name: "ST M29W800T", + size: 0x00100000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, + { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W160DT, + name: "ST M29W160DT", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, + { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W160DB, + name: "ST M29W160DB", + size: 0x00200000, + numeraseregions: 4, + regions: { + { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, + { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, + { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, + { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } + } + } + }; + + struct mtd_info *mtd; + struct flchip chips[MAX_AMD_CHIPS]; + int table_pos[MAX_AMD_CHIPS]; + struct amd_flash_private temp; + struct amd_flash_private *private; + u_long size; + unsigned long base; + int i; + int reg_idx; + int offset; + + mtd = (struct mtd_info*)kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_WARNING + "%s: kmalloc failed for info structure\n", map->name); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + + memset(&temp, 0, sizeof(temp)); + + printk("%s: Probing for AMD compatible flash...\n", map->name); + + if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table, + sizeof(table)/sizeof(table[0]))) + == -1) { + printk(KERN_WARNING + "%s: Found no AMD compatible device at location zero\n", + map->name); + kfree(mtd); + + return NULL; + } + + chips[0].start = 0; + chips[0].state = FL_READY; + chips[0].mutex = &chips[0]._spinlock; + temp.numchips = 1; + for (size = mtd->size; size > 1; size >>= 1) { + temp.chipshift++; + } + switch (temp.interleave) { + case 2: + temp.chipshift += 1; + break; + case 4: + temp.chipshift += 2; + break; + } + + /* Find out if there are any more chips in the map. */ + for (base = (1 << temp.chipshift); + base < map->size; + base += (1 << temp.chipshift)) { + int numchips = temp.numchips; + table_pos[numchips] = probe_new_chip(mtd, base, chips, + &temp, table, sizeof(table)/sizeof(table[0])); + } + + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * + mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_WARNING "%s: Failed to allocate " + "memory for MTD erase region info\n", map->name); + kfree(mtd); + map->fldrv_priv = NULL; + return 0; + } + + reg_idx = 0; + offset = 0; + for (i = 0; i < temp.numchips; i++) { + int dev_size; + int j; + + dev_size = 0; + for (j = 0; j < table[table_pos[i]].numeraseregions; j++) { + mtd->eraseregions[reg_idx].offset = offset + + (table[table_pos[i]].regions[j].offset * + temp.interleave); + mtd->eraseregions[reg_idx].erasesize = + table[table_pos[i]].regions[j].erasesize * + temp.interleave; + mtd->eraseregions[reg_idx].numblocks = + table[table_pos[i]].regions[j].numblocks; + if (mtd->erasesize < + mtd->eraseregions[reg_idx].erasesize) { + mtd->erasesize = + mtd->eraseregions[reg_idx].erasesize; + } + dev_size += mtd->eraseregions[reg_idx].erasesize * + mtd->eraseregions[reg_idx].numblocks; + reg_idx++; + } + offset += dev_size; + } + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + mtd->erase = amd_flash_erase; + mtd->read = amd_flash_read; + mtd->write = amd_flash_write; + mtd->sync = amd_flash_sync; + mtd->suspend = amd_flash_suspend; + mtd->resume = amd_flash_resume; + + private = kmalloc(sizeof(*private) + (sizeof(struct flchip) * + temp.numchips), GFP_KERNEL); + if (!private) { + printk(KERN_WARNING + "%s: kmalloc failed for private structure\n", map->name); + kfree(mtd); + map->fldrv_priv = NULL; + return NULL; + } + memcpy(private, &temp, sizeof(temp)); + memcpy(private->chips, chips, + sizeof(struct flchip) * private->numchips); + for (i = 0; i < private->numchips; i++) { + init_waitqueue_head(&private->chips[i].wq); + spin_lock_init(&private->chips[i]._spinlock); + } + + map->fldrv_priv = private; + + map->fldrv = &amd_flash_chipdrv; + MOD_INC_USE_COUNT; + + return mtd; +} + + + +static inline int read_one_chip(struct map_info *map, struct flchip *chip, + loff_t adr, size_t len, u_char *buf) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = jiffies + HZ; + +retry: + spin_lock_bh(chip->mutex); + + if (chip->state != FL_READY){ + printk(KERN_INFO "%s: waiting for chip to read, state = %d\n", + map->name, chip->state); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if(signal_pending(current)) { + return -EINTR; + } + + timeo = jiffies + HZ; + + goto retry; + } + + adr += chip->start; + + chip->state = FL_READY; + + map->copy_from(map, buf, adr, len); + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return 0; +} + + + +static int amd_flash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct amd_flash_private *private = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + if ((from + len) > mtd->size) { + printk(KERN_WARNING "%s: read request past end of device " + "(0x%lx)\n", map->name, (unsigned long)from + len); + + return -EINVAL; + } + + /* Offset within the first chip that the first read should start. */ + chipnum = (from >> private->chipshift); + ofs = from - (chipnum << private->chipshift); + + *retlen = 0; + + while (len) { + unsigned long this_len; + + if (chipnum >= private->numchips) { + break; + } + + if ((len + ofs - 1) >> private->chipshift) { + this_len = (1 << private->chipshift) - ofs; + } else { + this_len = len; + } + + ret = read_one_chip(map, &private->chips[chipnum], ofs, + this_len, buf); + if (ret) { + break; + } + + *retlen += this_len; + len -= this_len; + buf += this_len; + + ofs = 0; + chipnum++; + } + + return ret; +} + + + +static int write_one_word(struct map_info *map, struct flchip *chip, + unsigned long adr, __u32 datum) +{ + unsigned long timeo = jiffies + HZ; + struct amd_flash_private *private = map->fldrv_priv; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + int times_left; + +retry: + spin_lock_bh(chip->mutex); + + if (chip->state != FL_READY){ + printk("%s: waiting for chip to write, state = %d\n", + map->name, chip->state); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + printk(KERN_INFO "%s: woke up to write\n", map->name); + if(signal_pending(current)) + return -EINTR; + + timeo = jiffies + HZ; + + goto retry; + } + + chip->state = FL_WRITING; + + adr += chip->start; + ENABLE_VPP(map); + send_cmd(map, chip->start, CMD_PROGRAM_UNLOCK_DATA); + wide_write(map, datum, adr); + + times_left = 500000; + while (times_left-- && flash_is_busy(map, chip->start, + private->interleave)) { + if (current->need_resched) { + spin_unlock_bh(chip->mutex); + schedule(); + spin_lock_bh(chip->mutex); + } + } + + if (!times_left) { + printk(KERN_WARNING "%s: write to 0x%lx timed out!\n", + map->name, adr); + ret = -EIO; + } else { + __u32 verify; + if ((verify = wide_read(map, adr)) != datum) { + printk(KERN_WARNING "%s: write to 0x%lx failed. " + "datum = %x, verify = %x\n", + map->name, adr, datum, verify); + ret = -EIO; + } + } + + DISABLE_VPP(map); + chip->state = FL_READY; + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return ret; +} + + + +static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len, + size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct amd_flash_private *private = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs; + unsigned long chipstart; + + *retlen = 0; + if (!len) { + return 0; + } + + chipnum = to >> private->chipshift; + ofs = to - (chipnum << private->chipshift); + chipstart = private->chips[chipnum].start; + + /* If it's not bus-aligned, do the first byte write. */ + if (ofs & (map->buswidth - 1)) { + unsigned long bus_ofs = ofs & ~(map->buswidth - 1); + int i = ofs - bus_ofs; + int n = 0; + u_char tmp_buf[4]; + __u32 datum; + + map->copy_from(map, tmp_buf, + bus_ofs + private->chips[chipnum].start, + map->buswidth); + while (len && i < map->buswidth) + tmp_buf[i++] = buf[n++], len--; + + if (map->buswidth == 2) { + datum = *(__u16*)tmp_buf; + } else if (map->buswidth == 4) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = write_one_word(map, &private->chips[chipnum], bus_ofs, + datum); + if (ret) { + return ret; + } + + ofs += n; + buf += n; + (*retlen) += n; + + if (ofs >> private->chipshift) { + chipnum++; + ofs = 0; + if (chipnum == private->numchips) { + return 0; + } + } + } + + /* We are now aligned, write as much as possible. */ + while(len >= map->buswidth) { + __u32 datum; + + if (map->buswidth == 1) { + datum = *(__u8*)buf; + } else if (map->buswidth == 2) { + datum = *(__u16*)buf; + } else if (map->buswidth == 4) { + datum = *(__u32*)buf; + } else { + return -EINVAL; + } + + ret = write_one_word(map, &private->chips[chipnum], ofs, datum); + + if (ret) { + return ret; + } + + ofs += map->buswidth; + buf += map->buswidth; + (*retlen) += map->buswidth; + len -= map->buswidth; + + if (ofs >> private->chipshift) { + chipnum++; + ofs = 0; + if (chipnum == private->numchips) { + return 0; + } + chipstart = private->chips[chipnum].start; + } + } + + if (len & (map->buswidth - 1)) { + int i = 0, n = 0; + u_char tmp_buf[2]; + __u32 datum; + + map->copy_from(map, tmp_buf, + ofs + private->chips[chipnum].start, + map->buswidth); + while (len--) { + tmp_buf[i++] = buf[n++]; + } + + if (map->buswidth == 2) { + datum = *(__u16*)tmp_buf; + } else if (map->buswidth == 4) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = write_one_word(map, &private->chips[chipnum], ofs, datum); + + if (ret) { + return ret; + } + + (*retlen) += n; + } + + return 0; +} + + + +static inline int erase_one_block(struct map_info *map, struct flchip *chip, + unsigned long adr, u_long size) +{ + unsigned long timeo = jiffies + HZ; + struct amd_flash_private *private = map->fldrv_priv; + DECLARE_WAITQUEUE(wait, current); + +retry: + spin_lock_bh(chip->mutex); + + if (chip->state != FL_READY){ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if (signal_pending(current)) { + return -EINTR; + } + + timeo = jiffies + HZ; + + goto retry; + } + + chip->state = FL_ERASING; + + adr += chip->start; + ENABLE_VPP(map); + send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA); + send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr); + + timeo = jiffies + (HZ * 20); + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + while (flash_is_busy(map, chip->start, private->interleave)) { + + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + printk(KERN_INFO "%s: erase suspended. Sleeping\n", + map->name); + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if (signal_pending(current)) { + return -EINTR; + } + + timeo = jiffies + (HZ*2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_READY; + spin_unlock_bh(chip->mutex); + printk(KERN_WARNING "%s: waiting for erase to complete " + "timed out.\n", map->name); + DISABLE_VPP(map); + + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + if (current->need_resched) + schedule(); + else + udelay(1); + + spin_lock_bh(chip->mutex); + } + + /* Verify every single word */ + { + int address; + int error = 0; + __u8 verify; + + for (address = adr; address < (adr + size); address++) { + if ((verify = map->read8(map, address)) != 0xFF) { + error = 1; + break; + } + } + if (error) { + chip->state = FL_READY; + spin_unlock_bh(chip->mutex); + printk(KERN_WARNING + "%s: verify error at 0x%x, size %ld.\n", + map->name, address, size); + DISABLE_VPP(map); + + return -EIO; + } + } + + DISABLE_VPP(map); + chip->state = FL_READY; + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return 0; +} + + + +static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct amd_flash_private *private = map->fldrv_priv; + unsigned long adr, len; + int chipnum; + int ret = 0; + int i; + int first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (instr->addr > mtd->size) { + return -EINVAL; + } + + if ((instr->len + instr->addr) > mtd->size) { + return -EINVAL; + } + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while ((i < mtd->numeraseregions) && + (instr->addr >= regions[i].offset)) { + i++; + } + i--; + + /* OK, now i is pointing at the erase region in which this + * erase request starts. Check the start of the requested + * erase range is aligned with the erase size which is in + * effect here. + */ + + if (instr->addr & (regions[i].erasesize-1)) { + return -EINVAL; + } + + /* Remember the erase region we start on. */ + + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while ((i < mtd->numeraseregions) && + ((instr->addr + instr->len) >= regions[i].offset)) { + i++; + } + + /* As before, drop back one to point at the region in which + * the address actually falls. + */ + + i--; + + if ((instr->addr + instr->len) & (regions[i].erasesize-1)) { + return -EINVAL; + } + + chipnum = instr->addr >> private->chipshift; + adr = instr->addr - (chipnum << private->chipshift); + len = instr->len; + + i = first; + + while (len) { + ret = erase_one_block(map, &private->chips[chipnum], adr, + regions[i].erasesize); + + if (ret) { + return ret; + } + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if ((adr % (1 << private->chipshift)) == + ((regions[i].offset + (regions[i].erasesize * + regions[i].numblocks)) + % (1 << private->chipshift))) { + i++; + } + + if (adr >> private->chipshift) { + adr = 0; + chipnum++; + if (chipnum >= private->numchips) { + break; + } + } + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) { + instr->callback(instr); + } + + return 0; +} + + + +static void amd_flash_sync(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct amd_flash_private *private = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + for (i = 0; !ret && (i < private->numchips); i++) { + chip = &private->chips[i]; + + retry: + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + case FL_SYNCING: + spin_unlock_bh(chip->mutex); + break; + + default: + /* Not an idle state */ + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + + remove_wait_queue(&chip->wq, &wait); + + goto retry; + } + } + + /* Unlock the chips again */ + for (i--; i >= 0; i--) { + chip = &private->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_SYNCING) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } +} + + + +static int amd_flash_suspend(struct mtd_info *mtd) +{ +printk("amd_flash_suspend(): not implemented!\n"); + return -EINVAL; +} + + + +static void amd_flash_resume(struct mtd_info *mtd) +{ +printk("amd_flash_resume(): not implemented!\n"); +} + + + +static void amd_flash_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct amd_flash_private *private = map->fldrv_priv; + kfree(private); +} + +int __init amd_flash_init(void) +{ + register_mtd_chip_driver(&amd_flash_chipdrv); + return 0; +} + +void __exit amd_flash_exit(void) +{ + unregister_mtd_chip_driver(&amd_flash_chipdrv); +} + +module_init(amd_flash_init); +module_exit(amd_flash_exit); diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c new file mode 100644 index 000000000000..479e555b486c --- /dev/null +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -0,0 +1,1636 @@ +/* + * Common Flash Interface support: + * Intel Extended Vendor Command Set (ID 0x0001) + * + * (C) 2000 Red Hat. GPL'd + * + * $Id: cfi_cmdset_0001.c,v 1.80 2001/06/03 01:32:57 nico Exp $ + * + * + * 10/10/2000 Nicolas Pitre + * - completely revamped method functions so they are aware and + * independent of the flash geometry (buswidth, interleave, etc.) + * - scalability vs code size is completely set at compile-time + * (see include/linux/mtd/cfi.h for selection) + * - optimized write buffer method + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); +static void cfi_intelext_sync (struct mtd_info *); +static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_intelext_suspend (struct mtd_info *); +static void cfi_intelext_resume (struct mtd_info *); + +static void cfi_intelext_destroy(struct mtd_info *); + +void cfi_cmdset_0001(struct map_info *, int, unsigned long); + +static struct mtd_info *cfi_intelext_setup (struct map_info *); + +static struct mtd_chip_driver cfi_intelext_chipdrv = { + probe: cfi_intelext_setup, + destroy: cfi_intelext_destroy, + name: "cfi_intel", + module: THIS_MODULE +}; + +/* #define DEBUG_LOCK_BITS */ + +/* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The + * addresses passed back in cfi are valid as long as the use count of + * this module is non-zero, i.e. between inter_module_get and + * inter_module_put. Keith Owens 29 Oct 2000. + */ +void cfi_cmdset_0001(struct map_info *map, int primary, unsigned long base) +{ + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct cfi_pri_intelext *extp; + int ofs_factor = cfi->interleave * cfi->device_type; + + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + + //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); + + if (!adr) + return; + + /* Switch it into Query Mode */ + switch(CFIDEV_BUSWIDTH) { + case 1: + map->write8(map, 0x98, 0x55); + break; + case 2: + map->write16(map, 0x9898, 0xaa); + break; + case 4: + map->write32(map, 0x98989898, 0x154); + break; + } + + extp = kmalloc(sizeof(*extp), GFP_KERNEL); + if (!extp) { + printk("Failed to allocate memory\n"); + return; + } + + /* Read in the Extended Query Table */ + for (i=0; iinterleave*cfi->device_type))); + } + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { + printk(" Unknown IntelExt Extended Query version %c.%c.\n", + extp->MajorVersion, extp->MinorVersion); + kfree(extp); + return; + } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); + + + /* Tell the user about it in lots of lovely detail */ +#if 0 + printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); + printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); + printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); + printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); + printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); + printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); + printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); + printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); + printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); + printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); + for (i=9; i<32; i++) { + if (extp->FeatureSupport & (1<SuspendCmdSupport); + printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); + for (i=1; i<8; i++) { + if (extp->SuspendCmdSupport & (1<BlkStatusRegMask); + printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); + printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); + for (i=2; i<16; i++) { + if (extp->BlkStatusRegMask & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", + extp->VppOptimal >> 8, extp->VppOptimal & 0xf); +#endif + /* OK. We like it. Take over the control of it. */ + + /* Switch it into Read Mode */ + switch(CFIDEV_BUSWIDTH) { + case 1: + map->write8(map, 0xff, 0x55); + break; + case 2: + map->write16(map, 0xffff, 0xaa); + break; + case 4: + map->write32(map, 0xffffffff, 0x154); + break; + } + + + /* If there was an old setup function, decrease its use count */ + if (map->fldrv) + if(map->fldrv->module) + __MOD_DEC_USE_COUNT(map->fldrv->module); + + if (cfi->cmdset_priv) + kfree(cfi->cmdset_priv); + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 128; + cfi->chips[i].buffer_write_time = 128; + cfi->chips[i].erase_time = 1024; + } + + + map->fldrv = &cfi_intelext_chipdrv; + MOD_INC_USE_COUNT; + + cfi->cmdset_priv = extp; + +#if 1 /* Does this work? */ + cfi_send_gen_cmd(0x90, 0x55, base, map, cfi, cfi->device_type, NULL); + + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + + printk("JEDEC ID: %2.2X %2.2X\n", cfi->mfr, cfi->id); +#endif + + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + return; +} + +static struct mtd_info *cfi_intelext_setup(struct map_info *map) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + unsigned long offset = 0; + int i,j; + unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + //printk("number of CFI chips: %d\n", cfi->numchips); + + if (!mtd) { + printk("Failed to allocate memory for MTD device\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + mtd->size = devsize * cfi->numchips; + + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) + * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk("Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + for (i=0; icfiq->NumEraseRegions; i++) { + unsigned long ernum, ersize; + ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; + ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; + + if (mtd->erasesize < ersize) { + mtd->erasesize = ersize; + } + for (j=0; jnumchips; j++) { + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; + } + offset += (ersize * ernum); + } + + if (offset != devsize) { + /* Argh */ + printk("Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + kfree(mtd->eraseregions); + kfree(cfi->cmdset_priv); + return NULL; + } + + for (i=0; inumeraseregions;i++){ + printk("%d: offset=0x%x,size=0x%x,blocks=%d\n", + i,mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + } + + /* Also select the correct geometry setup too */ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + if ( cfi->cfiq->BufWriteTimeoutTyp ) { + //printk( KERN_INFO"Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } else { + //printk( KERN_INFO"Using word write method\n" ); + mtd->write = cfi_intelext_write_words; + } + mtd->sync = cfi_intelext_sync; + mtd->lock = cfi_intelext_lock; + mtd->unlock = cfi_intelext_unlock; + mtd->suspend = cfi_intelext_suspend; + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_intelext_chipdrv; + MOD_INC_USE_COUNT; + mtd->name = map->name; + return mtd; +} + + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + __u32 status, status_OK; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + int suspended = 0; + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; + + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. + * If it's in FL_ERASING state, suspend it and make it talk now. + */ + switch (chip->state) { + case FL_ERASING: + cfi_write (map, CMD(0xb0), cmd_addr); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; +// printk("Erase suspending at 0x%lx\n", cmd_addr); + for (;;) { + status = cfi_read(map, cmd_addr); + if ((status & status_OK) == status_OK) + break; + + if (time_after(jiffies, timeo)) { + /* Urgh */ + cfi_write(map, CMD(0xd0), cmd_addr); + chip->state = FL_ERASING; + spin_unlock_bh(chip->mutex); + printk("Chip not ready after erase suspended\n"); + return -EIO; + } + + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + suspended = 1; + cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; + break; + +#if 0 + case FL_WRITING: + /* Not quite yet */ +#endif + + case FL_READY: + break; + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + cfi_write(map, CMD(0x70), cmd_addr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, cmd_addr); + if ((status & status_OK) == status_OK) { + cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; + break; + } + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in read. WSM status = %x\n", status); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + map->copy_from(map, buf, adr, len); + + if (suspended) { + chip->state = chip->oldstate; + /* What if one interleaved chip has finished and the + other hasn't? The old code would leave the finished + one in READY mode. That's bad, and caused -EROFS + errors to be returned from do_erase_oneblock because + that's the only bit it checked for at the time. + As the state machine appears to explicitly allow + sending the 0x70 (Read Status) command to an erasing + chip and expecting it to be ignored, that's what we + do. */ + cfi_write(map, CMD(0xd0), cmd_addr); + cfi_write(map, CMD(0x70), cmd_addr); + } + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} + +static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + /* ofs: offset within the first chip that the first read should start */ + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<chipshift) - ofs; + else + thislen = len; + + ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + int z; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. + * Later, we can actually think about interrupting it + * if it's in FL_ERASING state. + * Not just yet, though. + */ + switch (chip->state) { + case FL_READY: + break; + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in read\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0x40), adr); + cfi_write(map, datum, adr); + chip->state = FL_WRITING; + + spin_unlock_bh(chip->mutex); + cfi_udelay(chip->word_write_time); + spin_lock_bh(chip->mutex); + + timeo = jiffies + (HZ/2); + z = 0; + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in word write\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + z++; + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + if (!z) { + chip->word_write_time--; + if (!chip->word_write_time) + chip->word_write_time++; + } + if (z > 1) + chip->word_write_time++; + + /* Done and happy. */ + DISABLE_VPP(map); + chip->state = FL_STATUS; + /* check for lock bit */ + if (status & CMD(0x02)) { + /* clear status */ + cfi_write(map, CMD(0x50), adr); + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return -EROFS; + } + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} + + +static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs; + + *retlen = 0; + if (!len) + return 0; + + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); + + /* If it's not bus-aligned, do the first byte write */ + if (ofs & (CFIDEV_BUSWIDTH-1)) { + unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + int gap = ofs - bus_ofs; + int i = 0, n = 0; + u_char tmp_buf[4]; + __u32 datum; + + while (gap--) + tmp_buf[i++] = 0xff; + while (len && i < CFIDEV_BUSWIDTH) + tmp_buf[i++] = buf[n++], len--; + while (i < CFIDEV_BUSWIDTH) + tmp_buf[i++] = 0xff; + + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], + bus_ofs, datum); + if (ret) + return ret; + + ofs += n; + buf += n; + (*retlen) += n; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + while(len >= CFIDEV_BUSWIDTH) { + __u32 datum; + + if (cfi_buswidth_is_1()) { + datum = *(__u8*)buf; + } else if (cfi_buswidth_is_2()) { + datum = *(__u16*)buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)buf; + } else { + return -EINVAL; + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], + ofs, datum); + if (ret) + return ret; + + ofs += CFIDEV_BUSWIDTH; + buf += CFIDEV_BUSWIDTH; + (*retlen) += CFIDEV_BUSWIDTH; + len -= CFIDEV_BUSWIDTH; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + if (len & (CFIDEV_BUSWIDTH-1)) { + int i = 0, n = 0; + u_char tmp_buf[4]; + __u32 datum; + + while (len--) + tmp_buf[i++] = buf[n++]; + while (i < CFIDEV_BUSWIDTH) + tmp_buf[i++] = 0xff; + + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], + ofs, datum); + if (ret) + return ret; + + (*retlen) += n; + } + + return 0; +} + + +static inline int do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long cmd_adr, timeo; + DECLARE_WAITQUEUE(wait, current); + int wbufsize, z; + + wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; + cmd_adr = adr & ~(wbufsize-1); + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. + * Later, we can actually think about interrupting it + * if it's in FL_ERASING state. + * Not just yet, though. + */ + switch (chip->state) { + case FL_READY: + break; + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + cfi_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, cmd_adr); + if ((status & status_OK) == status_OK) + break; + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in buffer write\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0xe8), cmd_adr); + chip->state = FL_WRITING_TO_BUFFER; + + z = 0; + for (;;) { + status = cfi_read(map, cmd_adr); + if ((status & status_OK) == status_OK) + break; + + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + + if (++z > 20) { + /* Argh. Not ready for write to buffer */ + cfi_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + printk("Chip not ready for buffer write. Xstatus = %x, status = %x\n", status, cfi_read(map, cmd_adr)); + return -EIO; + } + } + + /* Write length of data to come */ + cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); + + /* Write data */ + for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { + map->write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { + map->write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { + map->write32 (map, *((__u32*)buf)++, adr+z); + } else { + DISABLE_VPP(map); + return -EINVAL; + } + } + /* GO GO GO */ + cfi_write(map, CMD(0xd0), cmd_adr); + chip->state = FL_WRITING; + + spin_unlock_bh(chip->mutex); + cfi_udelay(chip->buffer_write_time); + spin_lock_bh(chip->mutex); + + timeo = jiffies + (HZ/2); + z = 0; + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + status = cfi_read(map, cmd_adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in bufwrite\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + z++; + spin_lock_bh(chip->mutex); + } + if (!z) { + chip->buffer_write_time--; + if (!chip->buffer_write_time) + chip->buffer_write_time++; + } + if (z > 1) + chip->buffer_write_time++; + + /* Done and happy. */ + DISABLE_VPP(map); + chip->state = FL_STATUS; + /* check for lock bit */ + if (status & CMD(0x02)) { + /* clear status */ + cfi_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return -EROFS; + } + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} + +static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + int ret = 0; + int chipnum; + unsigned long ofs; + + *retlen = 0; + if (!len) + return 0; + + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); + + /* If it's not bus-aligned, do the first word write */ + if (ofs & (CFIDEV_BUSWIDTH-1)) { + size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); + if (local_len > len) + local_len = len; + ret = cfi_intelext_write_words(mtd, to, local_len, + retlen, buf); + if (ret) + return ret; + ofs += local_len; + buf += local_len; + len -= local_len; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + /* Write buffer is worth it only if more than one word to write... */ + while(len > CFIDEV_BUSWIDTH) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) + size = len & ~(CFIDEV_BUSWIDTH-1); + ret = do_write_buffer(map, &cfi->chips[chipnum], + ofs, buf, size); + if (ret) + return ret; + + ofs += size; + buf += size; + (*retlen) += size; + len -= size; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + /* ... and write the remaining bytes */ + if (len > 0) { + size_t local_retlen; + ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift), + len, &local_retlen, buf); + if (ret) + return ret; + (*retlen) += local_retlen; + } + + return 0; +} + + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo; + int retries = 3; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; +retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. */ + switch (chip->state) { + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in erase\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + /* Clear the status register first */ + cfi_write(map, CMD(0x50), adr); + + /* Now erase */ + cfi_write(map, CMD(0x20), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + timeo = jiffies + (HZ*20); + for (;;) { + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ*2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk("waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + DISABLE_VPP(map); + ret = 0; + + /* We've broken this before. It doesn't hurt to be safe */ + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + status = cfi_read(map, adr); + + /* check for lock bit */ + if (status & CMD(0x3a)) { + unsigned char chipstatus = status; + if (status != CMD(status & 0xff)) { + int i; + for (i = 1; i> (cfi->device_type * 8); + } + printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); + } + /* Reset the error bits */ + cfi_write(map, CMD(0x50), adr); + cfi_write(map, CMD(0x70), adr); + + if ((chipstatus & 0x30) == 0x30) { + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); + ret = -EIO; + } else if (chipstatus & 0x02) { + /* Protection bit set */ + ret = -EROFS; + } else if (chipstatus & 0x8) { + /* Voltage */ + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); + ret = -EIO; + } else if (chipstatus & 0x20) { + if (retries--) { + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); + timeo = jiffies + HZ; + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + goto retry; + } + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); + ret = -EIO; + } + } + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return ret; +} + +int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr, len; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (instr->addr > mtd->size) + return -EINVAL; + + if ((instr->len + instr->addr) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (instr->addr & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((instr->addr + instr->len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = instr->addr >> cfi->chipshift; + adr = instr->addr - (chipnum << cfi->chipshift); + len = instr->len; + + i=first; + + while(len) { + ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +static void cfi_intelext_sync (struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + + retry: + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + case FL_SYNCING: + spin_unlock_bh(chip->mutex); + break; + + default: + /* Not an idle state */ + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + + goto retry; + } + } + + /* Unlock the chips again */ + + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_SYNCING) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } +} + +static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; +retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. */ + switch (chip->state) { + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in lock\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0x01), adr); + chip->state = FL_LOCKING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + timeo = jiffies + (HZ*2); + for (;;) { + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk("waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + /* Done and happy. */ + chip->state = FL_STATUS; + DISABLE_VPP(map); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} +static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; +#ifdef DEBUG_LOCK_BITS + int ofs_factor = cfi->interleave * cfi->device_type; +#endif + + if (ofs & (mtd->erasesize - 1)) + return -EINVAL; + + if (len & (mtd->erasesize -1)) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + while(len) { + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + if (ret) + return ret; + + adr += mtd->erasesize; + len -= mtd->erasesize; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + return 0; +} +static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; +retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. */ + switch (chip->state) { + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in unlock\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_UNLOCKING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + timeo = jiffies + (HZ*2); + for (;;) { + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk("waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the unlock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + /* Done and happy. */ + chip->state = FL_STATUS; + DISABLE_VPP(map); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} +static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; +#ifdef DEBUG_LOCK_BITS + int ofs_factor = cfi->interleave * cfi->device_type; +#endif + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + +#ifdef DEBUG_LOCK_BITS + { + unsigned long temp_adr = adr; + unsigned long temp_len = len; + + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + while (temp_len) { + printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); + temp_adr += mtd->erasesize; + temp_len -= mtd->erasesize; + } + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + } +#endif + + ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + return ret; +} + +static int cfi_intelext_suspend(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + case FL_PM_SUSPENDED: + break; + + default: + ret = -EAGAIN; + break; + } + spin_unlock_bh(chip->mutex); + } + + /* Unlock the chips again */ + + if (ret) { + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } + } + + return ret; +} + +static void cfi_intelext_resume(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + + for (i=0; inumchips; i++) { + + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + /* We need to force it back to a known state. */ + cfi_write(map, CMD(0xff), 0); + chip->state = FL_READY; + wake_up(&chip->wq); + } + + spin_unlock_bh(chip->mutex); + } +} + +static void cfi_intelext_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + kfree(cfi->cmdset_priv); + kfree(cfi); +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cfi_intelext_init init_module +#define cfi_intelext_exit cleanup_module +#endif + +static char im_name_1[]="cfi_cmdset_0001"; +static char im_name_3[]="cfi_cmdset_0003"; + + +mod_init_t cfi_intelext_init(void) +{ + inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001); + inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001); + return 0; +} + +mod_exit_t cfi_intelext_exit(void) +{ + inter_module_unregister(im_name_1); + inter_module_unregister(im_name_3); +} + +module_init(cfi_intelext_init); +module_exit(cfi_intelext_exit); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c new file mode 100644 index 000000000000..8ae961010934 --- /dev/null +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -0,0 +1,936 @@ +/* + * Common Flash Interface support: + * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) + * + * Copyright (C) 2000 Crossnet Co. + * + * 2_by_8 routines added by Simon Munton + * + * This code is GPL + * + * $Id: cfi_cmdset_0002.c,v 1.48 2001/06/03 01:32:57 nico Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define AMD_BOOTLOC_BUG + +static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *); +static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); +static void cfi_amdstd_sync (struct mtd_info *); +static int cfi_amdstd_suspend (struct mtd_info *); +static void cfi_amdstd_resume (struct mtd_info *); + +static void cfi_amdstd_destroy(struct mtd_info *); + +void cfi_cmdset_0002(struct map_info *, int, unsigned long); +static struct mtd_info *cfi_amdstd_setup (struct map_info *); + + +static struct mtd_chip_driver cfi_amdstd_chipdrv = { + probe: cfi_amdstd_setup, + destroy: cfi_amdstd_destroy, + name: "cfi_cmdset_0002", + module: THIS_MODULE +}; + +void cfi_cmdset_0002(struct map_info *map, int primary, unsigned long base) +{ + struct cfi_private *cfi = map->fldrv_priv; + unsigned char bootloc; + int ofs_factor = cfi->interleave * cfi->device_type; + int i; + __u8 major, minor; +// struct cfi_pri_intelext *extp; + + if (cfi->cfi_mode==0){ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + + cfi_send_gen_cmd(0x98, 0x55, 0, map, cfi, cfi->device_type, NULL); + + major = cfi_read_query(map, (adr+3)*ofs_factor); + minor = cfi_read_query(map, (adr+4)*ofs_factor); + + printk(" Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", + major, minor, adr); + + cfi_send_gen_cmd(0xf0, 0x55, 0, map, cfi, cfi->device_type, NULL); + + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + + /* Wheee. Bring me the head of someone at AMD. */ +#ifdef AMD_BOOTLOC_BUG + if (((major << 8) | minor) < 0x3131) { + /* CFI version 1.0 => don't trust bootloc */ + if (cfi->id & 0x80) { + printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); + bootloc = 3; /* top boot */ + } else { + bootloc = 2; /* bottom boot */ + } + } else +#endif + { + cfi_send_gen_cmd(0x98, 0x55, 0, map, cfi, cfi->device_type, NULL); + bootloc = cfi_read_query(map, (adr+15)*ofs_factor); + } + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + + for (i=0; icfiq->NumEraseRegions / 2; i++) { + int j = (cfi->cfiq->NumEraseRegions-1)-i; + __u32 swap; + + swap = cfi->cfiq->EraseRegionInfo[i]; + cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; + cfi->cfiq->EraseRegionInfo[j] = swap; + } + } + } + + /* If there was an old setup function, decrease its use count */ + if (map->fldrv) + if(map->fldrv->module) + __MOD_DEC_USE_COUNT(map->fldrv->module); + + if (cfi->cmdset_priv) + kfree(cfi->cmdset_priv); + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; + cfi->chips[i].buffer_write_time = 1<cfiq->BufWriteTimeoutTyp; + cfi->chips[i].erase_time = 1<cfiq->BlockEraseTimeoutTyp; + } + + map->fldrv = &cfi_amdstd_chipdrv; + MOD_INC_USE_COUNT; + cfi_send_gen_cmd(0xf0, 0x55, 0, map, cfi, cfi->device_type, NULL); + return; +} + +static struct mtd_info *cfi_amdstd_setup(struct map_info *map) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + printk("number of %s chips: %d\n", (cfi->cfi_mode)?"JEDEC":"CFI",cfi->numchips); + + if (!mtd) { + printk("Failed to allocate memory for MTD device\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + /* Also select the correct geometry setup too */ + mtd->size = devsize * cfi->numchips; + + if (cfi->cfiq->NumEraseRegions == 1) { + /* No need to muck about with multiple erase sizes */ + mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; + } else { + unsigned long offset = 0; + int i,j; + + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk("Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + for (i=0; icfiq->NumEraseRegions; i++) { + unsigned long ernum, ersize; + ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; + ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; + + if (mtd->erasesize < ersize) { + mtd->erasesize = ersize; + } + for (j=0; jnumchips; j++) { + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; + } + offset += (ersize * ernum); + } + if (offset != devsize) { + /* Argh */ + printk("Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + kfree(mtd->eraseregions); + kfree(cfi->cmdset_priv); + return NULL; + } + // debug + for (i=0; inumeraseregions;i++){ + printk("%d: offset=0x%x,size=0x%x,blocks=%d\n", + i,mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + } + } + + switch (CFIDEV_BUSWIDTH) + { + case 1: + case 2: + case 4: +#if 1 + if (mtd->numeraseregions > 1) + mtd->erase = cfi_amdstd_erase_varsize; + else +#endif + mtd->erase = cfi_amdstd_erase_onesize; + mtd->read = cfi_amdstd_read; + mtd->write = cfi_amdstd_write; + break; + + default: + printk("Unsupported buswidth\n"); + kfree(mtd); + kfree(cfi->cmdset_priv); + return NULL; + break; + } + mtd->sync = cfi_amdstd_sync; + mtd->suspend = cfi_amdstd_suspend; + mtd->resume = cfi_amdstd_resume; + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_amdstd_chipdrv; + mtd->name = map->name; + MOD_INC_USE_COUNT; + return mtd; +} + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = jiffies + HZ; + + retry: + cfi_spin_lock(chip->mutex); + + if (chip->state != FL_READY){ + printk("Waiting for chip to read, status = %d\n", chip->state); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + cfi_spin_unlock(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + timeo = jiffies + HZ; + + goto retry; + } + + adr += chip->start; + + chip->state = FL_READY; + + map->copy_from(map, buf, adr, len); + + wake_up(&chip->wq); + cfi_spin_unlock(chip->mutex); + + return 0; +} + +static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + /* ofs: offset within the first chip that the first read should start */ + + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<chipshift) - ofs; + else + thislen = len; + + ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) +{ + unsigned long timeo = jiffies + HZ; + unsigned int Last[4]; + unsigned long Count = 0; + struct cfi_private *cfi = map->fldrv_priv; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + retry: + cfi_spin_lock(chip->mutex); + + if (chip->state != FL_READY){ + printk("Waiting for chip to write, status = %d\n", chip->state); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + cfi_spin_unlock(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + printk("Wake up to write:\n"); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + timeo = jiffies + HZ; + + goto retry; + } + + chip->state = FL_WRITING; + + adr += chip->start; + ENABLE_VPP(map); + if (fast) { /* Unlock bypass */ + cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); + } + else { + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + + cfi_write(map, datum, adr); + + cfi_spin_unlock(chip->mutex); + cfi_udelay(chip->word_write_time); + cfi_spin_lock(chip->mutex); + + Last[0] = cfi_read(map, adr); + // printk("Last[0] is %x\n", Last[0]); + Last[1] = cfi_read(map, adr); + // printk("Last[1] is %x\n", Last[1]); + Last[2] = cfi_read(map, adr); + // printk("Last[2] is %x\n", Last[2]); + + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){ + cfi_spin_unlock(chip->mutex); + cfi_udelay(10); + cfi_spin_lock(chip->mutex); + + Last[Count % 4] = cfi_read(map, adr); + // printk("Last[%d%%4] is %x\n", Count, Last[Count%4]); + } + + if (Last[(Count - 1) % 4] != datum){ + printk("Last[%ld] is %x, datum is %x\n",(Count - 1) % 4,Last[(Count - 1) % 4],datum); + cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL); + DISABLE_VPP(map); + ret = -EIO; + } + DISABLE_VPP(map); + chip->state = FL_READY; + wake_up(&chip->wq); + cfi_spin_unlock(chip->mutex); + + return ret; +} + +static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs, chipstart; + + *retlen = 0; + if (!len) + return 0; + + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); + chipstart = cfi->chips[chipnum].start; + + /* If it's not bus-aligned, do the first byte write */ + if (ofs & (CFIDEV_BUSWIDTH-1)) { + unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + int i = ofs - bus_ofs; + int n = 0; + u_char tmp_buf[4]; + __u32 datum; + + map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + while (len && i < CFIDEV_BUSWIDTH) + tmp_buf[i++] = buf[n++], len--; + + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], + bus_ofs, datum, 0); + if (ret) + return ret; + + ofs += n; + buf += n; + (*retlen) += n; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + /* Go into unlock bypass mode */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + + /* We are now aligned, write as much as possible */ + while(len >= CFIDEV_BUSWIDTH) { + __u32 datum; + + if (cfi_buswidth_is_1()) { + datum = *(__u8*)buf; + } else if (cfi_buswidth_is_2()) { + datum = *(__u16*)buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)buf; + } else { + return -EINVAL; + } + ret = do_write_oneword(map, &cfi->chips[chipnum], + ofs, datum, cfi->fast_prog); + if (ret) { + if (cfi->fast_prog){ + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); + } + return ret; + } + + ofs += CFIDEV_BUSWIDTH; + buf += CFIDEV_BUSWIDTH; + (*retlen) += CFIDEV_BUSWIDTH; + len -= CFIDEV_BUSWIDTH; + + if (ofs >> cfi->chipshift) { + if (cfi->fast_prog){ + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); + } + + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + chipstart = cfi->chips[chipnum].start; + if (cfi->fast_prog){ + /* Go into unlock bypass mode for next set of chips */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + } + } + + if (cfi->fast_prog){ + /* Get out of unlock bypass mode */ + cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); + } + + if (len & (CFIDEV_BUSWIDTH-1)) { + int i = 0, n = 0; + u_char tmp_buf[4]; + __u32 datum; + + map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + while (len--) + tmp_buf[i++] = buf[n++]; + + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; + } else { + return -EINVAL; /* should never happen, but be safe */ + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], + ofs, datum, 0); + if (ret) + return ret; + + (*retlen) += n; + } + + return 0; +} + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + unsigned int status; + unsigned long timeo = jiffies + HZ; + struct cfi_private *cfi = map->fldrv_priv; + unsigned int rdy_mask; + DECLARE_WAITQUEUE(wait, current); + + retry: + cfi_spin_lock(chip->mutex); + + if (chip->state != FL_READY){ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + cfi_spin_unlock(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + timeo = jiffies + HZ; + + goto retry; + } + + chip->state = FL_ERASING; + + adr += chip->start; + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_write(map, CMD(0x30), adr); + + timeo = jiffies + (HZ*20); + + cfi_spin_unlock(chip->mutex); + schedule_timeout(HZ); + cfi_spin_lock(chip->mutex); + + rdy_mask = CMD(0x80); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + while ( ( (status = cfi_read(map,adr)) & rdy_mask ) != rdy_mask ) { + static int z=0; + + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + cfi_spin_unlock(chip->mutex); + printk("erase suspended. Sleeping\n"); + + schedule(); + remove_wait_queue(&chip->wq, &wait); +#if 0 + if (signal_pending(current)) + return -EINTR; +#endif + timeo = jiffies + (HZ*2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_READY; + cfi_spin_unlock(chip->mutex); + printk("waiting for erase to complete timed out."); + DISABLE_VPP(map); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet after erase. looping\n"); + + cfi_udelay(1); + + cfi_spin_lock(chip->mutex); + continue; + } + + /* Done and happy. */ + DISABLE_VPP(map); + chip->state = FL_READY; + wake_up(&chip->wq); + cfi_spin_unlock(chip->mutex); + return 0; +} + +static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr, len; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (instr->addr > mtd->size) + return -EINVAL; + + if ((instr->len + instr->addr) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (instr->addr & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((instr->addr + instr->len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = instr->addr >> cfi->chipshift; + adr = instr->addr - (chipnum << cfi->chipshift); + len = instr->len; + + i=first; + + while(len) { + ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr, len; + int chipnum, ret = 0; + + if (instr->addr & (mtd->erasesize - 1)) + return -EINVAL; + + if (instr->len & (mtd->erasesize -1)) + return -EINVAL; + + if ((instr->len + instr->addr) > mtd->size) + return -EINVAL; + + chipnum = instr->addr >> cfi->chipshift; + adr = instr->addr - (chipnum << cfi->chipshift); + len = instr->len; + + while(len) { + ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += mtd->erasesize; + len -= mtd->erasesize; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +static void cfi_amdstd_sync (struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + + retry: + cfi_spin_lock(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + case FL_SYNCING: + cfi_spin_unlock(chip->mutex); + break; + + default: + /* Not an idle state */ + add_wait_queue(&chip->wq, &wait); + + cfi_spin_unlock(chip->mutex); + + schedule(); + + remove_wait_queue(&chip->wq, &wait); + + goto retry; + } + } + + /* Unlock the chips again */ + + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + cfi_spin_lock(chip->mutex); + + if (chip->state == FL_SYNCING) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + cfi_spin_unlock(chip->mutex); + } +} + + +static int cfi_amdstd_suspend(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; +//printk("suspend\n"); + + for (i=0; !ret && inumchips; i++) { + chip = &cfi->chips[i]; + + cfi_spin_lock(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + case FL_PM_SUSPENDED: + break; + + default: + ret = -EAGAIN; + break; + } + cfi_spin_unlock(chip->mutex); + } + + /* Unlock the chips again */ + + if (ret) { + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + cfi_spin_lock(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + cfi_spin_unlock(chip->mutex); + } + } + + return ret; +} + +static void cfi_amdstd_resume(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; +//printk("resume\n"); + + for (i=0; inumchips; i++) { + + chip = &cfi->chips[i]; + + cfi_spin_lock(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = FL_READY; + cfi_write(map, CMD(0xF0), chip->start); + wake_up(&chip->wq); + } + else + printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n"); + + cfi_spin_unlock(chip->mutex); + } +} + +static void cfi_amdstd_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + kfree(cfi->cmdset_priv); + kfree(cfi); +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cfi_amdstd_init init_module +#define cfi_amdstd_exit cleanup_module +#endif + +static char im_name[]="cfi_cmdset_0002"; + +mod_init_t cfi_amdstd_init(void) +{ + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); + return 0; +} + +mod_exit_t cfi_amdstd_exit(void) +{ + inter_module_unregister(im_name); +} + +module_init(cfi_amdstd_init); +module_exit(cfi_amdstd_exit); + diff --git a/drivers/mtd/chips/cfi_jedec.c b/drivers/mtd/chips/cfi_jedec.c new file mode 100644 index 000000000000..006e81fffe0e --- /dev/null +++ b/drivers/mtd/chips/cfi_jedec.c @@ -0,0 +1,289 @@ +/* $Id: cfi_jedec.c,v 1.5 2001/06/02 14:52:23 dwmw2 Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Manufacturers */ +#define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_FUJITSU 0x0004 +#define MANUFACTURER_ATMEL 0x001f +#define MANUFACTURER_ST 0x0020 +#define MANUFACTURER_SST 0x00BF +#define MANUFACTURER_TOSHIBA 0x0098 + +/* AMD */ +#define AM29F800BB 0x2258 +#define AM29F800BT 0x22D6 +#define AM29LV800BB 0x225B +#define AM29LV800BT 0x22DA +#define AM29LV160DT 0x22C4 +#define AM29LV160DB 0x2249 + +/* Atmel */ +#define AT49BV16X4 0x00c0 +#define AT49BV16X4T 0x00c2 + +/* Fujitsu */ +#define MBM29LV160TE 0x22C4 +#define MBM29LV160BE 0x2249 + +/* ST - www.st.com */ +#define M29W800T 0x00D7 +#define M29W160DT 0x22C4 +#define M29W160DB 0x2249 + +/* SST */ +#define SST39LF800 0x2781 +#define SST39LF160 0x2782 + +/* Toshiba */ +#define TC58FVT160 0x00C2 +#define TC58FVB160 0x0043 + + +struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const int DevSize; + const int InterfaceDesc; + const int NumEraseRegions; + const ulong regions[4]; +}; + +#define ERASEINFO(size,blocks) (size<<8)|(blocks-1) + +#define SIZE_1MiB 20 +#define SIZE_2MiB 21 +#define SIZE_4MiB 22 + +static const struct amd_flash_info jedec_table[] = { + { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV160DT, + name: "AMD AM29LV160DT", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV160DB, + name: "AMD AM29LV160DB", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + mfr_id: MANUFACTURER_TOSHIBA, + dev_id: TC58FVT160, + name: "Toshiba TC58FVT160", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_FUJITSU, + dev_id: MBM29LV160TE, + name: "Fujitsu MBM29LV160TE", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_TOSHIBA, + dev_id: TC58FVB160, + name: "Toshiba TC58FVB160", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + mfr_id: MANUFACTURER_FUJITSU, + dev_id: MBM29LV160BE, + name: "Fujitsu MBM29LV160BE", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BB, + name: "AMD AM29LV800BB", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29F800BB, + name: "AMD AM29F800BB", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,15), + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BT, + name: "AMD AM29LV800BT", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29F800BT, + name: "AMD AM29F800BT", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29LV800BB, + name: "AMD AM29LV800BB", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W800T, + name: "ST M29W800T", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W160DT, + name: "ST M29W160DT", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: MANUFACTURER_ST, + dev_id: M29W160DB, + name: "ST M29W160DB", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + mfr_id: MANUFACTURER_ATMEL, + dev_id: AT49BV16X4, + name: "Atmel AT49BV16X4", + DevSize: SIZE_2MiB, + NumEraseRegions: 3, + regions: {ERASEINFO(0x02000,8), + ERASEINFO(0x08000,2), + ERASEINFO(0x10000,30) + } + }, { + mfr_id: MANUFACTURER_ATMEL, + dev_id: AT49BV16X4T, + name: "Atmel AT49BV16X4T", + DevSize: SIZE_2MiB, + NumEraseRegions: 3, + regions: {ERASEINFO(0x10000,30), + ERASEINFO(0x08000,2), + ERASEINFO(0x02000,8) + } + }, { + 0 + } +}; + +int cfi_jedec_lookup(int index, int mfr_id, int dev_id) +{ + if (index>=0){ + if (jedec_table[index].mfr_id == mfr_id && + jedec_table[index].dev_id == dev_id) return index; + } + else{ + for (index=0; jedec_table[index].mfr_id; index++){ + if (jedec_table[index].mfr_id == mfr_id && + jedec_table[index].dev_id == dev_id) return index; + } + } + return -1; +} + +int cfi_jedec_setup(struct cfi_private *p_cfi, int index) +{ +int i,num_erase_regions; + + printk("Found: %s\n",jedec_table[index].name); + + num_erase_regions = jedec_table[index].NumEraseRegions; + + p_cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL); + if (!p_cfi->cfiq) { + //xx printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name); + return -1; + } + + memset(p_cfi->cfiq,0,sizeof(struct cfi_ident)); + + p_cfi->cfiq->P_ID = P_ID_AMD_STD; + p_cfi->cfiq->NumEraseRegions = jedec_table[index].NumEraseRegions; + p_cfi->cfiq->DevSize = jedec_table[index].DevSize; + + for (i=0; icfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; + } + return 0; /* ok */ +} + diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c new file mode 100644 index 000000000000..fe5a8cdae2d2 --- /dev/null +++ b/drivers/mtd/chips/cfi_probe.c @@ -0,0 +1,671 @@ +/* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. + $Id: cfi_probe.c,v 1.60 2001/06/03 01:32:57 nico Exp $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* #define DEBUG_CFI */ + +#ifdef DEBUG_CFI +static void print_cfi_ident(struct cfi_ident *); +#endif + +int cfi_jedec_setup(struct cfi_private *p_cfi, int index); +int cfi_jedec_lookup(int index, int mfr_id, int dev_id); + +static void check_cmd_set(struct map_info *, int, unsigned long); +static struct cfi_private *cfi_cfi_probe(struct map_info *); +struct mtd_info *cfi_probe(struct map_info *map); + + +static struct mtd_chip_driver cfi_chipdrv = { + probe: cfi_probe, + name: "cfi", + module: THIS_MODULE +}; + + +struct mtd_info *cfi_probe(struct map_info *map) +{ + struct mtd_info *mtd = NULL; + struct cfi_private *cfi; + + /* First probe the map to see if we have CFI stuff there. */ + cfi = cfi_cfi_probe(map); + + if (!cfi) + return NULL; + + map->fldrv_priv = cfi; + /* OK we liked it. Now find a driver for the command set it talks */ + + check_cmd_set(map, 1, cfi->chips[0].start); /* First the primary cmdset */ + if (!map->fldrv) + check_cmd_set(map, 0, cfi->chips[0].start); /* Then the secondary */ + + /* check_cmd_set() will have used inter_module_get to increase + the use count of the module which provides the command set + driver. If we're quitting, we have to decrease it again. + */ + + if(map->fldrv) { + mtd = map->fldrv->probe(map); + /* Undo the use count we held onto from inter_module_get */ +#ifdef MODULE + if(map->fldrv->module) + __MOD_DEC_USE_COUNT(map->fldrv->module); +#endif + if (mtd) + return mtd; + } + printk(KERN_WARNING"cfi_probe: No supported Vendor Command Set found\n"); + + kfree(cfi->cfiq); + kfree(cfi); + map->fldrv_priv = NULL; + return NULL; +} + +static __u32 cfi_send_cmd(u_char cmd, __u32 base, struct map_info *map, struct cfi_private *cfi) +{ + return cfi_send_gen_cmd(cmd, 0x55, base, map, cfi, cfi->device_type, NULL); +} + +/* check for QRY, or search for jedec id. + in: interleave,type,mode + ret: table index, <0 for error + */ +static int cfi_check_qry_or_id(struct map_info *map, __u32 base, int index, + struct cfi_private *cfi) +{ + __u32 manufacturer_id, device_id; + int osf = cfi->interleave * cfi->device_type; // scale factor + + //printk("cfi_check_qry_or_id: base=0x%08lx interl=%d type=%d index=%d\n",base,cfi->interleave,cfi->device_type,index); + + switch(cfi->cfi_mode){ + case 0: + if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) && + cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) && + cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi)) + return 0; // ok ! + break; + + case 1: + manufacturer_id = cfi_read(map,base+0*osf); + device_id = cfi_read(map,base+1*osf); + //printk("cfi_check_qry_or_id: man=0x%lx,id=0x%lx\n",manufacturer_id, device_id); + + return cfi_jedec_lookup(index,manufacturer_id,device_id); + } + + return -1; // nothing found +} + +static void cfi_qry_mode(struct map_info *map, __u32 base, struct cfi_private *cfi) +{ + switch(cfi->cfi_mode){ + case 0: + /* Query */ + cfi_send_cmd(0x98, base, map, cfi); + break; + + case 1: + + /* Autoselect */ + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + break; + } +} + +static int cfi_probe_chip_1(struct map_info *map, __u32 base, + struct flchip *chips, struct cfi_private *cfi) +{ + int index; + __u32 tmp,ofs; + + ofs = cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, &tmp); + + cfi_qry_mode(map,base,cfi); + + index=cfi_check_qry_or_id(map,base,-1,cfi); + if (index<0) return -1; + + if (chips){ + int i; + + for (i=0; inumchips; i++){ + /* This chip should be in read mode if it's one + we've already touched. */ + if (cfi_check_qry_or_id(map,chips[i].start,index,cfi) >= 0){ + cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + if (cfi_check_qry_or_id(map,chips[i].start,index,cfi) >= 0){ + /* Yes it's got QRY for data. Most unfortunate. + Stick the old one in read mode too. */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + if (cfi_check_qry_or_id(map,base,index,cfi) >= 0){ + /* OK, so has the new one. Assume it's an alias */ + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", + map->name, base, chips[i].start); + return -1; + } + } else { + /* + * FIXME: Is this supposed to work? + * The third argument is already + * multiplied as this within the + * function definition. (Nicolas Pitre) + */ + cfi_send_gen_cmd(0xF0, 0, base+0xaa*cfi->interleave * cfi->device_type, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xF0, 0, chips[i].start+0xaa*cfi->interleave * cfi->device_type, map, cfi, cfi->device_type, NULL); + return -1; + } + } + } /* for i */ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ + if (cfi->numchips == MAX_CFI_CHIPS) { + printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); + /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ + return -1; + } + chips[cfi->numchips].start = base; + chips[cfi->numchips].state = FL_READY; + chips[cfi->numchips].mutex = &chips[cfi->numchips]._spinlock; + cfi->numchips++; + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + } + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", map->name, + cfi->interleave, cfi->device_type*8, base, map->buswidth*8); + + return index; +} + +/* put dev into qry mode, and try cfi and jedec modes for the given type/interleave + */ +static int cfi_probe_chip(struct map_info *map, __u32 base, + struct flchip *chips, struct cfi_private *cfi) +{ + int index; + cfi->cfi_mode=0; /* cfi mode */ + + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + break; + case CFI_DEVICETYPE_X16: + cfi->addr_unlock1 = 0xaaa; + if (map->buswidth == cfi->interleave) { + /* X16 chip(s) in X8 mode */ + cfi->addr_unlock2 = 0x555; + } else { + cfi->addr_unlock2 = 0x554; + } + break; + case CFI_DEVICETYPE_X32: + cfi->addr_unlock1 = 0x1555; + cfi->addr_unlock2 = 0xaaa; + break; + default: + return 0; + } + index = cfi_probe_chip_1(map,base,chips,cfi); + if (index>=0) return index; + + cfi->cfi_mode=1; /* jedec mode */ + index = cfi_probe_chip_1(map,base,chips,cfi); + if (index>=0) return index; + + cfi->addr_unlock1 = 0x5555; + cfi->addr_unlock2 = 0x2aaa; + index = cfi_probe_chip_1(map,base,chips,cfi); + + return index; +} + +/* + * Since probeing for CFI chips requires writing to the device problems may + * occur if the flash is not present and RAM is accessed instead. For now we + * assume that the flash is present so we don't check for RAM or replace + * possibly overwritten data. + */ +static int cfi_probe_new_chip(struct map_info *map, unsigned long base, + struct flchip *chips, struct cfi_private *cfi) +{ +int index; + switch (map->buswidth) { +#ifdef CFIDEV_BUSWIDTH_1 + case CFIDEV_BUSWIDTH_1: + cfi->interleave = CFIDEV_INTERLEAVE_1; + cfi->device_type = CFI_DEVICETYPE_X8; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + + cfi->device_type = CFI_DEVICETYPE_X16; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + break; +#endif + +#ifdef CFIDEV_BUSWIDTH_2 + case CFIDEV_BUSWIDTH_2: +#ifdef CFIDEV_INTERLEAVE_1 + cfi->interleave = CFIDEV_INTERLEAVE_1; + cfi->device_type = CFI_DEVICETYPE_X16; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; +#endif +#ifdef CFIDEV_INTERLEAVE_2 + cfi->interleave = CFIDEV_INTERLEAVE_2; + cfi->device_type = CFI_DEVICETYPE_X8; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + + cfi->device_type = CFI_DEVICETYPE_X16; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + +#endif + break; +#endif + +#ifdef CFIDEV_BUSWIDTH_4 + case CFIDEV_BUSWIDTH_4: +#ifdef CFIDEV_INTERLEAVE_4 + cfi->interleave = CFIDEV_INTERLEAVE_4; + cfi->device_type = CFI_DEVICETYPE_X16; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + + cfi->device_type = CFI_DEVICETYPE_X32; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; + + cfi->device_type = CFI_DEVICETYPE_X8; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; +#endif +#ifdef CFIDEV_INTERLEAVE_2 + cfi->interleave = CFIDEV_INTERLEAVE_2; + cfi->device_type = CFI_DEVICETYPE_X16; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; +#endif +#ifdef CFIDEV_INTERLEAVE_1 + cfi->interleave = CFIDEV_INTERLEAVE_1; + cfi->device_type = CFI_DEVICETYPE_X32; + index = cfi_probe_chip(map,base,chips,cfi); + if (index>=0) return index; +#endif + break; +#endif + default: + printk(KERN_WARNING "cfi_probe called with unsupported buswidth %d\n", map->buswidth); + return -1; + } // switch + return -1; +} + +static struct cfi_private *cfi_cfi_probe(struct map_info *map) +{ + unsigned long base=0; + struct cfi_private cfi; + struct cfi_private *retcfi; + struct flchip chip[MAX_CFI_CHIPS]; + int i,index; + char num_erase_regions; + int ofs_factor; + + memset(&cfi, 0, sizeof(cfi)); + + /* The first invocation (with chips == NULL) leaves the device in Query Mode */ + index = cfi_probe_new_chip(map, 0, NULL, &cfi); + + if (index<0) { + printk(KERN_WARNING"%s: Found no CFI device at location zero\n", map->name); + /* Doesn't appear to be CFI-compliant at all */ + return NULL; + } + + /* Read the Basic Query Structure from the device */ + + ofs_factor = cfi.interleave*cfi.device_type; + + /* First, work out the amount of space to allocate */ + if (cfi.cfi_mode==0){ + num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); + +#ifdef DEBUG_CFI + printk("Number of erase regions: %d\n", num_erase_regions); +#endif + + cfi.cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL); + if (!cfi.cfiq) { + printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name); + return NULL; + } + + memset(cfi.cfiq,0,sizeof(struct cfi_ident)); + + cfi.fast_prog=1; /* CFI supports fast programming */ + + /* CFI flash */ + for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { + ((unsigned char *)cfi.cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); + } + + /* Do any necessary byteswapping */ + cfi.cfiq->P_ID = le16_to_cpu(cfi.cfiq->P_ID); + + cfi.cfiq->P_ADR = le16_to_cpu(cfi.cfiq->P_ADR); + cfi.cfiq->A_ID = le16_to_cpu(cfi.cfiq->A_ID); + cfi.cfiq->A_ADR = le16_to_cpu(cfi.cfiq->A_ADR); + cfi.cfiq->InterfaceDesc = le16_to_cpu(cfi.cfiq->InterfaceDesc); + cfi.cfiq->MaxBufWriteSize = le16_to_cpu(cfi.cfiq->MaxBufWriteSize); + + for (i=0; iNumEraseRegions; i++) { + cfi.cfiq->EraseRegionInfo[i] = le32_to_cpu(cfi.cfiq->EraseRegionInfo[i]); + +#ifdef DEBUG_CFI + printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks\n", + i, (cfi.cfiq->EraseRegionInfo[i] >> 8) & ~0xff, + (cfi.cfiq->EraseRegionInfo[i] & 0xffff) + 1); +#endif + } + } + else{ + /* JEDEC flash */ + if (cfi_jedec_setup(&cfi,index)<0){ + printk(KERN_WARNING "cfi_jedec_setup failed\n"); + return NULL; + } + } + + if (cfi.cfiq->NumEraseRegions == 0) { + printk(KERN_WARNING "Number of erase regions is zero\n"); + kfree(cfi.cfiq); + return NULL; + } + +#ifdef DEBUG_CFI + /* Dump the information therein */ + print_cfi_ident(cfi.cfiq); +#endif + + cfi_send_cmd(0xFF, base, map, &cfi); + + /* OK. We've worked out what it is and we're happy with it. Now see if there are others */ + + chip[0].start = 0; + chip[0].state = FL_READY; + chip[0].mutex = &chip[0]._spinlock; + + cfi.chipshift = cfi.cfiq->DevSize; + cfi.numchips = 1; + + if (!cfi.chipshift) { + printk(KERN_ERR"cfi.chipsize is zero. This is bad. cfi.cfiq->DevSize is %d\n", cfi.cfiq->DevSize); + kfree(cfi.cfiq); + return NULL; + } + switch (cfi.interleave) { + case 2: cfi.chipshift += 1; break; + case 4: cfi.chipshift += 2; break; + } + + for (base = (1<size; base += (1<name); + kfree(cfi.cfiq); + return NULL; + } + memcpy(retcfi, &cfi, sizeof(cfi)); + memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); + for (i=0; i< retcfi->numchips; i++) { + init_waitqueue_head(&retcfi->chips[i].wq); + spin_lock_init(&retcfi->chips[i]._spinlock); + retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock; + } + return retcfi; +} + +#ifdef DEBUG_CFI +static char *vendorname(__u16 vendor) +{ + switch (vendor) { + case P_ID_NONE: + return "None"; + + case P_ID_INTEL_EXT: + return "Intel/Sharp Extended"; + + case P_ID_AMD_STD: + return "AMD/Fujitsu Standard"; + + case P_ID_INTEL_STD: + return "Intel/Sharp Standard"; + + case P_ID_AMD_EXT: + return "AMD/Fujitsu Extended"; + + case P_ID_MITSUBISHI_STD: + return "Mitsubishi Standard"; + + case P_ID_MITSUBISHI_EXT: + return "Mitsubishi Extended"; + + case P_ID_RESERVED: + return "Not Allowed / Reserved for Future Use"; + + default: + return "Unknown"; + } +} + + +static void print_cfi_ident(struct cfi_ident *cfip) +{ +#if 0 + if (cfip->qry[0] != 'Q' || cfip->qry[1] != 'R' || cfip->qry[2] != 'Y') { + printk("Invalid CFI ident structure.\n"); + return; + } +#endif + printk("Primary Vendor Command Set: %4.4X (%s)\n", cfip->P_ID, vendorname(cfip->P_ID)); + if (cfip->P_ADR) + printk("Primary Algorithm Table at %4.4X\n", cfip->P_ADR); + else + printk("No Primary Algorithm Table\n"); + + printk("Alternative Vendor Command Set: %4.4X (%s)\n", cfip->A_ID, vendorname(cfip->A_ID)); + if (cfip->A_ADR) + printk("Alternate Algorithm Table at %4.4X\n", cfip->A_ADR); + else + printk("No Alternate Algorithm Table\n"); + + + printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); + printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + if (cfip->VppMin) { + printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); + printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + } + else + printk("No Vpp line\n"); + + printk("Typical byte/word write timeout: %d µs\n", 1<WordWriteTimeoutTyp); + printk("Maximum byte/word write timeout: %d µs\n", (1<WordWriteTimeoutMax) * (1<WordWriteTimeoutTyp)); + + if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) { + printk("Typical full buffer write timeout: %d µs\n", 1<BufWriteTimeoutTyp); + printk("Maximum full buffer write timeout: %d µs\n", (1<BufWriteTimeoutMax) * (1<BufWriteTimeoutTyp)); + } + else + printk("Full buffer write not supported\n"); + + printk("Typical block erase timeout: %d µs\n", 1<BlockEraseTimeoutTyp); + printk("Maximum block erase timeout: %d µs\n", (1<BlockEraseTimeoutMax) * (1<BlockEraseTimeoutTyp)); + if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) { + printk("Typical chip erase timeout: %d µs\n", 1<ChipEraseTimeoutTyp); + printk("Maximum chip erase timeout: %d µs\n", (1<ChipEraseTimeoutMax) * (1<ChipEraseTimeoutTyp)); + } + else + printk("Chip erase not supported\n"); + + printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20)); + printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc); + switch(cfip->InterfaceDesc) { + case 0: + printk(" - x8-only asynchronous interface\n"); + break; + + case 1: + printk(" - x16-only asynchronous interface\n"); + break; + + case 2: + printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n"); + break; + + case 3: + printk(" - x32-only asynchronous interface\n"); + break; + + case 65535: + printk(" - Not Allowed / Reserved\n"); + break; + + default: + printk(" - Unknown\n"); + break; + } + + printk("Max. bytes in buffer write: 0x%x\n", 1<< cfip->MaxBufWriteSize); + printk("Number of Erase Block Regions: %d\n", cfip->NumEraseRegions); + +} +#endif /* DEBUG_CFI */ + +typedef void cfi_cmdset_fn_t(struct map_info *, int, unsigned long); + +extern cfi_cmdset_fn_t cfi_cmdset_0001; +extern cfi_cmdset_fn_t cfi_cmdset_0002; + +static void cfi_cmdset_unknown(struct map_info *map, int primary, unsigned long base) +{ + __u16 adr; + struct cfi_private *cfi = map->fldrv_priv; + __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; +#ifdef HAVE_INTER_MODULE + char probename[32]; + cfi_cmdset_fn_t *probe_function; + + sprintf(probename, "cfi_cmdset_%4.4X", type); + + probe_function = inter_module_get_request(probename, probename); + + if (probe_function) { + (*probe_function)(map, primary, base); + return; + } +#endif + printk(KERN_NOTICE "Support for command set %04X not present\n", type); + /* This was a command set we don't know about. Print only the basic info */ + adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + + if (!adr) { + printk(" No Extended Query Table\n"); + } + else { + int ofs_factor = cfi->interleave * cfi->device_type; + + if (cfi_read_query(map,base + adr*ofs_factor) != (primary?'P':'A') || + cfi_read_query(map,base + (adr+1)*ofs_factor) != (primary?'R':'L') || + cfi_read_query(map,base + (adr+2)*ofs_factor) != (primary?'I':'T')) { + printk ("Invalid Extended Query Table at %4.4X: %2.2X %2.2X %2.2X\n", + adr, + cfi_read_query(map,base + adr*ofs_factor), + cfi_read_query(map,base + (adr+1)*ofs_factor), + cfi_read_query(map,base + (adr+2)*ofs_factor)); + } + else { + printk(" Extended Query Table version %c.%c\n", + cfi_read_query(map,base + (adr+3)*ofs_factor), + cfi_read_query(map,base + (adr+4)*ofs_factor)); + } + } + cfi_send_cmd(0xff, base, map, cfi); +} + +static void check_cmd_set(struct map_info *map, int primary, unsigned long base) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; + + if (type == P_ID_NONE || type == P_ID_RESERVED) + return; + /* Put it in query mode */ + cfi_qry_mode(map,base,cfi); + + switch(type){ + /* Urgh. Ifdefs. The version with weak symbols was + * _much_ nicer. Shame it didn't seem to work on + * anything but x86, really. + * But we can't rely in inter_module_get() because + * that'd mean we depend on link order. + */ +#ifdef CONFIG_MTD_CFI_INTELEXT + case 0x0001: + case 0x0003: + return cfi_cmdset_0001(map, primary, base); +#endif +#ifdef CONFIG_MTD_CFI_AMDSTD + case 0x0002: + return cfi_cmdset_0002(map, primary, base); +#endif + } + + return cfi_cmdset_unknown(map, primary, base); +} + + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cfi_probe_init init_module +#define cfi_probe_exit cleanup_module +#endif + +mod_init_t cfi_probe_init(void) +{ + register_mtd_chip_driver(&cfi_chipdrv); + return 0; +} + +mod_exit_t cfi_probe_exit(void) +{ + unregister_mtd_chip_driver(&cfi_chipdrv); +} + +module_init(cfi_probe_init); +module_exit(cfi_probe_exit); diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c new file mode 100644 index 000000000000..d5da82bf8e76 --- /dev/null +++ b/drivers/mtd/chips/chipreg.c @@ -0,0 +1,92 @@ +/* + * $Id: chipreg.c,v 1.8 2001/06/09 19:58:19 dwmw2 Exp $ + * + * Registration for chip drivers + * + */ + +#include +#include +#include +#include +#include +#include + +spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(chip_drvs_list); + +void register_mtd_chip_driver(struct mtd_chip_driver *drv) +{ + spin_lock(&chip_drvs_lock); + list_add(&drv->list, &chip_drvs_list); + spin_unlock(&chip_drvs_lock); +} + +void unregister_mtd_chip_driver(struct mtd_chip_driver *drv) +{ + spin_lock(&chip_drvs_lock); + list_del(&drv->list); + spin_unlock(&chip_drvs_lock); +} + +static struct mtd_chip_driver *get_mtd_chip_driver (char *name) +{ + struct list_head *pos; + struct mtd_chip_driver *ret = NULL, *this; + + spin_lock(&chip_drvs_lock); + + list_for_each(pos, &chip_drvs_list) { + this = list_entry(pos, typeof(*this), list); + + if (!strcmp(this->name, name)) { + ret = this; + break; + } + } + if (ret && !try_inc_mod_count(ret->module)) { + /* Eep. Failed. */ + ret = NULL; + } + + spin_unlock(&chip_drvs_lock); + + return ret; +} + + /* Hide all the horrid details, like some silly person taking + get_module_symbol() away from us, from the caller. */ + +struct mtd_info *do_map_probe(char *name, struct map_info *map) +{ + struct mtd_chip_driver *drv; + struct mtd_info *ret; + + drv = get_mtd_chip_driver(name); + + if (!drv && !request_module(name)) + drv = get_mtd_chip_driver(name); + + if (!drv) + return NULL; + + ret = drv->probe(map); +#ifdef CONFIG_MODULES + /* We decrease the use count here. It may have been a + probe-only module, which is no longer required from this + point, having given us a handle on (and increased the use + count of) the actual driver code. + */ + if(drv->module) + __MOD_DEC_USE_COUNT(drv->module); +#endif + + if (ret) + return ret; + + return NULL; +} + +EXPORT_SYMBOL(register_mtd_chip_driver); +EXPORT_SYMBOL(unregister_mtd_chip_driver); +EXPORT_SYMBOL(do_map_probe); diff --git a/drivers/mtd/jedec.c b/drivers/mtd/chips/jedec.c similarity index 76% rename from drivers/mtd/jedec.c rename to drivers/mtd/chips/jedec.c index 74e9db84e1dd..b04e6d232de6 100644 --- a/drivers/mtd/jedec.c +++ b/drivers/mtd/chips/jedec.c @@ -11,7 +11,7 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.1 2000/07/04 07:21:57 jgg Exp $ + * $Id: jedec.c,v 1.8 2001/06/09 23:56:57 dwmw2 Exp $ */ #include @@ -28,15 +28,18 @@ static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start static int flash_erase(struct mtd_info *mtd, struct erase_info *instr); static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, size_t *retlen, const u_char *buf); - -EXPORT_SYMBOL(jedec_probe); + +static unsigned long my_bank_size; /* Listing of parts and sizes. We need this table to learn the sector size of the chip and the total length */ static const struct JEDECTable JEDEC_table[] = - {{0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH}, + {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH}, + {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, {}}; static void jedec_sync(struct mtd_info *mtd) {}; @@ -45,9 +48,19 @@ static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +struct mtd_info *jedec_probe(struct map_info *map); + + + +static struct mtd_chip_driver jedec_chipdrv = { + probe: jedec_probe, + name: "jedec", + module: THIS_MODULE +}; + /* Probe entry point */ - struct jedec_private priv; - struct mtd_info __MTD; +struct jedec_private priv; +struct mtd_info __MTD; struct mtd_info *jedec_probe(struct map_info *map) { struct mtd_info *MTD = &__MTD; @@ -58,26 +71,29 @@ struct mtd_info *jedec_probe(struct map_info *map) char Part[200]; memset(&priv,0,sizeof(priv)); - if (map->bank_size == 0) - map->bank_size = map->size; + my_bank_size = map->size; - if (map->size/map->bank_size > MAX_JEDEC_CHIPS) + if (map->size/my_bank_size > MAX_JEDEC_CHIPS) { printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n"); return 0; } - for (Base = 0; Base < map->size; Base += map->bank_size) + for (Base = 0; Base < map->size; Base += my_bank_size) { // Perhaps zero could designate all tests? - if (map->bus_width == 0) - map->bus_width = 8; + if (map->buswidth == 0) + map->buswidth = 1; - if (map->bus_width == 8) - jedec_probe8(map,Base,&priv); - if (map->bus_width == 16) + if (map->buswidth == 1){ + if (jedec_probe8(map,Base,&priv) == 0) { + printk("did recognize jedec chip\n"); + return 0; + } + } + if (map->buswidth == 2) jedec_probe16(map,Base,&priv); - if (map->bus_width == 32) + if (map->buswidth == 4) jedec_probe32(map,Base,&priv); } @@ -85,6 +101,8 @@ struct mtd_info *jedec_probe(struct map_info *map) SectorSize = 0; for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) { + // printk("priv.chips[%d].jedec is %x\n",I,priv.chips[I].jedec); + // printk("priv.chips[%d].sectorsize is %lx\n",I,priv.chips[I].sectorsize); if (priv.chips[I].sectorsize > SectorSize) SectorSize = priv.chips[I].sectorsize; } @@ -141,34 +159,51 @@ struct mtd_info *jedec_probe(struct map_info *map) are empty banks. Note, the last bank does not count here, only the first banks are important. Holes on non-bank boundaries can not exist due to the way the detection algorithm works. */ - if (priv.size < map->bank_size) - map->bank_size = priv.size; + if (priv.size < my_bank_size) + my_bank_size = priv.size; priv.is_banked = 0; - for (I = 0; I != priv.size/map->bank_size - 1; I++) - { - if (priv.bank_fill[I] != map->bank_size) - priv.is_banked = 1; - - /* This even could be eliminated, but new de-optimized read/write - functions have to be written */ - if (priv.bank_fill[I] != priv.bank_fill[0]) - { - printk("mtd: Failed. Cannot handle unsymetric banking\n"); - return 0; - } + //printk("priv.size is %x, my_bank_size is %x\n",priv.size,my_bank_size); + //printk("priv.bank_fill[0] is %x\n",priv.bank_fill[0]); + if (!priv.size) { + printk("priv.size is zero\n"); + return 0; + } + if (priv.size/my_bank_size) { + if (priv.size/my_bank_size == 1) { + priv.size = my_bank_size; + } + else { + for (I = 0; I != priv.size/my_bank_size - 1; I++) + { + if (priv.bank_fill[I] != my_bank_size) + priv.is_banked = 1; + + /* This even could be eliminated, but new de-optimized read/write + functions have to be written */ + printk("priv.bank_fill[%d] is %lx, priv.bank_fill[0] is %lx\n",I,priv.bank_fill[I],priv.bank_fill[0]); + if (priv.bank_fill[I] != priv.bank_fill[0]) + { + printk("mtd: Failed. Cannot handle unsymetric banking\n"); + return 0; + } + } + } } if (priv.is_banked == 1) strcat(Part,", banked"); - xprintf("Part: '%s'\n",Part); + // printk("Part: '%s'\n",Part); memset(MTD,0,sizeof(*MTD)); - strncpy(MTD->name,Part,sizeof(MTD->name)); - MTD->name[sizeof(MTD->name)-1] = 0; + // strncpy(MTD->name,Part,sizeof(MTD->name)); + // MTD->name[sizeof(MTD->name)-1] = 0; + MTD->name = map->name; MTD->type = MTD_NORFLASH; MTD->flags = MTD_CAP_NORFLASH; - MTD->erasesize = SectorSize*(map->bus_width/8); + MTD->erasesize = SectorSize*(map->buswidth); + // printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize); MTD->size = priv.size; + // printk("MTD->size is %x\n",(unsigned int)MTD->size); //MTD->module = THIS_MODULE; // ? Maybe this should be the low level module? MTD->erase = flash_erase; if (priv.is_banked == 1) @@ -179,7 +214,8 @@ struct mtd_info *jedec_probe(struct map_info *map) MTD->sync = jedec_sync; MTD->priv = map; map->fldrv_priv = &priv; - + map->fldrv = &jedec_chipdrv; + MOD_INC_USE_COUNT; return MTD; } @@ -274,10 +310,10 @@ int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count, for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++); // Determine how filled this bank is. - Bank = base & (~(map->bank_size-1)); - if (priv->bank_fill[Bank/map->bank_size] < base + + Bank = base & (~(my_bank_size-1)); + if (priv->bank_fill[Bank/my_bank_size] < base + (JEDEC->size << priv->chips[I].addrshift) - Bank) - priv->bank_fill[Bank/map->bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank; + priv->bank_fill[Bank/my_bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank; I++; } @@ -301,7 +337,53 @@ const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id) int jedec_probe8(struct map_info *map,unsigned long base, struct jedec_private *priv) { - return 0; + #define flread(x) map->read8(map,base+x) + #define flwrite(v,x) map->write8(map,v,base+x) + + const unsigned long AutoSel1 = 0xAA; + const unsigned long AutoSel2 = 0x55; + const unsigned long AutoSel3 = 0x90; + const unsigned long Reset = 0xF0; + __u32 OldVal; + __u8 Mfg[1]; + __u8 Id[1]; + unsigned I; + unsigned long Size; + + // Wait for any write/erase operation to settle + OldVal = flread(base); + for (I = 0; OldVal != flread(base) && I < 10000; I++) + OldVal = flread(base); + + // Reset the chip + flwrite(Reset,0x555); + + // Send the sequence + flwrite(AutoSel1,0x555); + flwrite(AutoSel2,0x2AA); + flwrite(AutoSel3,0x555); + + // Get the JEDEC numbers + Mfg[0] = flread(0); + Id[0] = flread(1); + // printk("Mfg is %x, Id is %x\n",Mfg[0],Id[0]); + + Size = handle_jedecs(map,Mfg,Id,1,base,priv); + // printk("handle_jedecs Size is %x\n",(unsigned int)Size); + if (Size == 0) + { + flwrite(Reset,0x555); + return 0; + } + + + // Reset. + flwrite(Reset,0x555); + + return 1; + + #undef flread + #undef flwrite } // Look for flash using a 16 bit bus interface (ie 2 8-bit chips) @@ -321,7 +403,7 @@ int jedec_probe32(struct map_info *map,unsigned long base, const unsigned long AutoSel1 = 0xAAAAAAAA; const unsigned long AutoSel2 = 0x55555555; const unsigned long AutoSel3 = 0x90909090; - const unsigned long Reset = 0x90909090; + const unsigned long Reset = 0xF0F0F0F0; __u32 OldVal; __u8 Mfg[4]; __u8 Id[4]; @@ -370,7 +452,7 @@ int jedec_probe32(struct map_info *map,unsigned long base, more flashes have a truncated address space the probe test will still work */ if (base + Size+0x555 < map->size && - base + Size+0x555 < (base & (~(map->bank_size-1))) + map->bank_size) + base + Size+0x555 < (base & (~(my_bank_size-1))) + my_bank_size) { if (flread(base+Size) != flread(base+Size + 0x100) || flread(base+Size + 1) != flread(base+Size + 0x101)) @@ -418,7 +500,7 @@ static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, get = priv->bank_fill[0] - offset; bank /= priv->bank_fill[0]; - map->copy_from(map,buf + *retlen,bank*map->bank_size + offset,get); + map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); len -= get; *retlen += get; @@ -477,16 +559,9 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) if (chip->length == 0) continue; - // Send the erase setup code - xprintf("Erase: "); - puth(chip->start); putc(' '); - puth(chip->base); putc(' '); - puth(chip->length); putc(' '); - puth(chip->sectorsize); putc('\n'); - if (chip->start + chip->length > chip->size) { - xprintf("DIE\n"); + printk("DIE\n"); return -EIO; } @@ -497,20 +572,10 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) flwrite(0xAA,chip->start + 0x555); flwrite(0x55,chip->start + 0x2AA); - // Use chip erase if possible - if (chip->start == 0 && chip->length == chip->size) - { - flwrite(0x10,0x555); - continue; - } - /* Once we start selecting the erase sectors the delay between each command must not exceed 50us or it will immediately start erasing and ignore the other sectors */ -/* how do you portably turn off interrupts? - save_flags(flags); - cli();*/ - for (off = 0; off < chip->length; off += chip->sectorsize) + for (off = 0; off < len; off += chip->sectorsize) { // Check to make sure we didn't timeout flwrite(0x30,chip->start + off); @@ -522,7 +587,6 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) return -EIO; } } -// restore_flags(flags); } /* We could split this into a timer routine and return early, performing @@ -560,9 +624,9 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) } } - xprintf("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1], + /* printk("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1], (short)todo[2],(short)todo[3]); - + */ while (1) { __u32 Last[4]; @@ -571,9 +635,23 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) /* During erase bit 7 is held low and bit 6 toggles, we watch this, should it stop toggling or go high then the erase is completed, or this is not really flash ;> */ - Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); - Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + switch (map->buswidth) { + case 1: + Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: + Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 3: + Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } Count = 3; while (todo_left != 0) { @@ -604,12 +682,20 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) /* if (NoTime == 0) Time += HZ/10 - schedule_timeout(HZ/10);*/ NoTime = 0; - - Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + + switch (map->buswidth) { + case 1: + Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: + Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 4: + Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } Count++; - putc('.'); - /* // Count time, max of 15s per sector (according to AMD) if (Time > 15*len/mtd->erasesize*HZ) { @@ -618,8 +704,6 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) } */ } - puts("out\n"); - // Skip to the next chip if we used chip erase if (chip->length == chip->size) off = chip->size; @@ -639,7 +723,9 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) } } - puts("done\n"); + //printk("done\n"); + if (instr->callback) + instr->callback(instr); return 0; #undef flread @@ -663,12 +749,14 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; unsigned long base; unsigned long off; + size_t save_len = len; if (start + len > mtd->size) return -EIO; - puts("Here"); + //printk("Here"); + //printk("flash_write: start is %x, len is %x\n",start,(unsigned long)len); while (len != 0) { struct jedec_flash_chip *chip = priv->chips; @@ -676,16 +764,17 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, unsigned long boffset; // Compute the base of the flash. - off = start % (chip->size << chip->addrshift); + off = ((unsigned long)start) % (chip->size << chip->addrshift); base = start - off; // Perform banked addressing translation. bank = base & (~(priv->bank_fill[0]-1)); boffset = base & (priv->bank_fill[0]-1); - bank = (bank/priv->bank_fill[0])*map->bank_size; + bank = (bank/priv->bank_fill[0])*my_bank_size; base = bank + boffset; - xprintf("Flasing %X %X %X\n",base,chip->size,len); + // printk("Flasing %X %X %X\n",base,chip->size,len); + // printk("off is %x, compare with %x\n",off,chip->size << chip->addrshift); // Loop over this page for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) @@ -694,10 +783,10 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, unsigned char Last[4]; unsigned long Count = 0; -// putc('.'); - - if (oldbyte == *buf) + if (oldbyte == *buf) { + // printk("oldbyte and *buf is %x,len is %x\n",oldbyte,len); continue; + } if (((~oldbyte) & *buf) != 0) printk("mtd: warn: Trying to set a 0 to a 1\n"); @@ -724,7 +813,7 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, } } } - *retlen = len; + *retlen = save_len; return 0; } @@ -771,3 +860,21 @@ static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start } } /*}}}*/ +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define jedec_probe_init init_module +#define jedec_probe_exit cleanup_module +#endif + +int __init jedec_probe_init(void) +{ + register_mtd_chip_driver(&jedec_chipdrv); + return 0; +} + +static void __exit jedec_probe_exit(void) +{ + unregister_mtd_chip_driver(&jedec_chipdrv); +} + +module_init(jedec_probe_init); +module_exit(jedec_probe_exit); diff --git a/drivers/mtd/map_ram.c b/drivers/mtd/chips/map_ram.c similarity index 80% rename from drivers/mtd/map_ram.c rename to drivers/mtd/chips/map_ram.c index 1a4e5a06a761..93b4b87a763c 100644 --- a/drivers/mtd/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.7 2000/12/10 01:39:13 dwmw2 Exp $ + * $Id: map_ram.c,v 1.11 2001/06/08 15:34:04 dwmw2 Exp $ */ #include @@ -19,22 +19,21 @@ static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int mapram_erase (struct mtd_info *, struct erase_info *); static void mapram_nop (struct mtd_info *); +static struct mtd_info *map_ram_probe(struct map_info *map); -static const char im_name[] = "map_ram_probe"; -/* This routine is made available to other mtd code via - * inter_module_register. It must only be accessed through - * inter_module_get which will bump the use count of this module. The - * addresses passed back in mtd are valid as long as the use count of - * this module is non-zero, i.e. between inter_module_get and - * inter_module_put. Keith Owens 29 Oct 2000. - */ +static struct mtd_chip_driver mapram_chipdrv = { + probe: map_ram_probe, + name: "ram", + module: THIS_MODULE +}; static struct mtd_info *map_ram_probe(struct map_info *map) { struct mtd_info *mtd; /* Check the first byte is RAM */ +#if 0 map->write8(map, 0x55, 0); if (map->read8(map, 0) != 0x55) return NULL; @@ -51,7 +50,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) map->write8(map, 0xAA, map->size-1); if (map->read8(map, map->size-1) != 0xAA) return NULL; - +#endif /* OK. It seems to be RAM. */ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); @@ -60,8 +59,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) memset(mtd, 0, sizeof(*mtd)); - map->im_name = im_name; - map->fldrv_destroy = mapram_nop; + map->fldrv = &mapram_chipdrv; mtd->priv = map; mtd->name = map->name; mtd->type = MTD_RAM; @@ -74,6 +72,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) mtd->flags = MTD_CAP_RAM | MTD_VOLATILE; mtd->erasesize = PAGE_SIZE; + MOD_INC_USE_COUNT; return mtd; } @@ -124,13 +123,13 @@ static void mapram_nop(struct mtd_info *mtd) static int __init map_ram_init(void) { - inter_module_register(im_name, THIS_MODULE, &map_ram_probe); + register_mtd_chip_driver(&mapram_chipdrv); return 0; } static void __exit map_ram_exit(void) { - inter_module_unregister(im_name); + unregister_mtd_chip_driver(&mapram_chipdrv); } module_init(map_ram_init); diff --git a/drivers/mtd/map_rom.c b/drivers/mtd/chips/map_rom.c similarity index 68% rename from drivers/mtd/map_rom.c rename to drivers/mtd/chips/map_rom.c index 455cb99fd97a..387b1ad16f53 100644 --- a/drivers/mtd/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.10 2000/12/10 01:39:13 dwmw2 Exp $ + * $Id: map_rom.c,v 1.14 2001/06/02 14:30:43 dwmw2 Exp $ */ #include @@ -17,16 +17,13 @@ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static void maprom_nop (struct mtd_info *); +struct mtd_info *map_rom_probe(struct map_info *map); -static const char im_name[] = "map_rom_probe"; - -/* This routine is made available to other mtd code via - * inter_module_register. It must only be accessed through - * inter_module_get which will bump the use count of this module. The - * addresses passed back in mtd are valid as long as the use count of - * this module is non-zero, i.e. between inter_module_get and - * inter_module_put. Keith Owens 29 Oct 2000. - */ +static struct mtd_chip_driver maprom_chipdrv = { + probe: map_rom_probe, + name: "rom", + module: THIS_MODULE +}; struct mtd_info *map_rom_probe(struct map_info *map) { @@ -38,8 +35,7 @@ struct mtd_info *map_rom_probe(struct map_info *map) memset(mtd, 0, sizeof(*mtd)); - map->im_name = im_name; - map->fldrv_destroy = maprom_nop; + map->fldrv = &maprom_chipdrv; mtd->priv = map; mtd->name = map->name; mtd->type = MTD_ROM; @@ -50,6 +46,7 @@ struct mtd_info *map_rom_probe(struct map_info *map) mtd->flags = MTD_CAP_ROM; mtd->erasesize = 131072; + MOD_INC_USE_COUNT; return mtd; } @@ -79,15 +76,15 @@ static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *re #define map_rom_exit cleanup_module #endif -static int __init map_rom_init(void) +mod_init_t map_rom_init(void) { - inter_module_register(im_name, THIS_MODULE, &map_rom_probe); + register_mtd_chip_driver(&maprom_chipdrv); return 0; } -static void __exit map_rom_exit(void) +mod_exit_t map_rom_exit(void) { - inter_module_unregister(im_name); + unregister_mtd_chip_driver(&maprom_chipdrv); } module_init(map_rom_init); diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c new file mode 100644 index 000000000000..e603cc9c7996 --- /dev/null +++ b/drivers/mtd/chips/sharp.c @@ -0,0 +1,593 @@ +/* + * MTD chip driver for pre-CFI Sharp flash chips + * + * Copyright 2000,2001 David A. Schleef + * 2000,2001 Lineo, Inc. + * + * $Id: sharp.c,v 1.4 2001/04/29 16:21:17 dwmw2 Exp $ + * + * Devices supported: + * LH28F016SCT Symmetrical block flash memory, 2Mx8 + * LH28F008SCT Symmetrical block flash memory, 1Mx8 + * + * Documentation: + * http://www.sharpmeg.com/datasheets/memic/flashcmp/ + * http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf + * 016sctl9.pdf + * + * Limitations: + * This driver only supports 4x1 arrangement of chips. + * Not tested on anything but PowerPC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CMD_RESET 0xffffffff +#define CMD_READ_ID 0x90909090 +#define CMD_READ_STATUS 0x70707070 +#define CMD_CLEAR_STATUS 0x50505050 +#define CMD_BLOCK_ERASE_1 0x20202020 +#define CMD_BLOCK_ERASE_2 0xd0d0d0d0 +#define CMD_BYTE_WRITE 0x40404040 +#define CMD_SUSPEND 0xb0b0b0b0 +#define CMD_RESUME 0xd0d0d0d0 +#define CMD_SET_BLOCK_LOCK_1 0x60606060 +#define CMD_SET_BLOCK_LOCK_2 0x01010101 +#define CMD_SET_MASTER_LOCK_1 0x60606060 +#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1 +#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060 +#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0 + +#define SR_READY 0x80808080 // 1 = ready +#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended +#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits +#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit +#define SR_VPP 0x08080808 // 1 = Vpp is low +#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended +#define SR_PROTECT 0x02020202 // 1 = lock bit set +#define SR_RESERVED 0x01010101 + +#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT) + +/* Configuration options */ + +#undef AUTOUNLOCK /* automatically unlocks blocks before erasing */ + +struct mtd_info *sharp_probe(struct map_info *); + +static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd); + +static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, const u_char *buf); +static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr); +static void sharp_sync(struct mtd_info *mtd); +static int sharp_suspend(struct mtd_info *mtd); +static void sharp_resume(struct mtd_info *mtd); +static void sharp_destroy(struct mtd_info *mtd); + +static int sharp_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, __u32 datum); +static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr); +#ifdef AUTOUNLOCK +static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr); +#endif + + +struct sharp_info{ + struct flchip *chip; + int bogus; + int chipshift; + int numchips; + struct flchip chips[1]; +}; + +struct mtd_info *sharp_probe(struct map_info *map); +static void sharp_destroy(struct mtd_info *mtd); + +static struct mtd_chip_driver sharp_chipdrv = { + probe: sharp_probe, + destroy: sharp_destroy, + name: "sharp", + module: THIS_MODULE +}; + + +struct mtd_info *sharp_probe(struct map_info *map) +{ + struct mtd_info *mtd = NULL; + struct sharp_info *sharp = NULL; + int width; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if(!mtd) + return NULL; + + sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); + if(!sharp) + return NULL; + + memset(mtd, 0, sizeof(*mtd)); + + width = sharp_probe_map(map,mtd); + if(!width){ + kfree(mtd); + kfree(sharp); + return NULL; + } + + mtd->priv = map; + mtd->type = MTD_NORFLASH; + mtd->erase = sharp_erase; + mtd->read = sharp_read; + mtd->write = sharp_write; + mtd->sync = sharp_sync; + mtd->suspend = sharp_suspend; + mtd->resume = sharp_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + + memset(sharp, 0, sizeof(*sharp)); + sharp->chipshift = 23; + sharp->numchips = 1; + sharp->chips[0].start = 0; + sharp->chips[0].state = FL_READY; + sharp->chips[0].mutex = &sharp->chips[0]._spinlock; + sharp->chips[0].word_write_time = 0; + init_waitqueue_head(&sharp->chips[0].wq); + spin_lock_init(&sharp->chips[0]._spinlock); + + map->fldrv = &sharp_chipdrv; + map->fldrv_priv = sharp; + + MOD_INC_USE_COUNT; + return mtd; +} + +static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd) +{ + unsigned long tmp; + unsigned long base = 0; + u32 read0, read4; + int width = 4; + + tmp = map->read32(map, base+0); + + map->write32(map, CMD_READ_ID, base+0); + + read0=map->read32(map, base+0); + read4=map->read32(map, base+4); + if(read0 == 0x89898989){ + printk("Looks like sharp flash\n"); + switch(read4){ + case 0xaaaaaaaa: + case 0xa0a0a0a0: + /* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/ + /* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/ + mtd->erasesize = 0x10000 * width; + mtd->size = 0x200000 * width; + return width; + case 0xa6a6a6a6: + /* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/ + /* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/ + mtd->erasesize = 0x10000 * width; + mtd->size = 0x100000 * width; + return width; +#if 0 + case 0x00000000: /* unknown */ + /* XX - LH28F004SCT 512kx8, 8 64k blocks*/ + mtd->erasesize = 0x10000 * width; + mtd->size = 0x80000 * width; + return width; +#endif + default: + printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n", + read0,read4); + } + }else if((map->read32(map, base+0) == CMD_READ_ID)){ + /* RAM, probably */ + printk("Looks like RAM\n"); + map->write32(map, tmp, base+0); + }else{ + printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n", + read0,read4); + } + + return 0; +} + +/* This function returns with the chip->mutex lock held. */ +static int sharp_wait(struct map_info *map, struct flchip *chip) +{ + __u16 status; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + int adr = 0; + +retry: + spin_lock_bh(chip->mutex); + + switch(chip->state){ + case FL_READY: + map->write32(map,CMD_READ_STATUS,adr); + chip->state = FL_STATUS; + case FL_STATUS: + status = map->read32(map,adr); +//printk("status=%08x\n",status); + + udelay(100); + if((status & SR_READY)!=SR_READY){ +//printk(".status=%08x\n",status); + udelay(100); + } + break; + default: + printk("Waiting for chip\n"); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if(signal_pending(current)) + return -EINTR; + + timeo = jiffies + HZ; + + goto retry; + } + + map->write32(map,CMD_RESET, adr); + + chip->state = FL_READY; + + return 0; +} + +static void sharp_release(struct flchip *chip) +{ + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +} + +static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct sharp_info *sharp = map->fldrv_priv; + int chipnum; + int ret = 0; + int ofs = 0; + + chipnum = (from >> sharp->chipshift); + ofs = from & ((1 << sharp->chipshift)-1); + + *retlen = 0; + + while(len){ + unsigned long thislen; + + if(chipnum>=sharp->numchips) + break; + + thislen = len; + if(ofs+thislen >= (1<chipshift)) + thislen = (1<chipshift) - ofs; + + ret = sharp_wait(map,&sharp->chips[chipnum]); + if(ret<0) + break; + + map->copy_from(map,buf,ofs,thislen); + + sharp_release(&sharp->chips[chipnum]); + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct sharp_info *sharp = map->fldrv_priv; + int ret = 0; + int i,j; + int chipnum; + unsigned long ofs; + union { u32 l; unsigned char uc[4]; } tbuf; + + *retlen = 0; + + while(len){ + tbuf.l = 0xffffffff; + chipnum = to >> sharp->chipshift; + ofs = to & ((1<chipshift)-1); + + j=0; + for(i=ofs&3;i<4 && len;i++){ + tbuf.uc[i] = *buf; + buf++; + to++; + len--; + j++; + } + sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l); + if(ret<0) + return ret; + (*retlen)+=j; + } + + return 0; +} + +static int sharp_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, __u32 datum) +{ + int ret; + int timeo; + int try; + int i; + int status = 0; + + ret = sharp_wait(map,chip); + + for(try=0;try<10;try++){ + map->write32(map,CMD_BYTE_WRITE,adr); + /* cpu_to_le32 -> hack to fix the writel be->le conversion */ + map->write32(map,cpu_to_le32(datum),adr); + + chip->state = FL_WRITING; + + timeo = jiffies + (HZ/2); + + map->write32(map,CMD_READ_STATUS,adr); + for(i=0;i<100;i++){ + status = map->read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + } + if(i==100){ + printk("sharp: timed out writing\n"); + } + + if(!(status&SR_ERRORS)) + break; + + printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status); + + map->write32(map,CMD_CLEAR_STATUS,adr); + } + map->write32(map,CMD_RESET,adr); + chip->state = FL_READY; + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return 0; +} + +static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct sharp_info *sharp = map->fldrv_priv; + unsigned long adr,len; + int chipnum, ret=0; + +//printk("sharp_erase()\n"); + if(instr->addr & (mtd->erasesize - 1)) + return -EINVAL; + if(instr->len & (mtd->erasesize - 1)) + return -EINVAL; + if(instr->len + instr->addr > mtd->size) + return -EINVAL; + + chipnum = instr->addr >> sharp->chipshift; + adr = instr->addr & ((1<chipshift)-1); + len = instr->len; + + while(len){ + ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr); + if(ret)return ret; + + adr += mtd->erasesize; + len -= mtd->erasesize; + if(adr >> sharp->chipshift){ + adr = 0; + chipnum++; + if(chipnum>=sharp->numchips) + break; + } + } + + if(instr->callback) + instr->callback(instr); + + return 0; +} + +static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + int ret; + int timeo; + int status; + DECLARE_WAITQUEUE(wait, current); + + map->write32(map,CMD_READ_STATUS,adr); + status = map->read32(map,adr); + + timeo = jiffies + HZ; + + while(jiffieswrite32(map,CMD_READ_STATUS,adr); + status = map->read32(map,adr); + if((status & SR_READY)==SR_READY){ + ret = 0; + goto out; + } + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + //spin_unlock_bh(chip->mutex); + + schedule_timeout(1); + schedule(); + remove_wait_queue(&chip->wq, &wait); + + //spin_lock_bh(chip->mutex); + + if (signal_pending(current)){ + ret = -EINTR; + goto out; + } + + } + ret = -ETIME; +out: + return ret; +} + +static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + int ret; + //int timeo; + int status; + //int i; + +//printk("sharp_erase_oneblock()\n"); + +#ifdef AUTOUNLOCK + /* This seems like a good place to do an unlock */ + sharp_unlock_oneblock(map,chip,adr); +#endif + + map->write32(map,CMD_BLOCK_ERASE_1,adr); + map->write32(map,CMD_BLOCK_ERASE_2,adr); + + chip->state = FL_ERASING; + + ret = sharp_do_wait_for_ready(map,chip,adr); + if(ret<0)return ret; + + map->write32(map,CMD_READ_STATUS,adr); + status = map->read32(map,adr); + + if(!(status&SR_ERRORS)){ + map->write32(map,CMD_RESET,adr); + chip->state = FL_READY; + //spin_unlock_bh(chip->mutex); + return 0; + } + + printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status); + map->write32(map,CMD_CLEAR_STATUS,adr); + + //spin_unlock_bh(chip->mutex); + + return -EIO; +} + +#ifdef AUTOUNLOCK +static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + int i; + int status; + + map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); + map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); + + udelay(100); + + status = map->read32(map,adr); + printk("status=%08x\n",status); + + for(i=0;i<1000;i++){ + //map->write32(map,CMD_READ_STATUS,adr); + status = map->read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + udelay(100); + } + if(i==1000){ + printk("sharp: timed out unlocking block\n"); + } + + if(!(status&SR_ERRORS)){ + map->write32(map,CMD_RESET,adr); + chip->state = FL_READY; + return; + } + + printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status); + map->write32(map,CMD_CLEAR_STATUS,adr); +} +#endif + +static void sharp_sync(struct mtd_info *mtd) +{ + //printk("sharp_sync()\n"); +} + +static int sharp_suspend(struct mtd_info *mtd) +{ + printk("sharp_suspend()\n"); + return -EINVAL; +} + +static void sharp_resume(struct mtd_info *mtd) +{ + printk("sharp_resume()\n"); + +} + +static void sharp_destroy(struct mtd_info *mtd) +{ + printk("sharp_destroy()\n"); + +} + +#if LINUX_VERSION_CODE < 0x020212 && defined(MODULE) +#define sharp_probe_init init_module +#define sharp_probe_exit cleanup_module +#endif + +int __init sharp_probe_init(void) +{ + printk("MTD Sharp chip driver \n"); + + register_mtd_chip_driver(&sharp_chipdrv); + + return 0; +} + +static void __exit sharp_probe_exit(void) +{ + unregister_mtd_chip_driver(&sharp_chipdrv); +} + +module_init(sharp_probe_init); +module_exit(sharp_probe_exit); + diff --git a/drivers/mtd/devices/Config.in b/drivers/mtd/devices/Config.in new file mode 100644 index 000000000000..26539405d131 --- /dev/null +++ b/drivers/mtd/devices/Config.in @@ -0,0 +1,51 @@ +# drivers/mtd/maps/Config.in + +# $Id: Config.in,v 1.2 2001/04/29 16:24:34 dwmw2 Exp $ + +mainmenu_option next_comment + +comment 'Self-contained MTD device drivers' +dep_tristate ' Ramix PMC551 PCI Mezzanine RAM card support' CONFIG_MTD_PMC551 $CONFIG_MTD $CONFIG_PCI +if [ "$CONFIG_MTD_PMC551" = "y" -o "$CONFIG_MTD_PMC551" = "m" ]; then + bool ' PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX + bool ' PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG +fi +dep_tristate ' Uncached system RAM' CONFIG_MTD_SLRAM $CONFIG_MTD +dep_tristate ' Test driver using RAM' CONFIG_MTD_MTDRAM $CONFIG_MTD +if [ "$CONFIG_MTD_MTDRAM" = "y" -o "$CONFIG_MTD_MTDRAM" = "m" ]; then + int 'MTDRAM device size in KiB' CONFIG_MTDRAM_TOTAL_SIZE 4096 + int 'MTDRAM erase block size in KiB' CONFIG_MTDRAM_ERASE_SIZE 128 + if [ "$CONFIG_MTD_MTDRAM" = "y" ]; then #If not a module (I don't want to test it as a module) + hex 'SRAM Hexadecimal Absolute position or 0' CONFIG_MTDRAM_ABS_POS 0 + fi +fi + +comment 'Disk-On-Chip Device Drivers' + dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD + if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then + define_bool CONFIG_MTD_DOCPROBE y + else + if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then + define_bool CONFIG_MTD_DOCPROBE m + else + define_bool CONFIG_MTD_DOCPROBE n + fi + fi + + if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then + bool ' Advanced detection options for DiskOnChip' CONFIG_MTD_DOCPROBE_ADVANCED + if [ "$CONFIG_MTD_DOCPROBE_ADVANCED" = "n" ]; then + define_hex CONFIG_MTD_DOCPROBE_ADDRESS 0 + define_bool CONFIG_MTD_DOCPROBE_HIGH n + define_bool CONFIG_MTD_DOCPROBE_55AA n + else + hex ' Physical address of DiskOnChip' CONFIG_MTD_DOCPROBE_ADDRESS 0x0000 + bool ' Probe high addresses' CONFIG_MTD_DOCPROBE_HIGH + bool ' Probe for 0x55 0xAA BIOS Extension Signature' CONFIG_MTD_DOCPROBE_55AA + fi + fi + + +endmenu diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile new file mode 100644 index 000000000000..fcc7f30e447c --- /dev/null +++ b/drivers/mtd/devices/Makefile @@ -0,0 +1,23 @@ +# +# linux/drivers/devices/Makefile +# +# $Id: Makefile,v 1.2 2001/04/19 22:12:36 dwmw2 Exp $ + +O_TARGET := devlink.o + +# *** BIG UGLY NOTE *** +# +# The removal of get_module_symbol() and replacement with +# inter_module_register() et al has introduced a link order dependency +# here where previously there was none. We now have to ensure that +# doc200[01].o are linked before docprobe.o + +obj-$(CONFIG_MTD_DOC1000) += doc1000.o +obj-$(CONFIG_MTD_DOC2000) += doc2000.o +obj-$(CONFIG_MTD_DOC2001) += doc2001.o +obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o +obj-$(CONFIG_MTD_SLRAM) += slram.o +obj-$(CONFIG_MTD_PMC551) += pmc551.o +obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + +include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/doc1000.c b/drivers/mtd/devices/doc1000.c similarity index 100% rename from drivers/mtd/doc1000.c rename to drivers/mtd/devices/doc1000.c diff --git a/drivers/mtd/doc2000.c b/drivers/mtd/devices/doc2000.c similarity index 97% rename from drivers/mtd/doc2000.c rename to drivers/mtd/devices/doc2000.c index 4957fcd6695f..7bfe65b120e7 100644 --- a/drivers/mtd/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2000.c,v 1.39 2000/12/01 17:34:29 dwmw2 Exp $ + * $Id: doc2000.c,v 1.43 2001/06/02 14:30:43 dwmw2 Exp $ */ #include @@ -460,12 +460,10 @@ static void DoC_ScanChips(struct DiskOnChip *this) /* Calculate and print the total size of the device */ this->totlen = this->numchips * (1 << this->chipshift); - printk(KERN_INFO - "%d flash chips found. Total DiskOnChip size: %ld Mb\n", + printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", this->numchips, this->totlen >> 20); } - static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) { int tmp1, tmp2, retval; @@ -719,6 +717,13 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, WriteDOC(DOC_ECC_DIS, docptr , ECCConf); } + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ + if(0 == ((from + *retlen) & 0x1ff)) + { + DoC_WaitReady(this); + } + return ret; } @@ -931,7 +936,10 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_ReadBuf(this, &buf[len256], len - len256); *retlen = len; - return 0; + /* Reading the full OOB data drops us off of the end of the page, + * causing the flash device to go into busy mode, so we need + * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ + return DoC_WaitReady(this); } @@ -1028,13 +1036,13 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; - unsigned long ofs = instr->addr; - unsigned long len = instr->len; + __u32 ofs = instr->addr; + __u32 len = instr->len; unsigned long docptr; struct Nand *mychip; if (len != mtd->erasesize) - printk(KERN_WARNING "Erase not right size (%lx != %lx)n", + printk(KERN_WARNING "Erase not right size (%x != %x)n", len, mtd->erasesize); docptr = this->virtadr; diff --git a/drivers/mtd/doc2001.c b/drivers/mtd/devices/doc2001.c similarity index 92% rename from drivers/mtd/doc2001.c rename to drivers/mtd/devices/doc2001.c index d80143783a7c..849fcaba0f3c 100644 --- a/drivers/mtd/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001.c,v 1.24 2000/12/01 13:11:02 dwmw2 Exp $ + * $Id: doc2001.c,v 1.34 2001/06/02 14:30:43 dwmw2 Exp $ */ #include @@ -30,9 +30,8 @@ /* I have no idea why some DoC chips can not use memcop_form|to_io(). * This may be due to the different revisions of the ASIC controller built-in or * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment - * this: - #undef USE_MEMCPY -*/ + * this:*/ +#undef USE_MEMCPY static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); @@ -98,8 +97,8 @@ static inline int DoC_WaitReady(unsigned long docptr) return ret; } -/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to - bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is +/* DoC_Command: Send a flash command to the flash chip through the CDSN IO register + with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ static inline void DoC_Command(unsigned long docptr, unsigned char command, @@ -110,16 +109,16 @@ static inline void DoC_Command(unsigned long docptr, unsigned char command, DoC_Delay(docptr, 4); /* Send the command */ - WriteDOC(command, docptr, CDSNSlowIO); WriteDOC(command, docptr, Mil_CDSN_IO); + WriteDOC(0x00, docptr, WritePipeTerm); /* Lower the CLE line */ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); DoC_Delay(docptr, 4); } -/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to - bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is +/* DoC_Address: Set the current address for the flash chip through the CDSN IO register + with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs, @@ -133,25 +132,22 @@ static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long switch (numbytes) { case 1: - /* Send single byte, bits 0-7. */ - WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); - WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); - break; + /* Send single byte, bits 0-7. */ + WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); + WriteDOC(0x00, docptr, WritePipeTerm); + break; case 2: - /* Send bits 9-16 followed by 17-23 */ - WriteDOC((ofs >> 9) & 0xff, docptr, CDSNSlowIO); - WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO); - WriteDOC((ofs >> 17) & 0xff, docptr, CDSNSlowIO); - WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); + /* Send bits 9-16 followed by 17-23 */ + WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO); + WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); + WriteDOC(0x00, docptr, WritePipeTerm); break; case 3: - /* Send 0-7, 9-16, then 17-23 */ - WriteDOC(ofs & 0xff, docptr, CDSNSlowIO); - WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); - WriteDOC((ofs >> 9) & 0xff, docptr, CDSNSlowIO); - WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO); - WriteDOC((ofs >> 17) & 0xff, docptr, CDSNSlowIO); - WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); + /* Send 0-7, 9-16, then 17-23 */ + WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO); + WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO); + WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO); + WriteDOC(0x00, docptr, WritePipeTerm); break; default: return; @@ -205,14 +201,14 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) DoC_Address(doc->virtadr, 1, 0x00, CDSN_CTRL_WP, 0x00); /* Read the manufacturer and device id codes of the flash device through - CDSN Slow IO register see Software Requirement 11.4 item 5.*/ - dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + CDSN IO register see Software Requirement 11.4 item 5.*/ + dummy = ReadDOC(doc->virtadr, ReadPipeInit); DoC_Delay(doc->virtadr, 2); mfr = ReadDOC(doc->virtadr, Mil_CDSN_IO); - dummy = ReadDOC(doc->virtadr, CDSNSlowIO); DoC_Delay(doc->virtadr, 2); id = ReadDOC(doc->virtadr, Mil_CDSN_IO); + dummy = ReadDOC(doc->virtadr, LastDataRead); /* No response - return failure */ if (mfr == 0xff || mfr == 0) @@ -287,7 +283,7 @@ static void DoC_ScanChips(struct DiskOnChip *this) /* Calculate and print the total size of the device */ this->totlen = this->numchips * (1 << this->chipshift); - printk(KERN_NOTICE "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n", + printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", this->numchips ,this->totlen >> 20); } @@ -502,7 +498,7 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } nb_errors = doc_decode_ecc(buf, syndrome); #ifdef ECC_DEBUG - printk("Errors corrected: %x\n", nb_errors); + printk("ECC Errors corrected: %x\n", nb_errors); #endif if (nb_errors < 0) { /* We return error, but have actually done the read. Not that @@ -517,6 +513,7 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], eccbuf[5]); #endif + /* disable the ECC engine */ WriteDOC(DOC_ECC_DIS, docptr , ECCConf); } @@ -534,7 +531,7 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf) { - int i; + int i,ret = 0; volatile char dummy; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; unsigned long docptr = this->virtadr; @@ -644,23 +641,24 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); DoC_WaitReady(docptr); - /* Read the status of the flash device through CDSN Slow IO register + /* Read the status of the flash device through CDSN IO register see Software Requirement 11.4 item 5.*/ DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); - dummy = ReadDOC(docptr, CDSNSlowIO); + dummy = ReadDOC(docptr, ReadPipeInit); DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming flash\n"); /* Error in programming FIXME: implement Bad Block Replacement (in nftl.c ??) */ *retlen = 0; - return -EIO; + ret = -EIO; } + dummy = ReadDOC(docptr, LastDataRead); /* Let the caller know we completed it */ *retlen = len; - return 0; + return ret; } static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, @@ -704,7 +702,6 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ECC logic will not work properly */ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); } - buf[i] = ReadDOC(docptr, LastDataRead); #else memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1); #endif @@ -722,6 +719,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int i; #endif volatile char dummy; + int ret = 0; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -768,34 +766,35 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); DoC_WaitReady(docptr); - /* Read the status of the flash device through CDSN Slow IO register + /* Read the status of the flash device through CDSN IO register see Software Requirement 11.4 item 5.*/ DoC_Command(docptr, NAND_CMD_STATUS, 0x00); - dummy = ReadDOC(docptr, CDSNSlowIO); + dummy = ReadDOC(docptr, ReadPipeInit); DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { printk("Error programming oob data\n"); /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ *retlen = 0; - return -EIO; + ret = -EIO; } + dummy = ReadDOC(docptr, LastDataRead); *retlen = len; - return 0; + return ret; } int doc_erase (struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long ofs = instr->addr; - unsigned long len = instr->len; + __u32 ofs = instr->addr; + __u32 len = instr->len; unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; if (len != mtd->erasesize) - printk(KERN_WARNING "Erase not right size (%lx != %lx)n", + printk(KERN_WARNING "Erase not right size (%x != %x)n", len, mtd->erasesize); /* Find the chip which is to be used and select it */ @@ -821,20 +820,21 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) instr->state = MTD_ERASING; - /* Read the status of the flash device through CDSN Slow IO register + /* Read the status of the flash device through CDSN IO register see Software Requirement 11.4 item 5. FIXME: it seems that we are not wait long enough, some blocks are not erased fully */ DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); - dummy = ReadDOC(docptr, CDSNSlowIO); + dummy = ReadDOC(docptr, ReadPipeInit); DoC_Delay(docptr, 2); if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { - printk("Error Erasing at 0x%lx\n", ofs); + printk("Error Erasing at 0x%x\n", ofs); /* There was an error FIXME: implement Bad Block Replacement (in nftl.c ??) */ instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; + dummy = ReadDOC(docptr, LastDataRead); if (instr->callback) instr->callback(instr); @@ -864,7 +864,7 @@ static void __exit cleanup_doc2001(void) struct mtd_info *mtd; struct DiskOnChip *this; - while((mtd=docmillist)) { + while ((mtd=docmillist)) { this = (struct DiskOnChip *)mtd->priv; docmillist = this->nextdoc; diff --git a/drivers/mtd/docecc.c b/drivers/mtd/devices/docecc.c similarity index 100% rename from drivers/mtd/docecc.c rename to drivers/mtd/devices/docecc.c diff --git a/drivers/mtd/docprobe.c b/drivers/mtd/devices/docprobe.c similarity index 94% rename from drivers/mtd/docprobe.c rename to drivers/mtd/devices/docprobe.c index 9f5ba083bdb5..0a2c735e9208 100644 --- a/drivers/mtd/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -1,9 +1,9 @@ -/* Linux driver for Disk-On-Chip devices */ -/* Probe routines common to all DoC devices */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse */ -/* $Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $ */ +/* Linux driver for Disk-On-Chip devices */ +/* Probe routines common to all DoC devices */ +/* (c) 1999 Machine Vision Holdings, Inc. */ +/* Author: David Woodhouse */ +/* $Id: docprobe.c,v 1.27 2001/06/03 19:06:09 dwmw2 Exp $ */ @@ -86,7 +86,9 @@ static unsigned long __initdata doc_locations[] = { #endif /* CONFIG_MTD_DOCPROBE_HIGH */ #elif defined(__ppc__) 0xe4000000, -#else +#elif defined(CONFIG_MOMENCO_OCELOT) + 0x2f000000, +#else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif 0 }; @@ -175,7 +177,7 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr } -static void DoC_Probe(unsigned long physadr) +static void __init DoC_Probe(unsigned long physadr) { unsigned long docptr; struct DiskOnChip *this; @@ -187,7 +189,7 @@ static void DoC_Probe(unsigned long physadr) char *im_modname = NULL; void (*initroutine)(struct mtd_info *) = NULL; - docptr = (unsigned long)ioremap(physadr, 0x2000); + docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); if (!docptr) return; @@ -262,7 +264,7 @@ int __init init_doc(void) printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n"); #ifdef PRERELEASE - printk(KERN_INFO "$Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $\n"); + printk(KERN_INFO "$Id: docprobe.c,v 1.27 2001/06/03 19:06:09 dwmw2 Exp $\n"); #endif if (doc_config_location) { printk("Using configured probe address 0x%lx\n", doc_config_location); diff --git a/drivers/mtd/mtdram.c b/drivers/mtd/devices/mtdram.c similarity index 61% rename from drivers/mtd/mtdram.c rename to drivers/mtd/devices/mtdram.c index d3a33495d628..d0c9fc5cf737 100644 --- a/drivers/mtd/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,7 +1,7 @@ -/* +/* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.15 2000/07/13 12:40:46 scote1 Exp $ - * Author: Alexander Larsson + * $Id: mtdram.c,v 1.24 2001/06/09 23:09:23 dwmw2 Exp $ + * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson * @@ -11,15 +11,30 @@ #include #include - #include #include #include #include +#ifndef CONFIG_MTDRAM_ABS_POS + #define CONFIG_MTDRAM_ABS_POS 0 +#endif + +#if CONFIG_MTDRAM_ABS_POS > 0 + #include +#endif +#ifdef MODULE +static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; +static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; +MODULE_PARM(total_size,"l"); +MODULE_PARM(erase_size,"l"); +#define MTDRAM_TOTAL_SIZE (total_size * 1024) +#define MTDRAM_ERASE_SIZE (erase_size * 1024) +#else #define MTDRAM_TOTAL_SIZE (CONFIG_MTDRAM_TOTAL_SIZE * 1024) #define MTDRAM_ERASE_SIZE (CONFIG_MTDRAM_ERASE_SIZE * 1024) +#endif // We could store these in the mtd structure, but we only support 1 device.. @@ -29,11 +44,14 @@ static struct mtd_info *mtd_info; static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { - if (instr->addr + instr->len > mtd->size) + DEBUG(MTD_DEBUG_LEVEL2, "ram_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len); + if (instr->addr + instr->len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL1, "ram_erase() out of bounds (%ld > %ld)\n", (long)(instr->addr + instr->len), (long)mtd->size); return -EINVAL; + } memset((char *)mtd->priv + instr->addr, 0xff, instr->len); - + instr->state = MTD_ERASE_DONE; if (instr->callback) @@ -45,7 +63,7 @@ static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *ret { if (from + len > mtd->size) return -EINVAL; - + *mtdbuf = mtd->priv + from; *retlen = len; return 0; @@ -53,13 +71,17 @@ static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *ret static void ram_unpoint (struct mtd_info *mtd, u_char *addr) { + DEBUG(MTD_DEBUG_LEVEL2, "ram_unpoint\n"); } static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - if (from + len > mtd->size) + DEBUG(MTD_DEBUG_LEVEL2, "ram_read(pos:%ld, len:%ld)\n", (long)from, (long)len); + if (from + len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL1, "ram_read() out of bounds (%ld > %ld)\n", (long)(from + len), (long)mtd->size); return -EINVAL; + } memcpy(buf, mtd->priv + from, len); @@ -70,21 +92,22 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - if (to + len > mtd->size) + DEBUG(MTD_DEBUG_LEVEL2, "ram_write(pos:%ld, len:%ld)\n", (long)to, (long)len); + if (to + len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL1, "ram_write() out of bounds (%ld > %ld)\n", (long)(to + len), (long)mtd->size); return -EINVAL; - + } + memcpy ((char *)mtd->priv + to, buf, len); *retlen=len; return 0; } -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_mtdram init_module #define cleanup_mtdram cleanup_module #endif -#endif //static void __exit cleanup_mtdram(void) mod_exit_t cleanup_mtdram(void) @@ -92,18 +115,20 @@ mod_exit_t cleanup_mtdram(void) if (mtd_info) { del_mtd_device(mtd_info); if (mtd_info->priv) +#if CONFIG_MTDRAM_ABS_POS > 0 + iounmap(mtd_info->priv); +#else vfree(mtd_info->priv); +#endif kfree(mtd_info); } } -extern struct module __this_module; - mod_init_t init_mtdram(void) { // Allocate some memory mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (mtd_info == 0) + if (!mtd_info) return 0; memset(mtd_info, 0, sizeof(*mtd_info)); @@ -114,18 +139,21 @@ mod_init_t init_mtdram(void) mtd_info->flags = MTD_CAP_RAM; mtd_info->size = MTDRAM_TOTAL_SIZE; mtd_info->erasesize = MTDRAM_ERASE_SIZE; +#if CONFIG_MTDRAM_ABS_POS > 0 + mtd_info->priv = ioremap(CONFIG_MTDRAM_ABS_POS, MTDRAM_TOTAL_SIZE); +#else mtd_info->priv = vmalloc(MTDRAM_TOTAL_SIZE); - memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - mtd_info->module = THIS_MODULE; #endif if (!mtd_info->priv) { + DEBUG(MTD_DEBUG_LEVEL1, "Failed to vmalloc(/ioremap) memory region of size %ld (ABS_POS:%ld)\n", (long)MTDRAM_TOTAL_SIZE, (long)CONFIG_MTDRAM_ABS_POS); kfree(mtd_info); mtd_info = NULL; return -ENOMEM; } + memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + + mtd_info->module = THIS_MODULE; mtd_info->erase = ram_erase; mtd_info->point = ram_point; mtd_info->unpoint = ram_unpoint; @@ -133,7 +161,11 @@ mod_init_t init_mtdram(void) mtd_info->write = ram_write; if (add_mtd_device(mtd_info)) { +#if CONFIG_MTDRAM_ABS_POS > 0 + iounmap(mtd_info->priv); +#else vfree(mtd_info->priv); +#endif kfree(mtd_info); mtd_info = NULL; return -EIO; @@ -142,7 +174,5 @@ mod_init_t init_mtdram(void) return 0; } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_mtdram); module_exit(cleanup_mtdram); -#endif diff --git a/drivers/mtd/pmc551.c b/drivers/mtd/devices/pmc551.c similarity index 89% rename from drivers/mtd/pmc551.c rename to drivers/mtd/devices/pmc551.c index 8247a047d29e..043181879662 100644 --- a/drivers/mtd/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.11 2000/11/23 13:40:12 dwmw2 Exp $ + * $Id: pmc551.c,v 1.17 2001/05/22 13:56:46 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -8,66 +8,74 @@ * Copyright 1999,2000 Nortel Networks * * License: - * As part of this driver was derrived from the slram.c driver it falls - * under the same license, which is GNU General Public License v2 + * As part of this driver was derived from the slram.c driver it + * falls under the same license, which is GNU General Public + * License v2 * * Description: - * This driver is intended to support the PMC551 PCI Ram device from - * Ramix Inc. The PMC551 is a PMC Mezzanine module for cPCI embedded - * systems. The device contains a single SROM that initally programs the - * V370PDC chipset onboard the device, and various banks of DRAM/SDRAM - * onboard. This driver implements this PCI Ram device as an MTD (Memory - * Technologies Device) so that it can be used to hold a filesystem, or - * for added swap space in embedded systems. Since the memory on this - * board isn't as fast as main memory we do not try to hook it into main - * memeory as that would simply reduce performance on the system. Using - * it as a block device allows us to use it as high speed swap or for a - * high speed disk device of some sort. Which becomes very useful on - * diskless systems in the embedded market I might add. + * This driver is intended to support the PMC551 PCI Ram device + * from Ramix Inc. The PMC551 is a PMC Mezzanine module for + * cPCI embedded systems. The device contains a single SROM + * that initially programs the V370PDC chipset onboard the + * device, and various banks of DRAM/SDRAM onboard. This driver + * implements this PCI Ram device as an MTD (Memory Technology + * Device) so that it can be used to hold a file system, or for + * added swap space in embedded systems. Since the memory on + * this board isn't as fast as main memory we do not try to hook + * it into main memory as that would simply reduce performance + * on the system. Using it as a block device allows us to use + * it as high speed swap or for a high speed disk device of some + * sort. Which becomes very useful on diskless systems in the + * embedded market I might add. * * Notes: - * Due to what I assume is more buggy SROM, the 64M PMC551 I have - * available claims that all 4 of it's DRAM banks have 64M of ram - * configured (making a grand total of 256M onboard). This is slightly - * annoying since the BAR0 size reflects the aperture size, not the dram - * size, and the V370PDC supplies no other method for memory size - * discovery. This problem is mostly only relivant when compiled as a - * module, as the unloading of the module with an aperture size smaller - * then the ram will cause the driver to detect the onboard memory size - * to be equal to the aperture size when the module is reloaded. Soooo, - * to help, the module supports an msize option to allow the - * specification of the onboard memory, and an asize option, to allow the - * specification of the aperture size. The aperture must be equal to or - * less then the memory size, the driver will correct this if you screw - * it up. This problem is not relivant for compiled in drivers as - * compiled in drivers only init once. + * Due to what I assume is more buggy SROM, the 64M PMC551 I + * have available claims that all 4 of it's DRAM banks have 64M + * of ram configured (making a grand total of 256M onboard). + * This is slightly annoying since the BAR0 size reflects the + * aperture size, not the dram size, and the V370PDC supplies no + * other method for memory size discovery. This problem is + * mostly only relevant when compiled as a module, as the + * unloading of the module with an aperture size smaller then + * the ram will cause the driver to detect the onboard memory + * size to be equal to the aperture size when the module is + * reloaded. Soooo, to help, the module supports an msize + * option to allow the specification of the onboard memory, and + * an asize option, to allow the specification of the aperture + * size. The aperture must be equal to or less then the memory + * size, the driver will correct this if you screw it up. This + * problem is not relevant for compiled in drivers as compiled + * in drivers only init once. * * Credits: - * Saeed Karamooz of Ramix INC. for the initial - * example code of how to initialize this device and for help with - * questions I had concerning operation of the device. + * Saeed Karamooz of Ramix INC. for the + * initial example code of how to initialize this device and for + * help with questions I had concerning operation of the device. * - * Most of the MTD code for this driver was originally written for the - * slram.o module in the MTD drivers package written by David Hinds - * which allows the mapping of system - * memory into an mtd device. Since the PMC551 memory module is - * accessed in the same fashion as system memory, the slram.c code - * became a very nice fit to the needs of this driver. All we added was - * PCI detection/initialization to the driver and automaticly figure out - * the size via the PCI detection.o, later changes by Corey Minyard - * settup the card to utilize a 1M sliding apature. + * Most of the MTD code for this driver was originally written + * for the slram.o module in the MTD drivers package which + * allows the mapping of system memory into an MTD device. + * Since the PMC551 memory module is accessed in the same + * fashion as system memory, the slram.c code became a very nice + * fit to the needs of this driver. All we added was PCI + * detection/initialization to the driver and automatically figure + * out the size via the PCI detection.o, later changes by Corey + * Minyard set up the card to utilize a 1M sliding apature. * * Corey Minyard - * * Modified driver to utilize a sliding apature instead of mapping all - * memory into kernel space which turned out to be very wastefull. - * * Located a bug in the SROM's initialization sequence that made the - * memory unusable, added a fix to code to touch up the DRAM some. + * * Modified driver to utilize a sliding aperture instead of + * mapping all memory into kernel space which turned out to + * be very wasteful. + * * Located a bug in the SROM's initialization sequence that + * made the memory unusable, added a fix to code to touch up + * the DRAM some. * * Bugs/FIXME's: * * MUST fix the init function to not spin on a register - * waiting for it to set .. this does not safely handle busted devices - * that never reset the register correctly which will cause the system to - * hang w/ a reboot beeing the only chance at recover. + * waiting for it to set .. this does not safely handle busted + * devices that never reset the register correctly which will + * cause the system to hang w/ a reboot being the only chance at + * recover. */ #include @@ -104,7 +112,7 @@ #define PCI_BASE_ADDRESS(dev) (dev->base_address[0]) #endif -static struct mtd_info *pmc551list = NULL; +static struct mtd_info *pmc551list; static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) { @@ -548,7 +556,7 @@ static u32 fixup_pmc551 (struct pci_dev *dev) /* * Set to be prefetchable and put everything back based on old cfg. * it's possible that the reset of the V370PDC nuked the original - * settup + * setup */ cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH; pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); @@ -635,19 +643,17 @@ static u32 fixup_pmc551 (struct pci_dev *dev) /* * Kernel version specific module stuffages */ -#if LINUX_VERSION_CODE < 0x20211 -#ifdef MODULE + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_pmc551 init_module #define cleanup_pmc551 cleanup_module #endif -#define __exit -#endif #if defined(MODULE) MODULE_AUTHOR("Mark Ferrell "); MODULE_DESCRIPTION(PMC551_VERSION); MODULE_PARM(msize, "i"); -MODULE_PARM_DESC(msize, "memory size, 6=32M, 7=64M, 8=128M, ect.. [32M-1024M]"); +MODULE_PARM_DESC(msize, "memory size, 6=32M, 7=64M, 8=128M, etc.. [32M-1024M]"); MODULE_PARM(asize, "i"); MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1M-1024M]"); #endif @@ -758,6 +764,11 @@ int __init init_pmc551(void) & PCI_BASE_ADDRESS_MEM_MASK), priv->aperture_size); + if (!priv->start) { + kfree(mtd->priv); + kfree(mtd); + break; + } /* * Due to the dynamic nature of the code, we need to figure * this out in order to stuff the register to set the proper @@ -846,7 +857,7 @@ static void __exit cleanup_pmc551(void) pmc551list = priv->nextpmc551; if(priv->start) - iounmap(((struct mypriv *)mtd->priv)->start); + iounmap(priv->start); kfree (mtd->priv); del_mtd_device(mtd); @@ -857,7 +868,5 @@ static void __exit cleanup_pmc551(void) printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found); } -#if LINUX_VERSION_CODE >= 0x20211 module_init(init_pmc551); module_exit(cleanup_pmc551); -#endif diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c new file mode 100644 index 000000000000..d53fef7aaa61 --- /dev/null +++ b/drivers/mtd/devices/slram.c @@ -0,0 +1,341 @@ +/*====================================================================== + + $Id: slram.c,v 1.19 2001/06/02 20:33:20 dwmw2 Exp $ + +======================================================================*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ + +#define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) +#define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) + +typedef struct slram_priv { + u_char *start; + u_char *end; +} slram_priv_t; + +typedef struct slram_mtd_list { + struct mtd_info *mtdinfo; + struct slram_mtd_list *next; +} slram_mtd_list_t; + +#ifdef MODULE +static char *map[SLRAM_MAX_DEVICES_PARAMS]; +#else +static char *map; +#endif + +#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 +#define init_slram init_module +#define cleanup_slram cleanup_module +#endif + +MODULE_PARM(map, "3-" __MODULE_STRING(SLRAM_MAX_DEVICES_PARAMS) "s"); +MODULE_PARM_DESC(map, "List of memory regions to map. \"map=, , \""); +#endif + +static slram_mtd_list_t *slram_mtdlist = NULL; + +int slram_erase(struct mtd_info *, struct erase_info *); +int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); +void slram_unpoint(struct mtd_info *, u_char *); +int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + +int slram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + slram_priv_t *priv = mtd->priv; + + if (instr->addr + instr->len > mtd->size) { + return(-EINVAL); + } + + memset(priv->start + instr->addr, 0xff, instr->len); + + /* This'll catch a few races. Free the thing before returning :) + * I don't feel at all ashamed. This kind of thing is possible anyway + * with flash, but unlikely. + */ + + instr->state = MTD_ERASE_DONE; + + if (instr->callback) { + (*(instr->callback))(instr); + } + else { + kfree(instr); + } + + return(0); +} + +int slram_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf) +{ + slram_priv_t *priv = (slram_priv_t *)mtd->priv; + + *mtdbuf = priv->start + from; + *retlen = len; + return(0); +} + +void slram_unpoint(struct mtd_info *mtd, u_char *addr) +{ +} + +int slram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + slram_priv_t *priv = (slram_priv_t *)mtd->priv; + + memcpy(buf, priv->start + from, len); + + *retlen = len; + return(0); +} + +int slram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + slram_priv_t *priv = (slram_priv_t *)mtd->priv; + + memcpy(priv->start + to, buf, len); + + *retlen = len; + return(0); +} + +/*====================================================================*/ + +int register_device(char *name, long start, long length) +{ + slram_mtd_list_t **curmtd; + + curmtd = &slram_mtdlist; + while (*curmtd) { + curmtd = &(*curmtd)->next; + } + + *curmtd = kmalloc(sizeof(slram_mtd_list_t), GFP_KERNEL); + if (!curmtd) { + E("slram: Cannot allocate new MTD device.\n"); + return(-ENOMEM); + } + (*curmtd)->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + (*curmtd)->next = NULL; + + if ((*curmtd)->mtdinfo) { + memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); + (*curmtd)->mtdinfo->priv = + (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + + if (!(*curmtd)->mtdinfo->priv) { + kfree((*curmtd)->mtdinfo); + (*curmtd)->mtdinfo = NULL; + } else { + memset((*curmtd)->mtdinfo->priv,0,sizeof(slram_priv_t)); + } + } + + if (!(*curmtd)->mtdinfo) { + E("slram: Cannot allocate new MTD device.\n"); + return(-ENOMEM); + } + + if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = + ioremap(start, length))) { + E("slram: ioremap failed\n"); + return -EIO; + } + ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = + ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; + + + (*curmtd)->mtdinfo->name = name; + (*curmtd)->mtdinfo->size = length; + (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | + MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + (*curmtd)->mtdinfo->erase = slram_erase; + (*curmtd)->mtdinfo->point = slram_point; + (*curmtd)->mtdinfo->unpoint = slram_unpoint; + (*curmtd)->mtdinfo->read = slram_read; + (*curmtd)->mtdinfo->write = slram_write; + (*curmtd)->mtdinfo->module = THIS_MODULE; + (*curmtd)->mtdinfo->type = MTD_RAM; + (*curmtd)->mtdinfo->erasesize = 0x10000; + + if (add_mtd_device((*curmtd)->mtdinfo)) { + E("slram: Failed to register new device\n"); + iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); + kfree((*curmtd)->mtdinfo->priv); + kfree((*curmtd)->mtdinfo); + return(-EAGAIN); + } + T("slram: Registered device %s from %dKiB to %dKiB\n", name, + (int)(start / 1024), (int)((start + length) / 1024)); + T("slram: Mapped from 0x%p to 0x%p\n", + ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, + ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); + return(0); +} + +void unregister_devices(void) +{ + slram_mtd_list_t *nextitem; + + while (slram_mtdlist) { + nextitem = slram_mtdlist->next; + del_mtd_device(slram_mtdlist->mtdinfo); + iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); + kfree(slram_mtdlist->mtdinfo->priv); + kfree(slram_mtdlist->mtdinfo); + kfree(slram_mtdlist); + slram_mtdlist = nextitem; + } +} + +int handle_unit(long value, char *unit) +{ + if ((*unit == 'M') || (*unit == 'm')) { + return(value * 1024 * 1024); + } else if ((*unit == 'K') || (*unit == 'k')) { + return(value * 1024); + } + return(value); +} + +int parse_cmdline(char *devname, char *szstart, char *szlength) +{ + char *buffer; + long devstart; + long devlength; + + if ((!devname) || (!szstart) || (!szlength)) { + unregister_devices(); + return(-EINVAL); + } + + devstart = simple_strtoul(szstart, &buffer, 0); + devstart = handle_unit(devstart, buffer); + + if (*(szlength) != '+') { + devlength = simple_strtoul(szlength, &buffer, 0); + devlength = handle_unit(devlength, buffer) - devstart; + } else { + devlength = simple_strtoul(szlength + 1, &buffer, 0); + devlength = handle_unit(devlength, buffer); + } + T("slram: devname=%s, devstart=%li, devlength=%li\n", + devname, devstart, devlength); + if ((devstart < 0) || (devlength < 0)) { + E("slram: Illegal start / length parameter.\n"); + return(-EINVAL); + } + + if ((devstart = register_device(devname, devstart, devlength))){ + unregister_devices(); + return((int)devstart); + } + return(0); +} + +#ifndef MODULE + +static int __init mtd_slram_setup(char *str) +{ + map = str; + return(1); +} + +__setup("slram=", mtd_slram_setup); + +#endif + +int init_slram(void) +{ + char *devname; + int i; + +#ifndef MODULE + char *devstart; + char *devlength; + + i = 0; + + if (!map) { + E("slram: not enough parameters.\n"); + return(-EINVAL); + } + while (map) { + devname = devstart = devlength = NULL; + + if (!(devname = strsep(&map, ","))) { + E("slram: No devicename specified.\n"); + break; + } + T("slram: devname = %s\n", devname); + if ((!map) || (!(devstart = strsep(&map, ",")))) { + E("slram: No devicestart specified.\n"); + } + T("slram: devstart = %s\n", devstart); + if ((!map) || (!(devlength = strsep(&map, ",")))) { + E("slram: No devicelength / -end specified.\n"); + } + T("slram: devlength = %s\n", devlength); + if (parse_cmdline(devname, devstart, devlength) != 0) { + return(-EINVAL); + } + } +#else + int count; + + for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); + count++) { + } + + if ((count % 3 != 0) || (count == 0)) { + E("slram: not enough parameters.\n"); + return(-EINVAL); + } + for (i = 0; i < (count / 3); i++) { + devname = map[i * 3]; + + if (parse_cmdline(devname, map[i * 3 + 1], map[i * 3 + 2])!=0) { + return(-EINVAL); + } + + } +#endif /* !MODULE */ + + return(0); +} + +static void __exit cleanup_slram(void) +{ + unregister_devices(); +} + +module_init(init_slram); +module_exit(cleanup_slram); diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 379cd65f42ed..e222dd4b3760 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,10 +1,10 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org + * $Id: ftl.c,v 1.35 2001/06/09 00:40:17 dwmw2 Exp $ * - * - Based on Id: ftl.c,v 1.21 2000/08/01 13:07:49 dwmw2 Exp - * - With the Franz Galiana's set_bam_entry fix from v1.23 - * - Perhaps it's about time I made a branch for the 2.4 series. - - * Originally based on: + * Fixes: Arnaldo Carvalho de Melo + * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups + * + * Based on: */ /*====================================================================== @@ -30,8 +30,8 @@ are Copyright (C) 1999 David A. Hinds. All Rights Reserved. Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the + terms of the GNU General Public License version 2 (the "GPL"), in + which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision @@ -55,11 +55,6 @@ contact M-Systems (http://www.m-sys.com) directly. ======================================================================*/ -#define FTL_DEBUG 5 -#ifdef FTL_DEBUG -#define DEBUGLVL debug -#endif - #include #include #include @@ -108,19 +103,16 @@ /*====================================================================*/ /* Parameters that can be set with 'insmod' */ - -/* Major device # for FTL device */ static int shuffle_freq = 50; - MODULE_PARM(shuffle_freq, "i"); /*====================================================================*/ +/* Major device # for FTL device */ #ifndef FTL_MAJOR #define FTL_MAJOR 44 #endif - /* Funky stuff for setting up a block device */ #define MAJOR_NR FTL_MAJOR #define DEVICE_NAME "ftl" @@ -135,17 +127,8 @@ MODULE_PARM(shuffle_freq, "i"); #include -#ifdef FTL_DEBUG -static int debug = FTL_DEBUG; -MODULE_PARM(debug, "i"); -#endif - /*====================================================================*/ -#ifndef FTL_MAJOR -#define FTL_MAJOR 44 -#endif - /* Maximum number of separate memory devices we'll allow */ #define MAX_DEV 4 @@ -200,7 +183,10 @@ static void ftl_notify_remove(struct mtd_info *mtd); void ftl_freepart(partition_t *part); -static struct mtd_notifier ftl_notifier={ftl_notify_add, ftl_notify_remove, NULL}; +static struct mtd_notifier ftl_notifier = { + add: ftl_notify_add, + remove: ftl_notify_remove, +}; /* Partition state flags */ #define FTL_FORMATTED 0x01 @@ -226,7 +212,6 @@ static struct gendisk ftl_gendisk = { #endif part: ftl_hd, sizes: ftl_sizes, - nr_real: 0 }; /*====================================================================*/ @@ -274,7 +259,7 @@ static int scan_header(partition_t *part) /* Search first megabyte for a valid FTL header */ for (offset = 0; offset < max_offset; - offset += part->mtd->erasesize?part->mtd->erasesize:0x2000) { + offset += part->mtd->erasesize ? : 0x2000) { ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, (unsigned char *)&header); @@ -296,7 +281,7 @@ static int scan_header(partition_t *part) return -1; } if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { - printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %lx\n", + printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n", 1 << header.EraseUnitSize,part->mtd->erasesize); return -1; } @@ -309,7 +294,7 @@ static int build_maps(partition_t *part) erase_unit_header_t header; u_int16_t xvalid, xtrans, i; u_int blocks, j; - int hdr_ok, ret; + int hdr_ok, ret = -1; ssize_t retval; loff_t offset; @@ -318,13 +303,15 @@ static int build_maps(partition_t *part) part->header.NumTransferUnits; part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t), GFP_KERNEL); - if (!part->EUNInfo) return -1; + if (!part->EUNInfo) + goto out; for (i = 0; i < part->DataUnits; i++) part->EUNInfo[i].Offset = 0xffffffff; part->XferInfo = kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t), GFP_KERNEL); - if (!part->XferInfo) return -1; + if (!part->XferInfo) + goto out_EUNInfo; xvalid = xtrans = 0; for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { @@ -334,8 +321,9 @@ static int build_maps(partition_t *part) (unsigned char *)&header); if (ret) - return ret; + goto out_XferInfo; + ret = -1; /* Is this a transfer partition? */ hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0); if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) && @@ -348,7 +336,7 @@ static int build_maps(partition_t *part) if (xtrans == part->header.NumTransferUnits) { printk(KERN_NOTICE "ftl_cs: format error: too many " "transfer units!\n"); - return -1; + goto out_XferInfo; } if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) { part->XferInfo[xtrans].state = XFER_PREPARED; @@ -369,18 +357,22 @@ static int build_maps(partition_t *part) (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) { printk(KERN_NOTICE "ftl_cs: format error: erase units " "don't add up!\n"); - return -1; + goto out_XferInfo; } /* Set up virtual page map */ blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t)); + if (!part->VirtualBlockMap) + goto out_XferInfo; + memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t)); part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t), GFP_KERNEL); - if (!part->bam_cache) return -1; + if (!part->bam_cache) + goto out_VirtualBlockMap; part->bam_index = 0xffff; part->FreeTotal = 0; @@ -395,7 +387,7 @@ static int build_maps(partition_t *part) (unsigned char *)part->bam_cache); if (ret) - return ret; + goto out_bam_cache; for (j = 0; j < part->BlocksPerUnit; j++) { if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) { @@ -410,8 +402,19 @@ static int build_maps(partition_t *part) } } - return 0; - + ret = 0; + goto out; + +out_bam_cache: + kfree(part->bam_cache); +out_VirtualBlockMap: + vfree(part->VirtualBlockMap); +out_XferInfo: + kfree(part->XferInfo); +out_EUNInfo: + kfree(part->EUNInfo); +out: + return ret; } /* build_maps */ /*====================================================================== @@ -699,7 +702,7 @@ static int reclaim_block(partition_t *part) int queued, ret; DEBUG(0, "ftl_cs: reclaiming space...\n"); - DEBUG(4, "NumTransferUnits == %x\n", part->header.NumTransferUnits); + DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits); /* Pick the least erased transfer unit */ best = 0xffffffff; xfer = 0xffff; do { @@ -707,22 +710,22 @@ static int reclaim_block(partition_t *part) for (i = 0; i < part->header.NumTransferUnits; i++) { int n=0; if (part->XferInfo[i].state == XFER_UNKNOWN) { - DEBUG(4,"XferInfo[%d].state == XFER_UNKNOWN\n",i); + DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i); n=1; erase_xfer(part, i); } if (part->XferInfo[i].state == XFER_ERASING) { - DEBUG(4,"XferInfo[%d].state == XFER_ERASING\n",i); + DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i); n=1; queued = 1; } else if (part->XferInfo[i].state == XFER_ERASED) { - DEBUG(4,"XferInfo[%d].state == XFER_ERASED\n",i); + DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i); n=1; prepare_xfer(part, i); } if (part->XferInfo[i].state == XFER_PREPARED) { - DEBUG(4,"XferInfo[%d].state == XFER_PREPARED\n",i); + DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i); n=1; if (part->XferInfo[i].EraseCount <= best) { best = part->XferInfo[i].EraseCount; @@ -730,7 +733,7 @@ static int reclaim_block(partition_t *part) } } if (!n) - DEBUG(4,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state); + DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state); } if (xfer == 0xffff) { @@ -1219,11 +1222,12 @@ static int ftl_reread_partitions(int minor) } whole = minor & ~(MAX_PART-1); - for (i = 0; i < MAX_PART; i++) { + i = MAX_PART - 1; + while (i-- > 0) { if (ftl_hd[whole+i].nr_sects > 0) { kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); - sync_dev(rdev); - invalidate_buffers(rdev); + + invalidate_device(rdev, 1); } ftl_hd[whole+i].start_sect = 0; ftl_hd[whole+i].nr_sects = 0; @@ -1362,7 +1366,8 @@ static void ftl_notify_add(struct mtd_info *mtd) printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", le32_to_cpu(partition->header.FormattedSize) >> 10); #endif - } + } else + kfree(partition); } static void ftl_notify_remove(struct mtd_info *mtd) @@ -1395,13 +1400,10 @@ static void ftl_notify_remove(struct mtd_info *mtd) } } - -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_ftl init_module #define cleanup_ftl cleanup_module #endif -#endif mod_init_t init_ftl(void) { @@ -1409,6 +1411,8 @@ mod_init_t init_ftl(void) memset(myparts, 0, sizeof(myparts)); + DEBUG(0, "$Id: ftl.c,v 1.35 2001/06/09 00:40:17 dwmw2 Exp $\n"); + if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { printk(KERN_NOTICE "ftl_cs: unable to grab major " "device number!\n"); @@ -1449,7 +1453,5 @@ mod_exit_t cleanup_ftl(void) } } -#if LINUX_VERSION_CODE > 0x20300 module_init(init_ftl); module_exit(cleanup_ftl); -#endif diff --git a/drivers/mtd/maps/Config.in b/drivers/mtd/maps/Config.in new file mode 100644 index 000000000000..68b0db082a47 --- /dev/null +++ b/drivers/mtd/maps/Config.in @@ -0,0 +1,41 @@ +# drivers/mtd/maps/Config.in + +# $Id: Config.in,v 1.9.2.1 2001/06/09 19:43:49 dwmw2 Exp $ + +mainmenu_option next_comment + +comment 'Mapping drivers for chip access' + +dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI +if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 + hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 + int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 +fi + +dep_tristate ' Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_SPARC64 +dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI +dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS +dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI +dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI +dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS +dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS +dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS +dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS + dep_bool ' Support for RedBoot partition tables on SA11x0' CONFIG_MTD_SA1100_REDBOOT_PARTITIONS $CONFIG_MTD_SA1100 $CONFIG_MTD_REDBOOT_PARTS + dep_bool ' Support for Compaq bootldr partition tables on SA11x0' CONFIG_MTD_SA1100_BOOTLDR_PARTITIONS $CONFIG_MTD_SA1100 $CONFIG_MTD_BOOTLDR_PARTS +dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_PARTITIONS +dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_XSCALE_IQ80310 +dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI_INTELSTD $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI_AMDSTD +dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS +if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000 + hex ' Physical length of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_LEN 0x4000000 + int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2 +fi +dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI +dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC +dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC +dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC +dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT +endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile new file mode 100644 index 000000000000..7f501d728199 --- /dev/null +++ b/drivers/mtd/maps/Makefile @@ -0,0 +1,29 @@ +# +# linux/drivers/maps/Makefile +# +# $Id: Makefile,v 1.9.2.1 2001/06/09 19:43:49 dwmw2 Exp $ + +O_TARGET := mapslink.o + +# Chip mappings + +obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o +obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o +obj-$(CONFIG_MTD_DC21285) += dc21285.o +obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o +obj-$(CONFIG_MTD_IQ80310) += iq80310.o +obj-$(CONFIG_MTD_NORA) += nora.o +obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o +obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o +obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o +obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o +obj-$(CONFIG_MTD_NETSC520) += netsc520.o +obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o +obj-$(CONFIG_MTD_VMAX) += vmax301.o +obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o +obj-$(CONFIG_MTD_OCELOT) += ocelot.o + +include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c new file mode 100644 index 000000000000..cae567224ffd --- /dev/null +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -0,0 +1,184 @@ +/* + * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson + * + * $Id: cfi_flagadm.c,v 1.5 2001/05/29 15:47:49 kd Exp $ + * + * 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 +#include + + +/* We split the flash chip up into four parts. + * 1: bootloader firts 128k (0x00000000 - 0x0001FFFF) size 0x020000 + * 2: kernel 640k (0x00020000 - 0x000BFFFF) size 0x0A0000 + * 3: compressed 1536k root ramdisk (0x000C0000 - 0x0023FFFF) size 0x180000 + * 4: writeable diskpartition (jffs)(0x00240000 - 0x003FFFFF) size 0x1C0000 + */ + +#define FLASH_PHYS_ADDR 0x40000000 +#define FLASH_SIZE 0x400000 + +#define FLASH_PARTITION0_ADDR 0x00000000 +#define FLASH_PARTITION0_SIZE 0x00020000 + +#define FLASH_PARTITION1_ADDR 0x00020000 +#define FLASH_PARTITION1_SIZE 0x000A0000 + +#define FLASH_PARTITION2_ADDR 0x000C0000 +#define FLASH_PARTITION2_SIZE 0x00180000 + +#define FLASH_PARTITION3_ADDR 0x00240000 +#define FLASH_PARTITION3_SIZE 0x001C0000 + +__u8 flagadm_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 flagadm_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 flagadm_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info flagadm_map = { + name: "FlagaDM flash device", + size: FLASH_SIZE, + buswidth: 2, + read8: flagadm_read8, + read16: flagadm_read16, + read32: flagadm_read32, + copy_from: flagadm_copy_from, + write8: flagadm_write8, + write16: flagadm_write16, + write32: flagadm_write32, + copy_to: flagadm_copy_to +}; + +struct mtd_partition flagadm_parts[] = { + { + name : "Bootloader", + offset : FLASH_PARTITION0_ADDR, + size : FLASH_PARTITION0_SIZE + }, + { + name : "Kernel image", + offset : FLASH_PARTITION1_ADDR, + size : FLASH_PARTITION1_SIZE + }, + { + name : "Initial ramdisk image", + offset : FLASH_PARTITION2_ADDR, + size : FLASH_PARTITION2_SIZE + }, + { + name : "Persistant storage", + offset : FLASH_PARTITION3_ADDR, + size : FLASH_PARTITION3_SIZE + } +}; + +#define PARTITION_COUNT (sizeof(flagadm_parts)/sizeof(struct mtd_partition)) + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_flagadm init_module +#define cleanup_flagadm cleanup_module +#endif + +static struct mtd_info *mymtd; + +int __init init_flagadm(void) +{ + printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + + flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + + if (!flagadm_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("cfi", &flagadm_map); + if (mymtd) { + mymtd->module = THIS_MODULE; + add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT); + printk(KERN_NOTICE "FlagaDM flash device initialized\n"); + return 0; + } + + iounmap((void *)flagadm_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_flagadm(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (flagadm_map.map_priv_1) { + iounmap((void *)flagadm_map.map_priv_1); + flagadm_map.map_priv_1 = 0; + } +} + +module_init(init_flagadm); +module_exit(cleanup_flagadm); diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c new file mode 100644 index 000000000000..ec6c37d6a1c4 --- /dev/null +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -0,0 +1,314 @@ +/* + * $Id: cstm_mips_ixx.c,v 1.3 2001/06/02 14:52:23 dwmw2 Exp $ + * + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. + * + * Basically physmap.c with the addition of partitions and + * an array of mapping info to accomodate more than one flash type per board. + * + * Copyright 2000 MontaVista Software 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 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 +#include +#include + +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) +#include +#endif + +__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(map->map_priv_1 + ofs); +} + +__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(map->map_priv_1 + ofs); +} + +__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(map->map_priv_1 + ofs); +} + +void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(map->map_priv_1 + adr) = d; +} + +void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(map->map_priv_1 + adr) = d; +} + +void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(map->map_priv_1 + adr) = d; +} + +void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) +#define CC_GCR 0xB4013818 +#define CC_GPBCR 0xB401380A +#define CC_GPBDR 0xB4013808 +#define CC_M68K_DEVICE 1 +#define CC_M68K_FUNCTION 6 +#define CC_CONFADDR 0xB8004000 +#define CC_CONFDATA 0xB8004004 +#define CC_FC_FCR 0xB8002004 +#define CC_FC_DCR 0xB8002008 +#define CC_GPACR 0xB4013802 +#define CC_GPAICR 0xB4013804 +#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + +void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) +{ + if (vpp) { +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + __u16 data; + __u8 data1; + static u8 first = 1; + + // Set GPIO port B pin3 to high + data = *(__u16 *)(CC_GPBCR); + data = (data & 0xff0f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08; + if (first) { + first = 0; + /* need to have this delay for first + enabling vpp after powerup */ + udelay(40); + } +#endif /* CONFIG_MIPS_ITE8172 */ + } + else { +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + __u16 data; + + // Set GPIO port B pin3 to high + data = *(__u16 *)(CC_GPBCR); + data = (data & 0xff3f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; +#endif /* CONFIG_MIPS_ITE8172 */ + } +} + +const struct map_info basic_cstm_mips_ixx_map = { + NULL, + 0, + 0, + cstm_mips_ixx_read8, + cstm_mips_ixx_read16, + cstm_mips_ixx_read32, + cstm_mips_ixx_copy_from, + cstm_mips_ixx_write8, + cstm_mips_ixx_write16, + cstm_mips_ixx_write32, + cstm_mips_ixx_copy_to, + cstm_mips_ixx_set_vpp, + 0, + 0 +}; + +/* board and partition description */ + +#define MAX_PHYSMAP_PARTITIONS 8 +struct cstm_mips_ixx_info { + char *name; + unsigned long window_addr; + unsigned long window_size; + int buswidth; + int num_partitions; +}; + +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) +#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type +const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = +{ + { // 28F128J3A in 2x16 configuration + "big flash", // name + 0x08000000, // window_addr + 0x02000000, // window_size + 4, // buswidth + 1, // num_partitions + } + +}; +static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { +{ // 28F128J3A in 2x16 configuration + { + name: "main partition ", + size: 0x02000000, // 128 x 2 x 128k byte sectors + offset: 0, + }, +}, +}; +#else /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ +#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type +const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = +{ + { + "MTD flash", // name + CONFIG_MTD_CSTM_MIPS_IXX_START, // window_addr + CONFIG_MTD_CSTM_MIPS_IXX_LEN, // window_size + CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // buswidth + 1, // num_partitions + }, + +}; +static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { +{ + { + name: "main partition", + size: CONFIG_MTD_CSTM_MIPS_IXX_LEN, + offset: 0, + }, +}, +}; +#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + +struct map_info cstm_mips_ixx_map[PHYSMAP_NUMBER]; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_cstm_mips_ixx init_module +#define cleanup_cstm_mips_ixx cleanup_module +#endif + +int __init init_cstm_mips_ixx(void) +{ + int i; + int jedec; + struct mtd_info *mymtd; + struct mtd_partition *parts; + + /* Initialize mapping */ + for (i=0;imodule = THIS_MODULE; + + cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; + add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); + } + else + return -ENXIO; + } + return 0; +} + +static void __exit cleanup_cstm_mips_ixx(void) +{ + int i; + struct mtd_info *mymtd; + + for (i=0;i> 8 >>1; // Bug: we must shift one more bit + + /* need to set ITE flash to 32 bits instead of default 8 */ +#ifdef CONFIG_MIPS_IVR + *(__u32 *)CC_FC_FCR = 0x55; + *(__u32 *)CC_GPACR = 0xfffc; +#else + *(__u32 *)CC_FC_FCR = 0x77; +#endif + /* turn bursting off */ + *(__u32 *)CC_FC_DCR = 0x0; + + /* setup for one chip 4 byte PCI access */ + PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x60, size | base); + PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x64, 0x02); +} +#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + +module_init(init_cstm_mips_ixx); +module_exit(cleanup_cstm_mips_ixx); diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c new file mode 100644 index 000000000000..bfdd2884f2aa --- /dev/null +++ b/drivers/mtd/maps/dbox2-flash.c @@ -0,0 +1,151 @@ +/* + * $Id: dbox2-flash.c,v 1.2 2001/04/26 15:42:43 dwmw2 Exp $ + * + * Nokia / Sagem D-Box 2 flash driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw + size: 128 * 1024, + offset: 0, + mask_flags: MTD_WRITEABLE}, + {name: "PPC bootloader", // flfs + size: 128 * 1024, + offset: MTDPART_OFS_APPEND, + mask_flags: 0}, + {name: "Kernel", // idxfs + size: 768 * 1024, + offset: MTDPART_OFS_APPEND, + mask_flags: 0}, + {name: "System", // jffs + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + mask_flags: 0}}; + +#define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) + +#define WINDOW_ADDR 0x10000000 +#define WINDOW_SIZE 0x800000 + +static struct mtd_info *mymtd; + +__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info dbox2_flash_map = { + name: "D-Box 2 flash memory", + size: WINDOW_SIZE, + buswidth: 4, + read8: dbox2_flash_read8, + read16: dbox2_flash_read16, + read32: dbox2_flash_read32, + copy_from: dbox2_flash_copy_from, + write8: dbox2_flash_write8, + write16: dbox2_flash_write16, + write32: dbox2_flash_write32, + copy_to: dbox2_flash_copy_to +}; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_dbox2_flash init_module +#define cleanup_dbox2_flash cleanup_module +#endif + +mod_init_t init_dbox2_flash(void) +{ + printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); + dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + + if (!dbox2_flash_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + + // Probe for dual Intel 28F320 or dual AMD + mymtd = do_map_probe("cfi", &dbox2_flash_map); + if (!mymtd) { + // Probe for single Intel 28F640 + dbox2_flash_map.buswidth = 2; + + mymtd = do_map_probe("cfi", &dbox2_flash_map); + } + + if (mymtd) { + mymtd->module = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + + return 0; + } + + iounmap((void *)dbox2_flash_map.map_priv_1); + return -ENXIO; +} + +mod_exit_t cleanup_dbox2_flash(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (dbox2_flash_map.map_priv_1) { + iounmap((void *)dbox2_flash_map.map_priv_1); + dbox2_flash_map.map_priv_1 = 0; + } +} + +module_init(init_dbox2_flash); +module_exit(cleanup_dbox2_flash); + diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c new file mode 100644 index 000000000000..830ffdd28b68 --- /dev/null +++ b/drivers/mtd/maps/dc21285.c @@ -0,0 +1,193 @@ +/* + * MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip) + * + * (C) 2000 Nicolas Pitre + * + * This code is GPL + * + * $Id: dc21285.c,v 1.4 2001/04/26 15:40:23 dwmw2 Exp $ + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + + +static struct mtd_info *mymtd; + +__u8 dc21285_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8*)(map->map_priv_1 + ofs); +} + +__u16 dc21285_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16*)(map->map_priv_1 + ofs); +} + +__u32 dc21285_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32*)(map->map_priv_1 + ofs); +} + +void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void*)(map->map_priv_1 + from), len); +} + +void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *CSR_ROMWRITEREG = adr; + adr &= ~3; + *(__u8*)(map->map_priv_1 + adr) = d; +} + +void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *CSR_ROMWRITEREG = adr; + adr &= ~1; + *(__u16*)(map->map_priv_1 + adr) = d; +} + +void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32*)(map->map_priv_1 + adr) = d; +} + +void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + switch (map->buswidth) { + case 4: + while (len > 0) { + __u32 d = *((__u32*)from)++; + dc21285_write32(map, d, to); + to += 4; + len -= 4; + } + break; + case 2: + while (len > 0) { + __u16 d = *((__u16*)from)++; + dc21285_write16(map, d, to); + to += 2; + len -= 2; + } + break; + case 1: + while (len > 0) { + __u8 d = *((__u8*)from)++; + dc21285_write8(map, d, to); + to++; + len--; + } + break; + } +} + +struct map_info dc21285_map = { + name: "DC21285 flash", + size: 16*1024*1024, + read8: dc21285_read8, + read16: dc21285_read16, + read32: dc21285_read32, + copy_from: dc21285_copy_from, + write8: dc21285_write8, + write16: dc21285_write16, + write32: dc21285_write32, + copy_to: dc21285_copy_to +}; + + +/* Partition stuff */ +static struct mtd_partition *dc21285_parts; + +extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); + +int __init init_dc21285(void) +{ + /* Determine buswidth */ + switch (*CSR_SA110_CNTL & (3<<14)) { + case SA110_CNTL_ROMWIDTH_8: + dc21285_map.buswidth = 1; + break; + case SA110_CNTL_ROMWIDTH_16: + dc21285_map.buswidth = 2; + break; + case SA110_CNTL_ROMWIDTH_32: + dc21285_map.buswidth = 4; + break; + default: + printk (KERN_ERR "DC21285 flash: undefined buswidth\n"); + return -ENXIO; + } + printk (KERN_NOTICE "DC21285 flash support (%d-bit buswidth)\n", + dc21285_map.buswidth*8); + + /* Let's map the flash area */ + dc21285_map.map_priv_1 = (unsigned long)__ioremap(DC21285_FLASH, 16*1024*1024, 0); + if (!dc21285_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + + mymtd = do_map_probe("cfi", &dc21285_map); + if (mymtd) { + int nrparts; + + mymtd->module = THIS_MODULE; + + /* partition fixup */ + + nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); + if (nrparts <=0) { + printk(KERN_NOTICE "RedBoot partition table failed\n"); + iounmap((void *)dc21285_map.map_priv_1); + return -ENXIO; + } + + add_mtd_partitions(mymtd, dc21285_parts, nrparts); + + /* + * Flash timing is determined with bits 19-16 of the + * CSR_SA110_CNTL. The value is the number of wait cycles, or + * 0 for 16 cycles (the default). Cycles are 20 ns. + * Here we use 7 for 140 ns flash chips. + */ + /* access time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); + /* burst time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); + /* tristate time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); + + return 0; + } + + iounmap((void *)dc21285_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_dc21285(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + mymtd = NULL; + } + if (dc21285_map.map_priv_1) { + iounmap((void *)dc21285_map.map_priv_1); + dc21285_map.map_priv_1 = 0; + } + if(dc21285_parts) + kfree(dc21285_parts); +} + +module_init(init_dc21285); +module_exit(cleanup_dc21285); diff --git a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c new file mode 100644 index 000000000000..3eb07b9e49c1 --- /dev/null +++ b/drivers/mtd/maps/elan-104nc.c @@ -0,0 +1,277 @@ +/* elan-104nc.c -- MTD map driver for Arcom Control Systems ELAN-104NC + + Copyright (C) 2000 Arcom Control System Ltd + + 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 + + $Id: elan-104nc.c,v 1.10 2001/06/02 14:30:44 dwmw2 Exp $ + +The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 +mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. + +The flash is accessed as follows: + + 32 kbyte memory window at 0xb0000-0xb7fff + + 16 bit I/O port (0x22) for some sort of paging. + +The single flash device is divided into 3 partition which appear as seperate +MTD devices. + +Linux thinks that the I/O port is used by the PIC and hence check_region() will +always fail. So we don't do it. I just hope it doesn't break anything. +*/ +#include +#include +#include +#include +#include + +#include +#include + +#define WINDOW_START 0xb0000 +/* Number of bits in offset. */ +#define WINDOW_SHIFT 15 +#define WINDOW_LENGTH (1 << WINDOW_SHIFT) +/* The bits for the offset into the window. */ +#define WINDOW_MASK (WINDOW_LENGTH-1) +#define PAGE_IO 0x22 +#define PAGE_IO_SIZE 2 + +static volatile int page_in_window = -1; // Current page in window. +static unsigned long iomapadr; +static spinlock_t elan_104nc_spin = SPIN_LOCK_UNLOCKED; + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[]={ + { name: "ELAN-104NC flash boot partition", + offset: 0, + size: 640*1024 }, + { name: "ELAN-104NC flash partition 1", + offset: 640*1024, + size: 896*1024 }, + { name: "ELAN-104NC flash partition 2", + offset: (640+896)*1024 } +}; +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +/* + * If no idea what is going on here. This is taken from the FlashFX stuff. + */ +#define ROMCS 1 + +static inline void elan_104nc_setup(void) +{ + u16 t; + + outw( 0x0023 + ROMCS*2, PAGE_IO ); + t=inb( PAGE_IO+1 ); + + t=(t & 0xf9) | 0x04; + + outw( ((0x0023 + ROMCS*2) | (t << 8)), PAGE_IO ); +} + +static inline void elan_104nc_page(struct map_info *map, unsigned long ofs) +{ + unsigned long page = ofs >> WINDOW_SHIFT; + + if( page!=page_in_window ) { + int cmd1; + int cmd2; + + cmd1=(page & 0x700) + 0x0833 + ROMCS*0x4000; + cmd2=((page & 0xff) << 8) + 0x0032; + + outw( cmd1, PAGE_IO ); + outw( cmd2, PAGE_IO ); + + page_in_window = page; + } +} + + +static __u8 elan_104nc_read8(struct map_info *map, unsigned long ofs) +{ + __u8 ret; + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, ofs); + ret = readb(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); + return ret; +} + +static __u16 elan_104nc_read16(struct map_info *map, unsigned long ofs) +{ + __u16 ret; + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, ofs); + ret = readw(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); + return ret; +} + +static __u32 elan_104nc_read32(struct map_info *map, unsigned long ofs) +{ + __u32 ret; + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, ofs); + ret = readl(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); + return ret; +} + +static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(from & WINDOW_MASK); + + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, from); + memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); + spin_unlock(&elan_104nc_spin); + (__u8*)to += thislen; + from += thislen; + len -= thislen; + } +} + +static void elan_104nc_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, adr); + writeb(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); +} + +static void elan_104nc_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, adr); + writew(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); +} + +static void elan_104nc_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, adr); + writel(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&elan_104nc_spin); +} + +static void elan_104nc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(to & WINDOW_MASK); + + spin_lock(&elan_104nc_spin); + elan_104nc_page(map, to); + memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); + spin_unlock(&elan_104nc_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static struct map_info elan_104nc_map = { + name: "ELAN-104NC flash", + size: 8*1024*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ + buswidth: 2, + read8: elan_104nc_read8, + read16: elan_104nc_read16, + read32: elan_104nc_read32, + copy_from: elan_104nc_copy_from, + write8: elan_104nc_write8, + write16: elan_104nc_write16, + write32: elan_104nc_write32, + copy_to: elan_104nc_copy_to +}; + +/* MTD device for all of the flash. */ +static struct mtd_info *all_mtd; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_elan_104nc init_module +#define cleanup_elan_104nc cleanup_module +#endif + +mod_exit_t cleanup_elan_104nc(void) +{ + if( all_mtd ) { + del_mtd_partitions( all_mtd ); + map_destroy( all_mtd ); + } + + iounmap((void *)iomapadr); + release_region(PAGE_IO,PAGE_IO_SIZE); +} + +mod_init_t init_elan_104nc(void) +{ + /* Urg! We use I/O port 0x22 without request_region()ing it */ + /* + if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { + printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", + elan_104nc_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); + return -EAGAIN; + } + */ + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", + elan_104nc_map.name ); + return -EIO; + } + + /* + request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" ); + */ + + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + elan_104nc_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, + WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); + + elan_104nc_setup(); + + /* Probe for chip. */ + all_mtd = do_map_probe("cfi", &elan_104nc_map ); + if( !all_mtd ) { + cleanup_elan_104nc(); + return -ENXIO; + } + + all_mtd->module=THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS ); + + return 0; +} + +module_init(init_elan_104nc); +module_exit(cleanup_elan_104nc); diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c new file mode 100644 index 000000000000..6b66955b636b --- /dev/null +++ b/drivers/mtd/maps/iq80310.c @@ -0,0 +1,133 @@ +/* + * $Id: iq80310.c,v 1.3 2001/04/26 15:40:23 dwmw2 Exp $ + * + * Mapping for the Intel XScale IQ80310 evaluation board + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software 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 + + +#define WINDOW_ADDR 0 +#define WINDOW_SIZE 8*1024*1024 +#define BUSWIDTH 1 + +static struct mtd_info *mymtd; + +static __u8 iq80310_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(map->map_priv_1 + ofs); +} + +static __u16 iq80310_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(map->map_priv_1 + ofs); +} + +static __u32 iq80310_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(map->map_priv_1 + ofs); +} + +static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(map->map_priv_1 + adr) = d; +} + +static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(map->map_priv_1 + adr) = d; +} + +static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(map->map_priv_1 + adr) = d; +} + +static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(map->map_priv_1 + to), from, len); +} + +static struct map_info iq80310_map = { + name: "IQ80310 flash", + size: WINDOW_SIZE, + buswidth: BUSWIDTH, + read8: iq80310_read8, + read16: iq80310_read16, + read32: iq80310_read32, + copy_from: iq80310_copy_from, + write8: iq80310_write8, + write16: iq80310_write16, + write32: iq80310_write32, + copy_to: iq80310_copy_to +}; + +static struct mtd_partition iq80310_partitions[3] = { + { + name: "firmware", + size: 0x00080000, + offset: 0, + mask_flags: MTD_WRITEABLE /* force read-only */ + },{ + name: "kernel", + size: 0x00080000, + offset: 0x00080000, + },{ + name: "filesystem", + size: 0x00700000, + offset: 0x00100000 + } +}; + +static int __init init_iq80310(void) +{ + iq80310_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0); + + if (!iq80310_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("cfi", &iq80310_map); + if (mymtd) { + mymtd->module = THIS_MODULE; + add_mtd_partitions(mymtd, iq80310_partitions, 3); + return 0; + } + + iounmap((void *)iq80310_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_iq80310(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (iq80310_map.map_priv_1) { + iounmap((void *)iq80310_map.map_priv_1); + iq80310_map.map_priv_1 = 0; + } +} + +module_init(init_iq80310); +module_exit(cleanup_iq80310); + diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c new file mode 100644 index 000000000000..587f3499b10d --- /dev/null +++ b/drivers/mtd/maps/netsc520.c @@ -0,0 +1,192 @@ +/* netsc520.c -- MTD map driver for AMD NetSc520 Demonstration Board + * + * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) + * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH + * + * $Id: netsc520.c,v 1.3 2001/06/02 14:52:23 dwmw2 Exp $ + * + * 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 + * + * The NetSc520 is a demonstration board for the Elan Sc520 processor available + * from AMD. It has a single back of 16 megs of 32-bit Flash ROM and another + * 16 megs of SDRAM. + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* +** The single, 16 megabyte flash bank is divided into four virtual +** partitions. The first partition is 768 KiB and is intended to +** store the kernel image loaded by the bootstrap loader. The second +** partition is 256 KiB and holds the BIOS image. The third +** partition is 14.5 MiB and is intended for the flash file system +** image. The last partition is 512 KiB and contains another copy +** of the BIOS image and the reset vector. +** +** Only the third partition should be mounted. The first partition +** should not be mounted, but it can erased and written to using the +** MTD character routines. The second and fourth partitions should +** not be touched - it is possible to corrupt the BIOS image by +** mounting these partitions, and potentially the board will not be +** recoverable afterwards. +*/ + +static __u8 netsc520_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 netsc520_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 netsc520_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +} + +static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio((void *)(map->map_priv_1 + to), from, len); +} + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[]={ + { + name: "NetSc520 boot kernel", + offset: 0, + size: 0xc0000 + }, + { + name: "NetSc520 Low BIOS", + offset: 0xc0000, + size: 0x40000 + }, + { + name: "NetSc520 file system", + offset: 0x100000, + size: 0xe80000 + }, + { + name: "NetSc520 High BIOS", + offset: 0xf80000, + size: 0x80000 + }, +}; +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +/* + * If no idea what is going on here. This is taken from the FlashFX stuff. + */ +#define ROMCS 1 + + +#define WINDOW_SIZE 0x00100000 +#define WINDOW_ADDR 0x00200000 + +static struct map_info netsc520_map = { + name: "netsc520 Flash Bank", + size: WINDOW_SIZE, + buswidth: 4, + read8: netsc520_read8, + read16: netsc520_read16, + read32: netsc520_read32, + copy_from: netsc520_copy_from, + write8: netsc520_write8, + write16: netsc520_write16, + write32: netsc520_write32, + copy_to: netsc520_copy_to, + map_priv_2: WINDOW_ADDR +}; + +#define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) + +static struct mtd_info *mymtd; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_netsc520 init_module +#define cleanup_netsc520 cleanup_module +#endif + + +static int __init init_netsc520(void) +{ + printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2); + netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size); + + if (!netsc520_map.map_priv_1) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } + mymtd = do_map_probe("cfi", &netsc520_map); + if(!mymtd) + mymtd = do_map_probe("ram", &netsc520_map); + if(!mymtd) + mymtd = do_map_probe("rom", &netsc520_map); + + if (!mymtd) { + iounmap((void *)netsc520_map.map_priv_1); + return -ENXIO; + } + + mymtd->module = THIS_MODULE; + add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); + return 0; +} + +static void __exit cleanup_netsc520(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (netsc520_map.map_priv_1) { + iounmap((void *)netsc520_map.map_priv_1); + netsc520_map.map_priv_1 = 0; + } +} + +module_init(init_netsc520); +module_exit(cleanup_netsc520); diff --git a/drivers/mtd/nora.c b/drivers/mtd/maps/nora.c similarity index 96% rename from drivers/mtd/nora.c rename to drivers/mtd/maps/nora.c index 48da299fc880..149affb22678 100644 --- a/drivers/mtd/nora.c +++ b/drivers/mtd/maps/nora.c @@ -1,5 +1,5 @@ /* - * $Id: nora.c,v 1.17 2000/12/03 19:32:21 dwmw2 Exp $ + * $Id: nora.c,v 1.19 2001/04/26 15:40:23 dwmw2 Exp $ * * This is so simple I love it. */ @@ -177,11 +177,9 @@ int __init init_nora(void) { printk(KERN_NOTICE "nora flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - mymtd = do_cfi_probe(&nora_map); + mymtd = do_map_probe("cfi", &nora_map); if (mymtd) { -#ifdef MODULE - mymtd->module = &__this_module; -#endif + mymtd->module = THIS_MODULE; add_mtd_device(&nora_mtds[2]); add_mtd_device(&nora_mtds[0]); diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c new file mode 100644 index 000000000000..296df96d6e89 --- /dev/null +++ b/drivers/mtd/maps/ocelot.c @@ -0,0 +1,199 @@ +/* + * $Id: ocelot.c,v 1.4 2001/06/08 15:36:27 dwmw2 Exp $ + * + * Flash on Momenco Ocelot + */ + +#include +#include +#include +#include +#include +#include +#include + +#define OCELOT_PLD 0x2c000000 +#define FLASH_WINDOW_ADDR 0x2fc00000 +#define FLASH_WINDOW_SIZE 0x00080000 +#define FLASH_BUSWIDTH 1 +#define NVRAM_WINDOW_ADDR 0x2c800000 +#define NVRAM_WINDOW_SIZE 0x00007FF0 +#define NVRAM_BUSWIDTH 1 + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static unsigned int cacheflush = 0; + +static struct mtd_info *flash_mtd; +static struct mtd_info *nvram_mtd; + +__u8 ocelot_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + cacheflush = 1; + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + if (cacheflush) { + dma_cache_inv(map->map_priv_2, map->size); + cacheflush = 0; + } + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + /* If we use memcpy, it does word-wide writes. Even though we told the + GT64120A that it's an 8-bit wide region, word-wide writes don't work. + We end up just writing the first byte of the four to all four bytes. + So we have this loop instead */ + while(len) { + __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); + from++; + to++; + len--; + } +} + +static struct mtd_partition *parsed_parts; + +struct map_info ocelot_flash_map = { + name: "Ocelot boot flash", + size: FLASH_WINDOW_SIZE, + buswidth: FLASH_BUSWIDTH, + read8: ocelot_read8, + copy_from: ocelot_copy_from_cache, + write8: ocelot_write8, +}; + +struct map_info ocelot_nvram_map = { + name: "Ocelot NVRAM", + size: NVRAM_WINDOW_SIZE, + buswidth: NVRAM_BUSWIDTH, + read8: ocelot_read8, + copy_from: ocelot_copy_from, + write8: ocelot_write8, + copy_to: ocelot_copy_to +}; + +static int __init init_ocelot_maps(void) +{ + void *pld; + int nr_parts; + unsigned char brd_status; + + printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n", + FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR); + + /* First check whether the flash jumper is present */ + pld = ioremap(OCELOT_PLD, 0x10); + if (!pld) { + printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n"); + return -EIO; + } + brd_status = readb(pld+4); + iounmap(pld); + + /* Now ioremap the NVRAM space */ + ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); + if (!ocelot_nvram_map.map_priv_1) { + printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); + return -EIO; + } + // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1; + + /* And do the RAM probe on it to get an MTD device */ + nvram_mtd = do_map_probe("ram", &ocelot_nvram_map); + if (!nvram_mtd) { + printk("NVRAM probe failed\n"); + goto fail_1; + } + nvram_mtd->module = THIS_MODULE; + nvram_mtd->erasesize = 16; + + /* Now map the flash space */ + ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); + if (!ocelot_flash_map.map_priv_1) { + printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); + goto fail_2; + } + /* Now the cached version */ + ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); + + if (!ocelot_flash_map.map_priv_2) { + /* Doesn't matter if it failed. Just use the uncached version */ + ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1; + } + + /* Only probe for flash if the write jumper is present */ + if (brd_status & 0x40) { + flash_mtd = do_map_probe("jedec", &ocelot_flash_map); + } else { + printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n"); + } + /* If that failed or the jumper's absent, pretend it's ROM */ + if (!flash_mtd) { + flash_mtd = do_map_probe("rom", &ocelot_flash_map); + /* If we're treating it as ROM, set the erase size */ + if (flash_mtd) + flash_mtd->erasesize = 0x10000; + } + if (!flash_mtd) + goto fail3; + + add_mtd_device(nvram_mtd); + + flash_mtd->module = THIS_MODULE; + nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); + + if (nr_parts) + add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + else + add_mtd_device(flash_mtd); + + return 0; + + fail3: + iounmap((void *)ocelot_flash_map.map_priv_1); + if (ocelot_flash_map.map_priv_2 && + ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) + iounmap((void *)ocelot_flash_map.map_priv_2); + fail_2: + map_destroy(nvram_mtd); + fail_1: + iounmap((void *)ocelot_nvram_map.map_priv_1); + + return -ENXIO; +} + +static void __exit cleanup_ocelot_maps(void) +{ + del_mtd_device(nvram_mtd); + map_destroy(nvram_mtd); + iounmap((void *)ocelot_nvram_map.map_priv_1); + + if (parsed_parts) + del_mtd_partitions(flash_mtd); + else + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); + iounmap((void *)ocelot_flash_map.map_priv_1); + if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) + iounmap((void *)ocelot_flash_map.map_priv_2); +} + +module_init(init_ocelot_maps); +module_exit(cleanup_ocelot_maps); + diff --git a/drivers/mtd/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c similarity index 95% rename from drivers/mtd/octagon-5066.c rename to drivers/mtd/maps/octagon-5066.c index 9561e1b0a56e..58864e51a77c 100644 --- a/drivers/mtd/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.12.2.1 2001/02/15 10:12:48 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.17 2001/06/02 14:30:44 dwmw2 Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -188,7 +188,7 @@ static struct mtd_info *oct5066_mtd[2] = {NULL, NULL}; change pages while monitoring the window. A change in the window, controlled by the PAGE_IO port is a functioning 5066 board. This will fail if the thing in the socket is set to a uniform value. */ -static int __init OctProbe() +static int __init OctProbe(void) { unsigned int Base = (1 << 6); unsigned long I; @@ -260,13 +260,13 @@ int __init init_oct5066(void) WINDOW_START+WINDOW_LENGTH); for (i=0; i<2; i++) { - oct5066_mtd[i] = do_cfi_probe(&oct5066_map[i]); + oct5066_mtd[i] = do_map_probe("cfi", &oct5066_map[i]); if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_jedec_probe(&oct5066_map[i]); + oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]); if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_ram_probe(&oct5066_map[i]); + oct5066_mtd[i] = do_map_probe("ram", &oct5066_map[i]); if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_rom_probe(&oct5066_map[i]); + oct5066_mtd[i] = do_map_probe("rom", &oct5066_map[i]); if (oct5066_mtd[i]) { oct5066_mtd[i]->module = THIS_MODULE; add_mtd_device(oct5066_mtd[i]); diff --git a/drivers/mtd/physmap.c b/drivers/mtd/maps/physmap.c similarity index 84% rename from drivers/mtd/physmap.c rename to drivers/mtd/maps/physmap.c index 31ac39310fe2..a7a532afec00 100644 --- a/drivers/mtd/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.8 2000/11/27 08:50:22 dwmw2 Exp $ + * $Id: physmap.c,v 1.13 2001/06/10 00:14:55 dwmw2 Exp $ * * Normal mappings of chips in physical memory */ @@ -21,17 +21,17 @@ static struct mtd_info *mymtd; __u8 physmap_read8(struct map_info *map, unsigned long ofs) { - return readb(map->map_priv_1 + ofs); + return __raw_readb(map->map_priv_1 + ofs); } __u16 physmap_read16(struct map_info *map, unsigned long ofs) { - return readw(map->map_priv_1 + ofs); + return __raw_readw(map->map_priv_1 + ofs); } __u32 physmap_read32(struct map_info *map, unsigned long ofs) { - return readl(map->map_priv_1 + ofs); + return __raw_readl(map->map_priv_1 + ofs); } void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) @@ -41,17 +41,20 @@ void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) { - writeb(d, map->map_priv_1 + adr); + __raw_writeb(d, map->map_priv_1 + adr); + mb(); } void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) { - writew(d, map->map_priv_1 + adr); + __raw_writew(d, map->map_priv_1 + adr); + mb(); } void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) { - writel(d, map->map_priv_1 + adr); + __raw_writel(d, map->map_priv_1 + adr); + mb(); } void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) @@ -87,11 +90,10 @@ int __init init_physmap(void) printk("Failed to ioremap\n"); return -EIO; } - mymtd = do_cfi_probe(&physmap_map); + mymtd = do_map_probe("cfi", &physmap_map); if (mymtd) { -#ifdef MODULE - mymtd->module = &__this_module; -#endif + mymtd->module = THIS_MODULE; + add_mtd_device(mymtd); return 0; } diff --git a/drivers/mtd/pnc2000.c b/drivers/mtd/maps/pnc2000.c similarity index 88% rename from drivers/mtd/pnc2000.c rename to drivers/mtd/maps/pnc2000.c index a93dd354870e..2cfb5fcaa29a 100644 --- a/drivers/mtd/pnc2000.c +++ b/drivers/mtd/maps/pnc2000.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.4 2000/11/27 08:50:22 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.8 2001/06/10 00:09:45 dwmw2 Exp $ */ #include @@ -107,15 +107,15 @@ static struct mtd_partition pnc_partitions[3] = { static struct mtd_info *mymtd; #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) -#define init_pnc init_module -#define cleanup_pnc cleanup_module +#define init_pnc2000 init_module +#define cleanup_pnc2000 cleanup_module #endif -int __init init_pnc(void) +int __init init_pnc2000(void) { printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - mymtd = do_cfi_probe(&pnc_map); + mymtd = do_map_probe("cfi", &pnc_map); if (mymtd) { mymtd->module = THIS_MODULE; return add_mtd_partitions(mymtd, pnc_partitions, 3); @@ -124,7 +124,7 @@ int __init init_pnc(void) return -ENXIO; } -static void __exit cleanup_pnc(void) +static void __exit cleanup_pnc2000(void) { if (mymtd) { del_mtd_partitions(mymtd); @@ -132,5 +132,5 @@ static void __exit cleanup_pnc(void) } } -module_init(init_pnc); -module_exit(cleanup_pnc); +module_init(init_pnc2000); +module_exit(cleanup_pnc2000); diff --git a/drivers/mtd/rpxlite.c b/drivers/mtd/maps/rpxlite.c similarity index 92% rename from drivers/mtd/rpxlite.c rename to drivers/mtd/maps/rpxlite.c index 3c19cfb2d235..148bd6ebc09d 100644 --- a/drivers/mtd/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,5 +1,5 @@ /* - * $Id: rpxlite.c,v 1.8 2000/12/09 22:00:31 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.12 2001/04/26 15:40:23 dwmw2 Exp $ * * Handle mapping of the flash on the RPX Lite and CLLF boards */ @@ -19,7 +19,7 @@ static struct mtd_info *mymtd; __u8 rpxlite_read8(struct map_info *map, unsigned long ofs) { - return readb(map->map_priv_1 * ofs); + return readb(map->map_priv_1 + ofs); } __u16 rpxlite_read16(struct map_info *map, unsigned long ofs) @@ -85,11 +85,9 @@ int __init init_rpxlite(void) printk("Failed to ioremap\n"); return -EIO; } - mymtd = do_cfi_probe(&rpxlite_map); + mymtd = do_map_probe("cfi", &rpxlite_map); if (mymtd) { -#ifdef MODULE - mymtd->module = &__this_module; -#endif + mymtd->module = THIS_MODULE; add_mtd_device(mymtd); return 0; } diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c new file mode 100644 index 000000000000..b74aa39e0b95 --- /dev/null +++ b/drivers/mtd/maps/sa1100-flash.c @@ -0,0 +1,644 @@ +/* + * Flash memory access on SA11x0 based devices + * + * (C) 2000 Nicolas Pitre + * + * $Id: sa1100-flash.c,v 1.15 2001/06/02 18:29:22 nico Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +#ifndef CONFIG_ARCH_SA1100 +#error This is for SA1100 architecture only +#endif + + +#define WINDOW_ADDR 0xe8000000 + +static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(WINDOW_ADDR + ofs); +} + +static __u16 sa1100_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(WINDOW_ADDR + ofs); +} + +static __u32 sa1100_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(WINDOW_ADDR + ofs); +} + +static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(WINDOW_ADDR + from), len); +} + +static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(WINDOW_ADDR + adr) = d; +} + +static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(WINDOW_ADDR + adr) = d; +} + +static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(WINDOW_ADDR + adr) = d; +} + +static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(WINDOW_ADDR + to), from, len); +} + + +#ifdef CONFIG_SA1100_BITSY + +static void bitsy_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + set_bitsy_egpio(EGPIO_BITSY_VPP_ON); + else + clr_bitsy_egpio(EGPIO_BITSY_VPP_ON); +} + +#endif + +#ifdef CONFIG_SA1100_JORNADA720 + +static void jornada720_set_vpp(int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} + +#endif + +static struct map_info sa1100_map = { + name: "SA1100 flash", + read8: sa1100_read8, + read16: sa1100_read16, + read32: sa1100_read32, + copy_from: sa1100_copy_from, + write8: sa1100_write8, + write16: sa1100_write16, + write32: sa1100_write32, + copy_to: sa1100_copy_to +}; + + +/* + * Here are partition information for all known SA1100-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * + * The *_max_flash_size is the maximum possible mapped flash size which + * is not necessarily the actual flash size. It must correspond to the + * value specified in the mapping definition defined by the + * "struct map_desc *_io_desc" for the corresponding machine. + */ + +#ifdef CONFIG_SA1100_ASSABET + +/* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ +static unsigned long assabet4_max_flash_size = 0x00400000; +static struct mtd_partition assabet4_partitions[] = { + { + name: "bootloader", + size: 0x00020000, + offset: 0, + mask_flags: MTD_WRITEABLE + },{ + name: "bootloader params", + size: 0x00020000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "jffs", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND + } +}; + +/* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ +static unsigned long assabet5_max_flash_size = 0x02000000; +static struct mtd_partition assabet5_partitions[] = { + { + name: "bootloader", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE + },{ + name: "bootloader params", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "jffs", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND + } +}; + +#define assabet_max_flash_size assabet5_max_flash_size +#define assabet_partitions assabet5_partitions + +#endif + +#ifdef CONFIG_SA1100_FLEXANET + +/* Flexanet has two 28F128J3A flash parts in bank 0: */ +static unsigned long flexanet_max_flash_size = 0x02000000; +static struct mtd_partition flexanet_partitions[] = { + { + name: "bootloader", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE + },{ + name: "bootloader params", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "kernel", + size: 0x000C0000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "altkernel", + size: 0x000C0000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "root", + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "free1", + size: 0x00300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "free2", + size: 0x00300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + },{ + name: "free3", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + } +}; + +#endif + +#ifdef CONFIG_SA1100_HUW_WEBPANEL +static unsigned long huw_webpanel_max_flash_size = 0x01000000; +static struct mtd_partition huw_webpanel_partitions[] = { + { + name: "Loader", + size: 0x00040000, + offset: 0, + },{ + name: "Sector 1", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + },{ + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif /* CONFIG_SA1100_HUW_WEBPANEL */ + + +#ifdef CONFIG_SA1100_BITSY + +static unsigned long bitsy_max_flash_size = 0x02000000; +static struct mtd_partition bitsy_partitions[] = { + { + name: "BITSY boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE /* force read-only */ + },{ + name: "BITSY kernel", + size: 0x00080000, + offset: 0x40000 + },{ + name: "BITSY params", + size: 0x00040000, + offset: 0xC0000 + },{ +#ifdef CONFIG_JFFS2_FS + name: "BITSY root jffs2", + offset: 0x00100000, + size: MTDPART_SIZ_FULL +#else + name: "BITSY initrd", + size: 0x00100000, + offset: 0x00100000 + },{ + name: "BITSY root cramfs", + size: 0x00300000, + offset: 0x00200000 + },{ + name: "BITSY usr cramfs", + size: 0x00800000, + offset: 0x00500000 + },{ + name: "BITSY usr local", + offset: 0x00d00000, + size: MTDPART_SIZ_FULL +#endif + } +}; + +#endif +#ifdef CONFIG_SA1100_FREEBIRD +static unsigned long freebird_max_flash_size = 0x02000000; +static struct mtd_partition freebird_partitions[] = { +#if CONFIG_SA1100_FREEBIRD_NEW + { + name: "firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE /* force read-only */ + },{ + name: "kernel", + size: 0x00080000, + offset: 0x40000 + },{ + name: "params", + size: 0x00040000, + offset: 0xC0000 + },{ + name: "initrd", + size: 0x00100000, + offset: 0x00100000 + },{ + name: "root cramfs", + size: 0x00300000, + offset: 0x00200000 + },{ + name: "usr cramfs", + size: 0x00C00000, + offset: 0x00500000 + },{ + name: "local", + offset: 0x01100000, + size: MTDPART_SIZ_FULL + } +#else + { offset: 0, size: 0x00040000, }, + { offset: MTDPART_OFS_APPEND, size: 0x000c0000, }, + { offset: MTDPART_OFS_APPEND, size: 0x00400000, }, + { offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL } +#endif + }; +#endif + + +#ifdef CONFIG_SA1100_CERF + +static unsigned long cerf_max_flash_size = 0x01000000; +static struct mtd_partition cerf_partitions[] = { + { offset: 0, size: 0x00800000 }, + { offset: MTDPART_OFS_APPEND, size: 0x00800000 } +}; + +#endif + +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + +static unsigned long graphicsclient_max_flash_size = 0x01000000; +static struct mtd_partition graphicsclient_partitions[] = { + { + name: "Bootloader + zImage", + offset: 0, + size: 0x100000 + }, + { + name: "ramdisk.gz", + offset: MTDPART_OFS_APPEND, + size: 0x300000 + }, + { + name: "User FS", + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL + } +}; + +#endif + +#ifdef CONFIG_SA1100_LART + +static unsigned long lart_max_flash_size = 0x00400000; +static struct mtd_partition lart_partitions[] = { + { offset: 0, size: 0x020000 }, + { offset: MTDPART_OFS_APPEND, size: 0x0e0000 }, + { offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL } +}; + +#endif + +#ifdef CONFIG_SA1100_PANGOLIN + +static unsigned long pangolin_max_flash_size = 0x04000000; +static struct mtd_partition pangolin_partitions[] = { + { + name: "boot firmware", + offset: 0x00000000, + size: 0x00080000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, + { + name: "kernel", + offset: 0x00080000, + size: 0x00100000, + }, + { + name: "initrd", + offset: 0x00180000, + size: 0x00200000, + }, + { + name: "initrd-test", + offset: 0x00400000, + size: 0x03C00000, + } +}; + +#endif + +#ifdef CONFIG_SA1100_YOPY + +static unsigned long yopy_max_flash_size = 0x08000000; +static struct mtd_partition yopy_partitions[] = { + { + name: "boot firmware", + offset: 0x00000000, + size: 0x00040000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, + { + name: "kernel", + offset: 0x00080000, + size: 0x00080000, + }, + { + name: "initrd", + offset: 0x00100000, + size: 0x00300000, + }, + { + name: "root", + offset: 0x00400000, + size: 0x01000000, + }, +}; + +#endif + +#ifdef CONFIG_SA1100_JORNADA720 + +static unsigned long jornada720_max_flash_size = 0x02000000; +static struct mtd_partition jornada720_partitions[] = { + { + name: "JORNADA720 boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE /* force read-only */ + },{ + name: "JORNADA720 kernel", + size: 0x000c0000, + offset: 0x40000 + },{ + name: "JORNADA720 params", + size: 0x00040000, + offset: 0x100000 + },{ + name: "JORNADA720 initrd", + size: 0x00100000, + offset: 0x00140000 + },{ + name: "JORNADA720 root cramfs", + size: 0x00300000, + offset: 0x00240000 + },{ + name: "JORNADA720 usr cramfs", + size: 0x00800000, + offset: 0x00540000 + },{ + name: "JORNADA720 usr local", + offset: 0x00d00000, + size: 0 /* will expand to the end of the flash */ + } +}; +#endif + +#ifdef CONFIG_SA1100_SHERMAN + +static unsigned long sherman_max_flash_size = 0x02000000; +static struct mtd_partition sherman_partitions[] = { + { offset: 0, size: 0x50000 }, + { offset: MTDPART_OFS_APPEND, size: 0x70000 }, + { offset: MTDPART_OFS_APPEND, size: 0x600000 }, + { offset: MTDPART_OFS_APPEND, size: 0xA0000 } +}; + +#endif + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +int __init sa1100_mtd_init(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; + char *part_type; + + sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; + printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); + mymtd = do_map_probe("cfi", &sa1100_map); + if (!mymtd) + return -ENXIO; + mymtd->module = THIS_MODULE; + + /* + * Static partition definition selection + */ + part_type = "static"; +#ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + parts = assabet_partitions; + nb_parts = NB_OF(assabet_partitions); + sa1100_map.size = assabet_max_flash_size; + } +#endif + +#ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { + parts = huw_webpanel_partitions; + nb_parts = NB_OF(huw_webpanel_partitions); + sa1100_map.size = huw_webpanel_max_flash_size; + } +#endif + +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy()) { + parts = bitsy_partitions; + nb_parts = NB_OF(bitsy_partitions); + sa1100_map.size = bitsy_max_flash_size; + sa1100_map.set_vpp = bitsy_set_vpp; + } +#endif +#ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) { + parts = freebird_partitions; + nb_parts = NB_OF(freebird_partitions); + sa1100_map.size = freebird_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { + parts = cerf_partitions; + nb_parts = NB_OF(cerf_partitions); + sa1100_map.size = cerf_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_GRAPHICSCLIENT + if (machine_is_graphicsclient()) { + parts = graphicsclient_partitions; + nb_parts = NB_OF(graphicsclient_partitions); + sa1100_map.size = graphicsclient_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_LART + if (machine_is_lart()) { + parts = lart_partitions; + nb_parts = NB_OF(lart_partitions); + sa1100_map.size = lart_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { + parts = pangolin_partitions; + nb_parts = NB_OF(pangolin_partitions); + sa1100_map.size = pangolin_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { + parts = jornada720_partitions; + nb_parts = NB_OF(jornada720_partitions); + sa1100_map.size = jornada720_max_flash_size; + sa1100_map.set_vpp = jornada720_set_vpp; + } +#endif +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { + parts = yopy_partitions; + nb_parts = NB_OF(yopy_partitions); + sa1100_map.size = yopy_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_SHERMAN + if (machine_is_sherman()) { + parts = sherman_partitions; + nb_parts = NB_OF(sherman_partitions); + sa1100_map.size = sherman_max_flash_size; + } +#endif +#ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { + parts = flexanet_partitions; + nb_parts = NB_OF(flexanet_partitions); + sa1100_map.size = flexanet_max_flash_size; + } +#endif + + + if (!nb_parts) { + printk(KERN_WARNING "MTD: no known flash definition for this SA1100 machine\n"); + return -ENXIO; + } + + + /* + * Dynamic partition selection stuff (might override the static ones) + */ +#ifdef CONFIG_MTD_SA1100_REDBOOT_PARTITIONS + if (parsed_nr_parts == 0) { + int ret = parse_redboot_partitions(mymtd, &parsed_parts); + + if (ret > 0) { + part_type = "RedBoot"; + parsed_nr_parts = ret; + } + } +#endif +#ifdef CONFIG_MTD_SA1100_BOOTLDR_PARTITIONS + if (parsed_nr_parts == 0) { + int ret = parse_bootldr_partitions(mymtd, &parsed_parts); + if (ret > 0) { + part_type = "Compaq bootldr"; + parsed_nr_parts = ret; + } + } +#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } + + if (nb_parts == 0) { + printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(mymtd); + } else { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + } + return 0; +} + +static void __exit sa1100_mtd_cleanup(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + if (parsed_parts) + kfree(parsed_parts); + } +} + +module_init(sa1100_mtd_init); +module_exit(sa1100_mtd_cleanup); diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c new file mode 100644 index 000000000000..3d428f921f6b --- /dev/null +++ b/drivers/mtd/maps/sbc_gxx.c @@ -0,0 +1,278 @@ +/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX, + SBC-GXm and SBC-GX1 series boards. + + Copyright (C) 2001 Arcom Control System Ltd + + 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 + + $Id: sbc_gxx.c,v 1.17 2001/06/02 14:52:23 dwmw2 Exp $ + +The SBC-MediaGX / SBC-GXx has up to 16 MiB of +Intel StrataFlash (28F320/28F640) in x8 mode. + +This driver uses the CFI probe and Intel Extended Command Set drivers. + +The flash is accessed as follows: + + 16 kbyte memory window at 0xdc000-0xdffff + + Two IO address locations for paging + + 0x258 + bit 0-7: address bit 14-21 + 0x259 + bit 0-1: address bit 22-23 + bit 7: 0 - reset/powered down + 1 - device enabled + +The single flash device is divided into 3 partition which appear as +separate MTD devices. + +25/04/2001 AJL (Arcom) Modified signon strings and partition sizes + (to support bzImages up to 638KiB-ish) +*/ + +// Includes + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Defines + +// - Hardware specific + +#define WINDOW_START 0xdc000 + +/* Number of bits in offset. */ +#define WINDOW_SHIFT 14 +#define WINDOW_LENGTH (1 << WINDOW_SHIFT) + +/* The bits for the offset into the window. */ +#define WINDOW_MASK (WINDOW_LENGTH-1) +#define PAGE_IO 0x258 +#define PAGE_IO_SIZE 2 + +/* bit 7 of 0x259 must be 1 to enable device. */ +#define DEVICE_ENABLE 0x8000 + +// - Flash / Partition sizing + +#define MAX_SIZE_KiB 16384 +#define BOOT_PARTITION_SIZE_KiB 768 +#define DATA_PARTITION_SIZE_KiB 1280 +#define APP_PARTITION_SIZE_KiB 6144 + +// Globals + +static volatile int page_in_window = -1; // Current page in window. +static unsigned long iomapadr; +static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED; + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[]={ + { name: "SBC-GXx flash boot partition", + offset: 0, + size: BOOT_PARTITION_SIZE_KiB*1024 }, + { name: "SBC-GXx flash data partition", + offset: BOOT_PARTITION_SIZE_KiB*1024, + size: (DATA_PARTITION_SIZE_KiB)*1024 }, + { name: "SBC-GXx flash application partition", + offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } +}; + +#define NUM_PARTITIONS 3 + +static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) +{ + unsigned long page = ofs >> WINDOW_SHIFT; + + if( page!=page_in_window ) { + outw( page | DEVICE_ENABLE, PAGE_IO ); + page_in_window = page; + } +} + + +static __u8 sbc_gxx_read8(struct map_info *map, unsigned long ofs) +{ + __u8 ret; + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, ofs); + ret = readb(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); + return ret; +} + +static __u16 sbc_gxx_read16(struct map_info *map, unsigned long ofs) +{ + __u16 ret; + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, ofs); + ret = readw(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); + return ret; +} + +static __u32 sbc_gxx_read32(struct map_info *map, unsigned long ofs) +{ + __u32 ret; + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, ofs); + ret = readl(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); + return ret; +} + +static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(from & WINDOW_MASK); + + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, from); + memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); + spin_unlock(&sbc_gxx_spin); + (__u8*)to += thislen; + from += thislen; + len -= thislen; + } +} + +static void sbc_gxx_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, adr); + writeb(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); +} + +static void sbc_gxx_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, adr); + writew(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); +} + +static void sbc_gxx_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, adr); + writel(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&sbc_gxx_spin); +} + +static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(to & WINDOW_MASK); + + spin_lock(&sbc_gxx_spin); + sbc_gxx_page(map, to); + memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); + spin_unlock(&sbc_gxx_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static struct map_info sbc_gxx_map = { + name: "SBC-GXx flash", + size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ + buswidth: 1, + read8: sbc_gxx_read8, + read16: sbc_gxx_read16, + read32: sbc_gxx_read32, + copy_from: sbc_gxx_copy_from, + write8: sbc_gxx_write8, + write16: sbc_gxx_write16, + write32: sbc_gxx_write32, + copy_to: sbc_gxx_copy_to +}; + +/* MTD device for all of the flash. */ +static struct mtd_info *all_mtd; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_sbc_gxx init_module +#define cleanup_sbc_gxx cleanup_module +#endif + +mod_exit_t cleanup_sbc_gxx(void) +{ + if( all_mtd ) { + del_mtd_partitions( all_mtd ); + map_destroy( all_mtd ); + } + + iounmap((void *)iomapadr); + release_region(PAGE_IO,PAGE_IO_SIZE); +} + +mod_init_t init_sbc_gxx(void) +{ + if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { + printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", + sbc_gxx_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); + return -EAGAIN; + } + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", + sbc_gxx_map.name ); + return -EIO; + } + + request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" ); + + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + sbc_gxx_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, + WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); + + /* Probe for chip. */ + all_mtd = do_map_probe( "cfi", &sbc_gxx_map ); + if( !all_mtd ) { + cleanup_sbc_gxx(); + return -ENXIO; + } + + all_mtd->module=THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); + + return 0; +} + +module_init(init_sbc_gxx); +module_exit(cleanup_sbc_gxx); diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c new file mode 100644 index 000000000000..f02a605df893 --- /dev/null +++ b/drivers/mtd/maps/sc520cdp.c @@ -0,0 +1,348 @@ +/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform + * + * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH + * + * 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 + * + * $Id: sc520cdp.c,v 1.7 2001/06/02 14:52:23 dwmw2 Exp $ + * + * + * The SC520CDP is an evaluation board for the Elan SC520 processor available + * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, + * and up to 512 KiB of 8-bit DIL Flash ROM. + * For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html + */ + +#include +#include +#include +#include +#include +#include + + +/* +** The Embedded Systems BIOS decodes the first FLASH starting at +** 0x8400000. This is a *terrible* place for it because accessing +** the flash at this location causes the A22 address line to be high +** (that's what 0x8400000 binary's ought to be). But this is the highest +** order address line on the raw flash devices themselves!! +** This causes the top HALF of the flash to be accessed first. Beyond +** the physical limits of the flash, the flash chip aliases over (to +** 0x880000 which causes the bottom half to be accessed. This splits the +** flash into two and inverts it! If you then try to access this from another +** program that does NOT do this insanity, then you *will* access the +** first half of the flash, but not find what you expect there. That +** stuff is in the *second* half! Similarly, the address used by the +** BIOS for the second FLASH bank is also quite a bad choice. +** If REPROGRAM_PAR is defined below (the default), then this driver will +** choose more useful addresses for the FLASH banks by reprogramming the +** responsible PARxx registers in the SC520's MMCR region. This will +** cause the settings to be incompatible with the BIOS's settings, which +** shouldn't be a problem since you are running Linux, (i.e. the BIOS is +** not much use anyway). However, if you need to be compatible with +** the BIOS for some reason, just undefine REPROGRAM_PAR. +*/ +#define REPROGRAM_PAR + + + +#ifdef REPROGRAM_PAR + +/* These are the addresses we want.. */ +#define WINDOW_ADDR_0 0x08800000 +#define WINDOW_ADDR_1 0x09000000 +#define WINDOW_ADDR_2 0x09800000 + +/* .. and these are the addresses the BIOS gives us */ +#define WINDOW_ADDR_0_BIOS 0x08400000 +#define WINDOW_ADDR_1_BIOS 0x08c00000 +#define WINDOW_ADDR_2_BIOS 0x09400000 + +#else + +#define WINDOW_ADDR_0 0x08400000 +#define WINDOW_ADDR_1 0x08C00000 +#define WINDOW_ADDR_2 0x09400000 + +#endif + +#define WINDOW_SIZE_0 0x00800000 +#define WINDOW_SIZE_1 0x00800000 +#define WINDOW_SIZE_2 0x00080000 + +static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +} + +static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio((void *)(map->map_priv_1 + to), from, len); +} + +static struct map_info sc520cdp_map[] = { + { + name: "SC520CDP Flash Bank #0", + size: WINDOW_SIZE_0, + buswidth: 4, + read8: sc520cdp_read8, + read16: sc520cdp_read16, + read32: sc520cdp_read32, + copy_from: sc520cdp_copy_from, + write8: sc520cdp_write8, + write16: sc520cdp_write16, + write32: sc520cdp_write32, + copy_to: sc520cdp_copy_to, + map_priv_2: WINDOW_ADDR_0 + }, + { + name: "SC520CDP Flash Bank #1", + size: WINDOW_SIZE_1, + buswidth: 4, + read8: sc520cdp_read8, + read16: sc520cdp_read16, + read32: sc520cdp_read32, + copy_from: sc520cdp_copy_from, + write8: sc520cdp_write8, + write16: sc520cdp_write16, + write32: sc520cdp_write32, + copy_to: sc520cdp_copy_to, + map_priv_2: WINDOW_ADDR_1 + }, + { + name: "SC520CDP DIL Flash", + size: WINDOW_SIZE_2, + buswidth: 1, + read8: sc520cdp_read8, + read16: sc520cdp_read16, + read32: sc520cdp_read32, + copy_from: sc520cdp_copy_from, + write8: sc520cdp_write8, + write16: sc520cdp_write16, + write32: sc520cdp_write32, + copy_to: sc520cdp_copy_to, + map_priv_2: WINDOW_ADDR_2 + }, +}; + +#define NUM_FLASH_BANKS (sizeof(sc520cdp_map)/sizeof(struct map_info)) + +static struct mtd_info *mymtd[NUM_FLASH_BANKS]; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_sc520cdp init_module +#define cleanup_sc520cdp cleanup_module +#endif + +#ifdef REPROGRAM_PAR + +/* +** The SC520 MMCR (memory mapped control register) region resides +** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers +** are at offset 0x88 in the MMCR: +*/ +#define SC520_MMCR_BASE 0xFFFEF000 +#define SC520_MMCR_EXTENT 0x1000 +#define SC520_PAR(x) ((0x88/sizeof(unsigned long)) + (x)) +#define NUM_SC520_PAR 16 /* total number of PAR registers */ + +/* +** The highest three bits in a PAR register determine what target +** device is controlled by this PAR. Here, only ROMCS? and BOOTCS +** devices are of interest. +*/ +#define SC520_PAR_BOOTCS (0x4<<29) +#define SC520_PAR_ROMCS0 (0x5<<29) +#define SC520_PAR_ROMCS1 (0x6<<29) +#define SC520_PAR_TRGDEV (0x7<<29) + +/* +** Bits 28 thru 26 determine some attributes for the +** region controlled by the PAR. (We only use non-cacheable) +*/ +#define SC520_PAR_WRPROT (1<<26) /* write protected */ +#define SC520_PAR_NOCACHE (1<<27) /* non-cacheable */ +#define SC520_PAR_NOEXEC (1<<28) /* code execution denied */ + + +/* +** Bit 25 determines the granularity: 4K or 64K +*/ +#define SC520_PAR_PG_SIZ4 (0<<25) +#define SC520_PAR_PG_SIZ64 (1<<25) + +/* +** Build a value to be written into a PAR register. +** We only need ROM entries, 64K page size: +*/ +#define SC520_PAR_ENTRY(trgdev, address, size) \ + ((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \ + (address) >> 16 | (((size) >> 16) - 1) << 14) + +struct sc520_par_table +{ + unsigned long trgdev; + unsigned long new_par; + unsigned long default_address; +}; + +static struct sc520_par_table par_table[NUM_FLASH_BANKS] = +{ + { /* Flash Bank #0: selected by ROMCS0 */ + SC520_PAR_ROMCS0, + SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0), + WINDOW_ADDR_0_BIOS + }, + { /* Flash Bank #1: selected by ROMCS1 */ + SC520_PAR_ROMCS1, + SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1), + WINDOW_ADDR_1_BIOS + }, + { /* DIL (BIOS) Flash: selected by BOOTCS */ + SC520_PAR_BOOTCS, + SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2), + WINDOW_ADDR_2_BIOS + } +}; + + +static void sc520cdp_setup_par(void) +{ + volatile unsigned long *mmcr; + unsigned long mmcr_val; + int i, j; + + /* map in SC520's MMCR area */ + mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); + if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */ + /* force map_priv_2 fields to BIOS defaults: */ + for(i = 0; i < NUM_FLASH_BANKS; i++) + sc520cdp_map[i].map_priv_2 = par_table[i].default_address; + return; + } + + /* + ** Find the PARxx registers that are reponsible for activating + ** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a + ** new value from the table. + */ + for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */ + for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */ + mmcr_val = mmcr[SC520_PAR(j)]; + /* if target device field matches, reprogram the PAR */ + if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev) + { + mmcr[SC520_PAR(j)] = par_table[i].new_par; + break; + } + } + if(j == NUM_SC520_PAR) + { /* no matching PAR found: try default BIOS address */ + printk(KERN_NOTICE "Could not find PAR responsible for %s\n", + sc520cdp_map[i].name); + printk(KERN_NOTICE "Trying default address 0x%lx\n", + par_table[i].default_address); + sc520cdp_map[i].map_priv_2 = par_table[i].default_address; + } + } + iounmap((void *)mmcr); +} +#endif + + +static int __init init_sc520cdp(void) +{ + int i, devices_found = 0; + +#ifdef REPROGRAM_PAR + /* reprogram PAR registers so flash appears at the desired addresses */ + sc520cdp_setup_par(); +#endif + + for (i = 0; i < NUM_FLASH_BANKS; i++) { + printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2); + sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size); + + if (!sc520cdp_map[i].map_priv_1) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } + mymtd[i] = do_map_probe("cfi", &sc520cdp_map[i]); + if(!mymtd[i]) + mymtd[i] = do_map_probe("jedec", &sc520cdp_map[i]); + if(!mymtd[i]) + mymtd[i] = do_map_probe("rom", &sc520cdp_map[i]); + + if (mymtd[i]) { + mymtd[i]->module = THIS_MODULE; + add_mtd_device(mymtd[i]); + ++devices_found; + } + else { + iounmap((void *)sc520cdp_map[i].map_priv_1); + } + } + return(devices_found ? 0 : -ENXIO); +} + +static void __exit cleanup_sc520cdp(void) +{ + int i; + + for (i = 0; i < NUM_FLASH_BANKS; i++) { + if (mymtd[i]) { + del_mtd_device(mymtd[i]); + map_destroy(mymtd[i]); + } + if (sc520cdp_map[i].map_priv_1) { + iounmap((void *)sc520cdp_map[i].map_priv_1); + sc520cdp_map[i].map_priv_1 = 0; + } + } +} + +module_init(init_sc520cdp); +module_exit(cleanup_sc520cdp); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c new file mode 100644 index 000000000000..5106b4edf036 --- /dev/null +++ b/drivers/mtd/maps/sun_uflash.c @@ -0,0 +1,224 @@ +/* $Id: sun_uflash.c,v 1.2 2001/04/26 15:40:23 dwmw2 Exp $ + * + * sun_uflash - Driver implementation for user-programmable flash + * present on many Sun Microsystems SME boardsets. + * + * This driver does NOT provide access to the OBP-flash for + * safety reasons-- use /drivers/sbus/char/flash.c instead. + * + * Copyright (c) 2001 Eric Brower (ebrower@usa.net) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define UFLASH_OBPNAME "flashprom" +#define UFLASH_DEVNAME "userflash" + +#define UFLASH_WINDOW_SIZE 0x200000 +#define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ + +MODULE_AUTHOR + ("Eric Brower "); +MODULE_DESCRIPTION + ("User-programmable flash device on Sun Microsystems boardsets"); +MODULE_SUPPORTED_DEVICE + ("userflash"); + +static LIST_HEAD(device_list); +struct uflash_dev { + char * name; /* device name */ + struct map_info map; /* mtd map info */ + struct mtd_info * mtd; /* mtd info */ + struct list_head list; +}; + +__u8 uflash_read8(struct map_info *map, unsigned long ofs) +{ + return(__raw_readb(map->map_priv_1 + ofs)); +} + +__u16 uflash_read16(struct map_info *map, unsigned long ofs) +{ + return(__raw_readw(map->map_priv_1 + ofs)); +} + +__u32 uflash_read32(struct map_info *map, unsigned long ofs) +{ + return(__raw_readl(map->map_priv_1 + ofs)); +} + +void uflash_copy_from(struct map_info *map, void *to, unsigned long from, + ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void uflash_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); +} + +void uflash_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); +} + +void uflash_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); +} + +void uflash_copy_to(struct map_info *map, unsigned long to, const void *from, + ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info uflash_map_templ = { + name: "SUNW,???-????", + size: UFLASH_WINDOW_SIZE, + buswidth: UFLASH_BUSWIDTH, + read8: uflash_read8, + read16: uflash_read16, + read32: uflash_read32, + copy_from: uflash_copy_from, + write8: uflash_write8, + write16: uflash_write16, + write32: uflash_write32, + copy_to: uflash_copy_to +}; + +int uflash_devinit(struct linux_ebus_device* edev) +{ + int iTmp, nregs; + struct linux_prom_registers regs[2]; + struct uflash_dev *pdev; + + iTmp = prom_getproperty( + edev->prom_node, "reg", (void *)regs, sizeof(regs)); + if ((iTmp % sizeof(regs[0])) != 0) { + printk("%s: Strange reg property size %d\n", + UFLASH_DEVNAME, iTmp); + return -ENODEV; + } + + nregs = iTmp / sizeof(regs[0]); + + if (nregs != 1) { + /* Non-CFI userflash device-- once I find one we + * can work on supporting it. + */ + printk("%s: unsupported device at 0x%lx (%d regs): " \ + "email ebrower@usa.net\n", + UFLASH_DEVNAME, edev->resource[0].start, nregs); + return -ENODEV; + } + + if(0 == (pdev = kmalloc(sizeof(struct uflash_dev), GFP_KERNEL))) { + printk("%s: unable to kmalloc new device\n", UFLASH_DEVNAME); + return(-ENOMEM); + } + + /* copy defaults and tweak parameters */ + memcpy(&pdev->map, &uflash_map_templ, sizeof(uflash_map_templ)); + pdev->map.size = regs[0].reg_size; + + iTmp = prom_getproplen(edev->prom_node, "model"); + pdev->name = kmalloc(iTmp, GFP_KERNEL); + prom_getstring(edev->prom_node, "model", pdev->name, iTmp); + if(0 != pdev->name && 0 < strlen(pdev->name)) { + pdev->map.name = pdev->name; + } + + pdev->map.map_priv_1 = + (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size); + if(0 == pdev->map.map_priv_1) { + printk("%s: failed to map device\n", __FUNCTION__); + kfree(pdev->name); + kfree(pdev); + return(-1); + } + + /* MTD registration */ + pdev->mtd = do_map_probe("cfi", &pdev->map); + if(0 == pdev->mtd) { + iounmap((void *)pdev->map.map_priv_1); + kfree(pdev->name); + kfree(pdev); + return(-ENXIO); + } + + list_add(&pdev->list, &device_list); + + pdev->mtd->module = THIS_MODULE; + + add_mtd_device(pdev->mtd); + return(0); +} + +static int __init uflash_init(void) +{ + struct linux_ebus *ebus = NULL; + struct linux_ebus_device *edev = NULL; + + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if (!strcmp(edev->prom_name, UFLASH_OBPNAME)) { + if(0 > prom_getproplen(edev->prom_node, "user")) { + DEBUG(2, "%s: ignoring device at 0x%lx\n", + UFLASH_DEVNAME, edev->resource[0].start); + } else { + uflash_devinit(edev); + } + } + } + } + + if(list_empty(&device_list)) { + printk("%s: unable to locate device\n", UFLASH_DEVNAME); + return -ENODEV; + } + return(0); +} + +static void __exit uflash_cleanup(void) +{ + struct list_head *udevlist; + struct uflash_dev *udev; + + list_for_each(udevlist, &device_list) { + udev = list_entry(udevlist, struct uflash_dev, list); + DEBUG(2, "%s: removing device %s\n", + UFLASH_DEVNAME, udev->name); + + if(0 != udev->mtd) { + del_mtd_device(udev->mtd); + map_destroy(udev->mtd); + } + if(0 != udev->map.map_priv_1) { + iounmap((void*)udev->map.map_priv_1); + udev->map.map_priv_1 = 0; + } + if(0 != udev->name) { + kfree(udev->name); + } + kfree(udev); + } +} + +module_init(uflash_init); +module_exit(uflash_cleanup); diff --git a/drivers/mtd/vmax301.c b/drivers/mtd/maps/vmax301.c similarity index 94% rename from drivers/mtd/vmax301.c rename to drivers/mtd/maps/vmax301.c index cab69b451869..4721aa3a50d2 100644 --- a/drivers/mtd/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.15 2000/11/27 08:50:22 dwmw2 Exp $ +// $Id: vmax301.c,v 1.22 2001/06/02 14:30:44 dwmw2 Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -215,13 +215,13 @@ int __init init_vmax301(void) vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START); for (i=0; i<2; i++) { - vmax_mtd[i] = do_cfi_probe(&vmax_map[i]); + vmax_mtd[i] = do_map_probe("cfi", &vmax_map[i]); if (!vmax_mtd[i]) - vmax_mtd[i] = do_jedec_probe(&vmax_map[i]); + vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]); if (!vmax_mtd[i]) - vmax_mtd[i] = do_ram_probe(&vmax_map[i]); + vmax_mtd[i] = do_map_probe("ram", &vmax_map[i]); if (!vmax_mtd[i]) - vmax_mtd[i] = do_rom_probe(&vmax_map[i]); + vmax_mtd[i] = do_map_probe("rom", &vmax_map[i]); if (vmax_mtd[i]) { vmax_mtd[i]->module = THIS_MODULE; add_mtd_device(vmax_mtd[i]); @@ -229,7 +229,7 @@ int __init init_vmax301(void) } if (!vmax_mtd[1] && !vmax_mtd[2]) { - iounmap(iomapadr); + iounmap((void *)iomapadr); return -ENXIO; } diff --git a/drivers/mtd/mixmem.c b/drivers/mtd/mixmem.c deleted file mode 100644 index 18fc4e2d7a70..000000000000 --- a/drivers/mtd/mixmem.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * mixmem - a block device driver for flash rom found on the - * piggyback board of the multi-purpose mixcom card - * - * Author: Gergely Madarasz - * - * Copyright (c) 1999 ITConsult-Pro Co. - * - * This code is GPL - * - */ - -#include -#include -#include -#include -#include -#include - -#define MIXCOM_ID_OFFSET 0xc10 -#define MIXCOM_PAGE_OFFSET 0xc11 -#define MIXCOM_ID_1 0x11 -#define MIXCOM_ID_2 0x13 -#define MIXMEM_PAGESIZE 4096 -#define FIRST_BLOCK_OFFSET 0x1000 - -#if LINUX_VERSION_CODE < 0x20300 -#define __exit -#endif - -static unsigned int mixmem_addrs[] = { 0xc8000, 0xd8000, 0 }; -static unsigned int mixcom_ports[] = { 0x180, 0x280, 0x380, 0 }; - -// We could store these in the mtd structure, but we only support 1 device.. -static unsigned long base_io = 0; -static unsigned long base_addr = 0; -static struct mapped_mtd_info *SSD; - -static unsigned long mixmem_page(struct mapped_mtd_info *map, - unsigned long page) -{ - outb((char)(page & 0xff), base_io+MIXCOM_PAGE_OFFSET); - outb((char)((page >> 8) & 0x7), base_io+MIXCOM_PAGE_OFFSET+1); - return base_addr; -} - -static int flash_probe(int base) -{ - int prev,curr; - unsigned long flags; - - writeb(0xf0, base); - save_flags(flags); cli(); - - prev=readw(base); - - writeb(0xaa, base+0x555); - writeb(0x55, base+0x2AA); - writeb(0x90, base+0x555); - - curr=readw(base); - - restore_flags(flags); - writeb(0xf0, base); - return(prev==curr?0:curr); -} - -static int mixmem_probe(void) -{ - int i; - int id; - int chip; - - /* This should really check to see if the io ports are in use before - writing to them */ - for(i=0;mixcom_ports[i]!=0;i++) { - id=inb(mixcom_ports[i]+MIXCOM_ID_OFFSET); - if(id==MIXCOM_ID_1 || id==MIXCOM_ID_2) { - printk("mixmem: mixcom board found at 0x%3x\n",mixcom_ports[i]); - break; - } - } - - if(mixcom_ports[i]==0) { - printk("mixmem: no mixcom board found\n"); - return -ENODEV; - } - - if (check_region(mixcom_ports[i]+MIXCOM_PAGE_OFFSET, 2)) return -EAGAIN; - - - - // What is the deal with first_block_offset? - for(i=0;mixmem_addrs[i]!=0;i++) { - chip=flash_probe(mixmem_addrs[i]+FIRST_BLOCK_OFFSET); - if(chip)break; - } - - if(mixmem_addrs[i]==0) { - printk("mixmem: no flash available\n"); - return -ENODEV; - } - base_io = mixcom_ports[i]; - base_addr = mixmem_addrs[i]; - request_region(mixcom_ports[i]+MIXCOM_PAGE_OFFSET, 2, "mixmem"); - return 0; -} - - -static void __exit cleanup_mixmem() -{ - mtd_mapped_remove(SSD); - kfree(SSD); - SSD = 0; - release_region(base_io+MIXCOM_PAGE_OFFSET, 2); -} - -//static int __init init_mixmem(void) -int __init init_mixmem(void) -{ - if (mixmem_probe() != 0) - return -EAGAIN; - - // Print out our little header.. - printk("mixcom MTD IO:0x%lx MEM:0x%lx-0x%lx\n",base_io,base_addr, - base_addr+MIXMEM_PAGESIZE); - - // Allocate some memory - SSD = (struct mapped_mtd_info *)kmalloc(sizeof(*SSD),GFP_KERNEL); - if (SSD == 0) - return 0; - memset(SSD,0,sizeof(*SSD)); - - // Setup the MTD structure - SSD->page = mixmem_page; - SSD->pagesize = MIXMEM_PAGESIZE; - SSD->maxsize = 0x7FF; - SSD->mtd.name = "mixcom piggyback"; - - // Setup the MTD, this will sense the flash parameters and so on.. - if (mtd_mapped_setup(SSD) != 0) - { - printk("Failed to register new device\n"); - cleanup_module(); - return -EAGAIN; - } - - return 0; -} -module_init(init_mixmem); -module_exit(cleanup_mixmem); diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c new file mode 100644 index 000000000000..1905bd29e794 --- /dev/null +++ b/drivers/mtd/mtdblock_ro.c @@ -0,0 +1,289 @@ +/* + * $Id: mtdblock_ro.c,v 1.5 2001/06/10 01:41:53 dwmw2 Exp $ + * + * Read-only version of the mtdblock device, without the + * read/erase/modify/writeback stuff + */ + +#ifdef MTDBLOCK_DEBUG +#define DEBUGLVL debug +#endif + + +#include +#include + +#include + +#define MAJOR_NR MTD_BLOCK_MAJOR +#define DEVICE_NAME "mtdblock" +#define DEVICE_REQUEST mtdblock_request +#define DEVICE_NR(device) (device) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) +#define DEVICE_NO_RANDOM +#include + +#if LINUX_VERSION_CODE < 0x20300 +#define RQFUNC_ARG void +#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +#else +#define RQFUNC_ARG request_queue_t *q +#endif + +#ifdef MTDBLOCK_DEBUG +static int debug = MTDBLOCK_DEBUG; +MODULE_PARM(debug, "i"); +#endif + + +static int mtd_sizes[MAX_MTD_DEVICES]; + + +static int mtdblock_open(struct inode *inode, struct file *file) +{ + struct mtd_info *mtd = NULL; + + int dev; + + DEBUG(1,"mtdblock_open\n"); + + if (inode == 0) + return -EINVAL; + + dev = MINOR(inode->i_rdev); + + MOD_INC_USE_COUNT; + + mtd = get_mtd_device(NULL, dev); + + if (!mtd) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + mtd_sizes[dev] = mtd->size>>9; + + DEBUG(1, "ok\n"); + + return 0; +} + +static release_t mtdblock_release(struct inode *inode, struct file *file) +{ + int dev; + struct mtd_info *mtd; + + DEBUG(1, "mtdblock_release\n"); + + if (inode == NULL) + release_return(-ENODEV); + + invalidate_device(inode->i_rdev, 1); + + dev = MINOR(inode->i_rdev); + mtd = __get_mtd_device(NULL, dev); + + if (!mtd) { + printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); + MOD_DEC_USE_COUNT; + release_return(-ENODEV); + } + + if (mtd->sync) + mtd->sync(mtd); + + put_mtd_device(mtd); + + DEBUG(1, "ok\n"); + + MOD_DEC_USE_COUNT; + release_return(0); +} + + +static void mtdblock_request(RQFUNC_ARG) +{ + struct request *current_request; + unsigned int res = 0; + struct mtd_info *mtd; + + while (1) + { + /* Grab the Request and unlink it from the request list, INIT_REQUEST + will execute a return if we are done. */ + INIT_REQUEST; + current_request = CURRENT; + + if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) + { + printk("mtd: Unsupported device!\n"); + end_request(0); + continue; + } + + // Grab our MTD structure + + mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); + if (!mtd) { + printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); + end_request(0); + } + + if (current_request->sector << 9 > mtd->size || + (current_request->sector + current_request->nr_sectors) << 9 > mtd->size) + { + printk("mtd: Attempt to read past end of device!\n"); + printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors); + end_request(0); + continue; + } + + /* Remove the request we are handling from the request list so nobody messes + with it */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + /* Now drop the lock that the ll_rw_blk functions grabbed for us + and process the request. This is necessary due to the extreme time + we spend processing it. */ + spin_unlock_irq(&io_request_lock); +#endif + + // Handle the request + switch (current_request->cmd) + { + size_t retlen; + + case READ: + if (MTD_READ(mtd,current_request->sector<<9, + current_request->nr_sectors << 9, + &retlen, current_request->buffer) == 0) + res = 1; + else + res = 0; + break; + + case WRITE: + + /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, + current_request->nr_sectors); + */ + + // Read only device + if ((mtd->flags & MTD_CAP_RAM) == 0) + { + res = 0; + break; + } + + // Do the write + if (MTD_WRITE(mtd,current_request->sector<<9, + current_request->nr_sectors << 9, + &retlen, current_request->buffer) == 0) + res = 1; + else + res = 0; + break; + + // Shouldn't happen + default: + printk("mtd: unknown request\n"); + break; + } + + // Grab the lock and re-thread the item onto the linked list +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + spin_lock_irq(&io_request_lock); +#endif + end_request(res); + } +} + + + +static int mtdblock_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct mtd_info *mtd; + + mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); + + if (!mtd) return -EINVAL; + + switch (cmd) { + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EFAULT; + return Put_user((mtd->size >> 9), + (long *) arg); + + case BLKFLSBUF: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if(!capable(CAP_SYS_ADMIN)) return -EACCES; +#endif + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + if (mtd->sync) + mtd->sync(mtd); + return 0; + + default: + return -ENOTTY; + } +} + +#if LINUX_VERSION_CODE < 0x20326 +static struct file_operations mtd_fops = +{ + open: mtdblock_open, + ioctl: mtdblock_ioctl, + release: mtdblock_release, + read: block_read, + write: block_write +}; +#else +static struct block_device_operations mtd_fops = +{ + open: mtdblock_open, + release: mtdblock_release, + ioctl: mtdblock_ioctl +}; +#endif + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_mtdblock init_module +#define cleanup_mtdblock cleanup_module +#endif + +int __init init_mtdblock(void) +{ + int i; + + if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_BLOCK_MAJOR); + return EAGAIN; + } + + /* We fill it in at open() time. */ + for (i=0; i< MAX_MTD_DEVICES; i++) { + mtd_sizes[i] = 0; + } + + /* Allow the block size to default to BLOCK_SIZE. */ + blksize_size[MAJOR_NR] = NULL; + blk_size[MAJOR_NR] = mtd_sizes; + +#if LINUX_VERSION_CODE < 0x20320 + blk_dev[MAJOR_NR].request_fn = mtdblock_request; +#else + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +#endif + return 0; +} + +static void __exit cleanup_mtdblock(void) +{ + unregister_blkdev(MAJOR_NR,DEVICE_NAME); +} + +module_init(init_mtdblock); +module_exit(cleanup_mtdblock); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 7558ce18063e..9f506a1b88ed 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.21.2.3 2001/01/09 00:18:31 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.38.2.1 2001/06/09 17:31:16 dwmw2 Exp $ * * Character-device access to raw MTD devices. * @@ -7,7 +7,6 @@ #include - #include #include #include @@ -18,21 +17,18 @@ #include static void mtd_notify_add(struct mtd_info* mtd); static void mtd_notify_remove(struct mtd_info* mtd); + static struct mtd_notifier notifier = { - mtd_notify_add, - mtd_notify_remove, - NULL + add: mtd_notify_add, + remove: mtd_notify_remove, }; -static devfs_handle_t devfs_dir_handle = NULL; + +static devfs_handle_t devfs_dir_handle; static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES]; #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) -#else -static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int orig) -#endif { struct mtd_info *mtd=(struct mtd_info *)file->private_data; @@ -119,15 +115,18 @@ static release_t mtd_close(struct inode *inode, #define FILE_POS file->f_pos #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +/* FIXME: This _really_ needs to die. In 2.5, we should lock the + userspace buffer down and use it directly with readv/writev. +*/ +#define MAX_KMALLOC_SIZE 0x20000 + static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) -#else -static int mtd_read(struct inode *inode,struct file *file, char *buf, int count) -#endif { struct mtd_info *mtd = (struct mtd_info *)file->private_data; size_t retlen=0; + size_t total_retlen=0; int ret=0; + int len; char *kbuf; DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); @@ -138,39 +137,50 @@ static int mtd_read(struct inode *inode,struct file *file, char *buf, int count) if (!count) return 0; - /* FIXME: Use kiovec in 2.3 or 2.2+rawio, or at - * least split the IO into smaller chunks. - */ - - kbuf = vmalloc(count); - if (!kbuf) - return -ENOMEM; - - ret = MTD_READ(mtd, FILE_POS, count, &retlen, kbuf); - if (!ret) { - FILE_POS += retlen; - if (copy_to_user(buf, kbuf, retlen)) - ret = -EFAULT; + /* FIXME: Use kiovec in 2.5 to lock down the user's buffers + and pass them directly to the MTD functions */ + while (count) { + if (count > MAX_KMALLOC_SIZE) + len = MAX_KMALLOC_SIZE; else - ret = retlen; + len = count; + + kbuf=kmalloc(len,GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + ret = MTD_READ(mtd, FILE_POS, len, &retlen, kbuf); + if (!ret) { + FILE_POS += retlen; + if (copy_to_user(buf, kbuf, retlen)) { + kfree(kbuf); + return -EFAULT; + } + else + total_retlen += retlen; + count -= retlen; + buf += retlen; + } + else { + kfree(kbuf); + return ret; + } + + kfree(kbuf); } - vfree(kbuf); - - return ret; + return total_retlen; } /* mtd_read */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos) -#else -static read_write_t mtd_write(struct inode *inode,struct file *file, const char *buf, count_t count) -#endif { struct mtd_info *mtd = (struct mtd_info *)file->private_data; char *kbuf; size_t retlen; + size_t total_retlen=0; int ret=0; + int len; DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); @@ -183,27 +193,39 @@ static read_write_t mtd_write(struct inode *inode,struct file *file, const char if (!count) return 0; - kbuf=vmalloc(count); + while (count) { + if (count > MAX_KMALLOC_SIZE) + len = MAX_KMALLOC_SIZE; + else + len = count; - if (!kbuf) - return -ENOMEM; - - if (copy_from_user(kbuf, buf, count)) { - vfree(kbuf); - return -EFAULT; - } - + kbuf=kmalloc(len,GFP_KERNEL); + if (!kbuf) { + printk("kmalloc is null\n"); + return -ENOMEM; + } - ret = (*(mtd->write))(mtd, FILE_POS, count, &retlen, buf); + if (copy_from_user(kbuf, buf, len)) { + kfree(kbuf); + return -EFAULT; + } - if (!ret) { - FILE_POS += retlen; - ret = retlen; + ret = (*(mtd->write))(mtd, FILE_POS, len, &retlen, kbuf); + if (!ret) { + FILE_POS += retlen; + total_retlen += retlen; + count -= retlen; + buf += retlen; + } + else { + kfree(kbuf); + return ret; + } + + kfree(kbuf); } - vfree(kbuf); - - return ret; + return total_retlen; } /* mtd_write */ /*====================================================================== @@ -236,6 +258,30 @@ static int mtd_ioctl(struct inode *inode, struct file *file, } switch (cmd) { + case MEMGETREGIONCOUNT: + if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int))) + return -EFAULT; + break; + + case MEMGETREGIONINFO: + { + struct region_info_user ur; + + if (copy_from_user( &ur, + (struct region_info_user *)arg, + sizeof(struct region_info_user))) { + return -EFAULT; + } + + if (ur.regionindex >= mtd->numeraseregions) + return -EINVAL; + if (copy_to_user((struct mtd_erase_region_info *) arg, + &(mtd->eraseregions[ur.regionindex]), + sizeof(struct mtd_erase_region_info))) + return -EFAULT; + break; + } + case MEMGETINFO: if (copy_to_user((struct mtd_info *)arg, mtd, sizeof(struct mtd_info_user))) @@ -317,7 +363,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t))) + if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ret = -EFAULT; kfree(databuf); @@ -351,7 +397,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t))) + if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ret = -EFAULT; else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) ret = -EFAULT; @@ -371,6 +417,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = -EOPNOTSUPP; else ret = mtd->lock(mtd, adrs[0], adrs[1]); + break; } case MEMUNLOCK: @@ -384,14 +431,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = -EOPNOTSUPP; else ret = mtd->unlock(mtd, adrs[0], adrs[1]); + break; } default: - printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO); - ret = -EINVAL; + DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); + ret = -ENOTTY; } - + return ret; } /* memory_ioctl */ diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index df58ba446c3c..e91febf49e51 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.27 2000/12/10 01:10:09 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.30 2001/06/02 14:30:42 dwmw2 Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -258,8 +258,8 @@ static inline int mtd_proc_info (char *buf, int i) if (!this) return 0; - return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size, - this->name); + return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, + this->erasesize, this->name); } static int mtd_read_proc ( char *page, char **start, off_t off,int count @@ -270,11 +270,12 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count #endif ) { - int len = 0, l, i; + int len, l, i; off_t begin = 0; down(&mtd_table_mutex); + len = sprintf(page, "dev: size erasesize name\n"); for (i=0; i< MAX_MTD_DEVICES; i++) { l = mtd_proc_info(page + len, i); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 7101f7b2bdae..3fda277cb454 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.7 2000/12/09 23:29:47 dwmw2 Exp $ + * $Id: mtdpart.c,v 1.21 2001/06/09 16:33:32 dwmw2 Exp $ */ #include @@ -25,7 +25,7 @@ static LIST_HEAD(mtd_partitions); struct mtd_part { struct mtd_info mtd; struct mtd_info *master; - loff_t offset; + u_int32_t offset; int index; struct list_head list; }; @@ -100,15 +100,36 @@ static int part_erase (struct mtd_info *mtd, struct erase_info *instr) static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_part *part = PART(mtd); + if ((len + ofs) > mtd->size) + return -EINVAL; return part->master->lock(part->master, ofs + part->offset, len); } static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_part *part = PART(mtd); + if ((len + ofs) > mtd->size) + return -EINVAL; return part->master->unlock(part->master, ofs + part->offset, len); } +static void part_sync(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + part->master->sync(part->master); +} + +static int part_suspend(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + return part->master->suspend(part->master); +} + +static void part_resume(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + part->master->resume(part->master); +} /* * This function unregisters and destroy all slave MTD objects which are @@ -130,14 +151,12 @@ int del_mtd_partitions(struct mtd_info *master) del_mtd_device(&slave->mtd); kfree(slave); node = prev; - MOD_DEC_USE_COUNT; } } return 0; } - /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -150,10 +169,13 @@ int add_mtd_partitions(struct mtd_info *master, int nbparts) { struct mtd_part *slave; - u_long cur_offset = 0; + u_int32_t cur_offset = 0; int i; + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + for (i = 0; i < nbparts; i++) { + /* allocate the partition structure */ slave = kmalloc (sizeof(*slave), GFP_KERNEL); if (!slave) { @@ -162,63 +184,102 @@ int add_mtd_partitions(struct mtd_info *master, del_mtd_partitions(master); return -ENOMEM; } + memset(slave, 0, sizeof(*slave)); list_add(&slave->list, &mtd_partitions); /* set up the MTD object for this partition */ - slave->mtd = *master; - slave->mtd.name = parts[i].name; + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~parts[i].mask_flags; slave->mtd.size = parts[i].size; - slave->mtd.flags &= ~parts[i].mask_flags; + slave->mtd.oobblock = master->oobblock; + slave->mtd.oobsize = master->oobsize; + slave->mtd.ecctype = master->ecctype; + slave->mtd.eccsize = master->eccsize; + + slave->mtd.name = parts[i].name; + slave->mtd.bank_size = master->bank_size; + + slave->mtd.module = master->module; + slave->mtd.read = part_read; slave->mtd.write = part_write; - if (slave->mtd.writev) + slave->mtd.sync = part_sync; + if (!i && master->suspend && master->resume) { + slave->mtd.suspend = part_suspend; + slave->mtd.resume = part_resume; + } + + if (master->writev) slave->mtd.writev = part_writev; - if (slave->mtd.readv) + if (master->readv) slave->mtd.readv = part_readv; - if (slave->mtd.lock) + if (master->lock) slave->mtd.lock = part_lock; - if (slave->mtd.unlock) + if (master->unlock) slave->mtd.unlock = part_unlock; slave->mtd.erase = part_erase; slave->master = master; slave->offset = parts[i].offset; slave->index = i; - if (slave->offset == 0) + if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; - if (slave->mtd.size == 0) + if (slave->mtd.size == MTDPART_SIZ_FULL) 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, + slave->offset + slave->mtd.size, slave->mtd.name); /* let's do some sanity checks */ + if (slave->offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk ("mtd: partition \"%s\" is out of reach -- disabled\n", + parts[i].name); + } + 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", + parts[i].name, master->name, slave->mtd.size); + } + if (master->numeraseregions>1) { + /* Deal with variable erase size stuff */ + int i; + struct mtd_erase_region_info *regions = master->eraseregions; + + /* Find the first erase regions which is part of this partition. */ + for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) + ; + + for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { + if (slave->mtd.erasesize < regions[i].erasesize) { + slave->mtd.erasesize = regions[i].erasesize; + } + } + } else { + /* Single erase size */ + slave->mtd.erasesize = master->erasesize; + } + if ((slave->mtd.flags & MTD_WRITEABLE) && - (parts[i].offset % master->erasesize)) { + (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; printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - parts[i].name); + parts[i].name); } if ((slave->mtd.flags & MTD_WRITEABLE) && - (parts[i].size % master->erasesize)) { + (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); - } - if (parts[i].offset >= master->size) { - /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; - printk ("mtd: partition \"%s\" is out of reach -- disabled\n", - parts[i].name); - } - if (parts[i].offset + parts[i].size > master->size) { - slave->mtd.size = master->size - parts[i].offset; - printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#lx\n", - parts[i].name, master->name, slave->mtd.size); + parts[i].name); } /* register our partition */ add_mtd_device(&slave->mtd); - MOD_INC_USE_COUNT; } return 0; diff --git a/drivers/mtd/nand/Config.in b/drivers/mtd/nand/Config.in new file mode 100644 index 000000000000..b28fdb8b63b9 --- /dev/null +++ b/drivers/mtd/nand/Config.in @@ -0,0 +1,16 @@ +# drivers/mtd/nand/Config.in + +# $Id: Config.in,v 1.1 2001/04/20 15:27:38 dwmw2 Exp $ + +mainmenu_option next_comment + +comment 'NAND Flash Device Drivers' + +dep_tristate ' NAND Device Support' CONFIG_MTD_NAND $CONFIG_MTD +if [ "$CONFIG_MTD_NAND" = "y" -o "$CONFIG_MTD_NAND" = "m" ]; then + bool ' Enable ECC correction algorithm' CONFIG_MTD_NAND_ECC y + bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE y +fi +dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND + +endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile new file mode 100644 index 000000000000..64f845f20c79 --- /dev/null +++ b/drivers/mtd/nand/Makefile @@ -0,0 +1,14 @@ +# +# linux/drivers/nand/Makefile +# +# $Id: Makefile,v 1.3 2001/04/19 23:54:48 dwmw2 Exp $ + +O_TARGET := nandlink.o + +export-objs := nand.o + +obj-$(CONFIG_MTD_NAND) += nand.o +obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o +obj-$(CONFIG_MTD_NAND_SPIA) += spia.o + +include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c new file mode 100644 index 000000000000..546c2d3f6542 --- /dev/null +++ b/drivers/mtd/nand/nand.c @@ -0,0 +1,1369 @@ +/* + * drivers/mtd/nand.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * $Id: nand.c,v 1.10 2001/03/20 07:26:01 dwmw2 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 + * published by the Free Software Foundation. + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_NAND_ECC +#include +#endif + +/* + * Macros for low-level register control + */ +#define NAND_CTRL (*(volatile unsigned char *) \ + ((struct nand_chip *) mtd->priv)->CTRL_ADDR) +#define nand_select() NAND_CTRL &= ~this->NCE; \ + nand_command(mtd, NAND_CMD_RESET, -1, -1); \ + udelay (10); +#define nand_deselect() NAND_CTRL |= ~this->NCE; + +/* + * NAND low-level MTD interface functions + */ +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, u_char *ecc_code); +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, + u_char *ecc_code); +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen); +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); +static void nand_sync (struct mtd_info *mtd); + +/* + * Send command to NAND device + */ +static void nand_command (struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + register unsigned long NAND_IO_ADDR = this->IO_ADDR; + + /* Begin command latch cycle */ + NAND_CTRL |= this->CLE; + + /* + * Write out the command to the device. + */ + if (command != NAND_CMD_SEQIN) + writeb (command, NAND_IO_ADDR); + else { + if (mtd->oobblock == 256 && column >= 256) { + column -= 256; + writeb(NAND_CMD_RESET, NAND_IO_ADDR); + writeb(NAND_CMD_READOOB, NAND_IO_ADDR); + writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); + } + else if (mtd->oobblock == 512 && column >= 256) { + if (column < 512) { + column -= 256; + writeb(NAND_CMD_READ1, NAND_IO_ADDR); + writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); + } + else { + column -= 512; + writeb(NAND_CMD_READOOB, NAND_IO_ADDR); + writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); + } + } + else { + writeb(NAND_CMD_READ0, NAND_IO_ADDR); + writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); + } + } + + /* Set ALE and clear CLE to start address cycle */ + NAND_CTRL &= ~this->CLE; + NAND_CTRL |= this->ALE; + + /* Serially input address */ + if (column != -1) + writeb (column, NAND_IO_ADDR); + if (page_addr != -1) { + writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); + writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) { + writeb ((unsigned char) ((page_addr >> 16) & 0x0f), + NAND_IO_ADDR); + } + } + + /* Latch in address */ + NAND_CTRL &= ~this->ALE; + + /* Pause for 15us */ + udelay (15); +} + +/* + * NAND read + */ +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ +#ifdef CONFIG_MTD_NAND_ECC + struct nand_chip *this = mtd->priv; + + return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf); +#else + return nand_read_ecc (mtd, from, len, retlen, buf, NULL); +#endif +} + +/* + * NAND read with ECC + */ +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, u_char *ecc_code) +{ + int j, col, page, state; + int erase_state = 0; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); +#ifdef CONFIG_MTD_NAND_ECC + int ecc_result; + u_char ecc_calc[6]; +#endif + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, + (int) len); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_read_ecc: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ +retry: + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_READING; + spin_unlock_bh (&this->chip_lock); + break; + + case FL_ERASING: + this->state = FL_READING; + erase_state = 1; + spin_unlock_bh (&this->chip_lock); + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* First we calculate the starting page */ + page = from >> this->page_shift; + + /* Get raw starting column */ + col = from & (mtd->oobblock - 1); + + /* State machine for devices having pages larger than 256 bytes */ + state = (col < mtd->eccsize) ? 0 : 1; + + /* Calculate column address within ECC block context */ + col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col; + + /* Initialize return value */ + *retlen = 0; + + /* Select the NAND device */ + nand_select (); + + /* Loop until all data read */ + while (*retlen < len) { + +#ifdef CONFIG_MTD_NAND_ECC + /* Send the read command */ + if (!state) + nand_command (mtd, NAND_CMD_READ0, 0x00, page); + else + nand_command (mtd, NAND_CMD_READ1, 0x00, page); + + /* Read in a block big enough for ECC */ + for (j=0 ; j < mtd->eccsize ; j++) + this->data_buf[j] = readb (this->IO_ADDR); + + /* Read in the out-of-band data */ + if (!state) { + nand_command (mtd, NAND_CMD_READOOB, 0x00, page); + for (j=0 ; j<3 ; j++) + ecc_code[j] = readb(this->IO_ADDR); + nand_command (mtd, NAND_CMD_READ0, 0x00, page); + } + else { + nand_command (mtd, NAND_CMD_READOOB, 0x03, page); + for (j=3 ; j<6 ; j++) + ecc_code[j] = readb(this->IO_ADDR); + nand_command (mtd, NAND_CMD_READ0, 0x00, page); + } + + /* Calculate the ECC and verify it */ + if (!state) { + nand_calculate_ecc (&this->data_buf[0], + &ecc_calc[0]); + ecc_result = nand_correct_data (&this->data_buf[0], + &ecc_code[0], &ecc_calc[0]); + } + else { + nand_calculate_ecc (&this->data_buf[0], + &ecc_calc[3]); + ecc_result = nand_correct_data (&this->data_buf[0], + &ecc_code[3], &ecc_calc[3]); + } + if (ecc_result == -1) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_read_ecc: " \ + "Failed ECC read, page 0x%08x\n", page); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + if (erase_state) + this->state = FL_ERASING; + else + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Read the data from ECC data buffer into return buffer */ + if ((*retlen + (mtd->eccsize - col)) >= len) { + while (*retlen < len) + buf[(*retlen)++] = this->data_buf[col++]; + /* We're done */ + continue; + } + else + for (j=col ; j < mtd->eccsize ; j++) + buf[(*retlen)++] = this->data_buf[j]; +#else + /* Send the read command */ + if (!state) + nand_command (mtd, NAND_CMD_READ0, col, page); + else + nand_command (mtd, NAND_CMD_READ1, col, page); + + /* Read the data directly into the return buffer */ + if ((*retlen + (mtd->eccsize - col)) >= len) { + while (*retlen < len) + buf[(*retlen)++] = readb (this->IO_ADDR); + /* We're done */ + continue; + } + else + for (j=col ; j < mtd->eccsize ; j++) + buf[(*retlen)++] = readb (this->IO_ADDR); +#endif + + /* + * If the amount of data to be read is greater than + * (256 - col), then all subsequent reads will take + * place on page or half-page (in the case of 512 byte + * page devices) aligned boundaries and the column + * address will be zero. Setting the column address to + * to zero after the first read allows us to simplify + * the reading of data and the if/else statements above. + */ + if (col) + col = 0x00; + + /* Increment page address */ + if ((mtd->oobblock == 256) || state) + page++; + + /* Toggle state machine */ + if (mtd->oobblock == 512) + state = state ? 0 : 1; + } + + /* De-select the NAND device */ + nand_deselect (); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + if (erase_state) + this->state = FL_ERASING; + else + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + return 0; +} + +/* + * NAND read out-of-band + */ +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int i, col, page; + int erase_state = 0; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, + (int) len); + + /* Shift to get page */ + page = ((int) from) >> this->page_shift; + + /* Mask to get column */ + col = from & 0x0f; + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow read past end of page */ + if ((col + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_read_oob: Attempt read past end of page " \ + "0x%08x, column %i, length %i\n", page, col, len); + return -EINVAL; + } + +retry: + /* Grab the lock and see if the device is available */ + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_READING; + spin_unlock_bh (&this->chip_lock); + break; + + case FL_ERASING: + this->state = FL_READING; + erase_state = 1; + spin_unlock_bh (&this->chip_lock); + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* Select the NAND device */ + nand_select (); + + /* Send the read command */ + nand_command (mtd, NAND_CMD_READOOB, col, page); + + /* Read the data */ + for (i = 0 ; i < len ; i++) + buf[i] = readb (this->IO_ADDR); + + /* De-select the NAND device */ + nand_deselect (); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + if (erase_state) + this->state = FL_ERASING; + else + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + *retlen = len; + return 0; +} + +/* + * NAND write + */ +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ +#ifdef CONFIG_MTD_NAND_ECC + struct nand_chip *this = mtd->priv; + + return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf); +#else + return nand_write_ecc (mtd, to, len, retlen, buf, NULL); +#endif +} + +/* + * NAND write with ECC + */ +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, + u_char *ecc_code) +{ + int i, page, col, cnt, status; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); +#ifdef CONFIG_MTD_NAND_ECC + int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; +#endif + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, + (int) len); + + /* Do not allow write past end of page */ + if ((to + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: Attempted write past end of device\n"); + return -EINVAL; + } + +retry: + /* Grab the lock and see if the device is available */ + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_WRITING; + spin_unlock_bh (&this->chip_lock); + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Get the starting column */ + col = to & (mtd->oobblock - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Select the NAND device */ + nand_select (); + + /* Check the WP bit */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: Device is write protected!!!\n"); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Loop until all data is written */ + while (*retlen < len) { + /* Write data into buffer */ + if ((col + len) >= mtd->oobblock) + for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++) + this->data_buf[i] = buf[(*retlen + cnt)]; + else + for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++) + this->data_buf[i] = buf[(*retlen + cnt)]; + +#ifdef CONFIG_MTD_NAND_ECC + /* Zero out the ECC array */ + for (i=0 ; i < 6 ; i++) + ecc_code[i] = 0x00; + + /* Calculate and write the ECC if we have enough data */ + if ((col < mtd->eccsize) && + ((col + (len - *retlen)) >= mtd->eccsize)) { + nand_command (mtd, NAND_CMD_READ0, col, page); + for (i=0 ; i < col ; i++) + this->data_buf[i] = readb (this->IO_ADDR); + nand_calculate_ecc (&this->data_buf[0], &ecc_code[0]); + for (i=0 ; i<3 ; i++) + this->data_buf[(mtd->oobblock + i)] = + ecc_code[i]; + } + + /* Calculate and write the second ECC if we have enough data */ + if ((mtd->oobblock == 512) && + ((col + (len - *retlen)) >= mtd->oobblock)) { + nand_calculate_ecc (&this->data_buf[256], &ecc_code[3]); + for (i=3 ; i<6 ; i++) + this->data_buf[(mtd->oobblock + i)] = + ecc_code[i]; + } + + /* Write ones for partial page programming */ + for (i=ecc_bytes ; i < mtd->oobsize ; i++) + this->data_buf[(mtd->oobblock + i)] = 0xff; +#else + /* Write ones for partial page programming */ + for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) + this->data_buf[i] = 0xff; +#endif + + /* Write pre-padding bytes into buffer */ + for (i=0 ; i < col ; i++) + this->data_buf[i] = 0xff; + + /* Write post-padding bytes into buffer */ + if ((col + (len - *retlen)) < mtd->oobblock) { + for(i=(col + cnt) ; i < mtd->oobblock ; i++) + this->data_buf[i] = 0xff; + } + + /* Send command to begin auto page programming */ + nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data */ + for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) + writeb (this->data_buf[i], this->IO_ADDR); + + /* Send command to actually program the data */ + nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* + * Wait for program operation to complete. This could + * take up to 3000us (3ms) on some devices, so we try + * and exit as quickly as possible. + */ + status = 0; + for (i=0 ; i<24 ; i++) { + /* Delay for 125us */ + udelay (125); + + /* Check the status */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + status = (int) readb (this->IO_ADDR); + if (status & 0x40) + break; + } + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: " \ + "Failed write, page 0x%08x, " \ + "%6i bytes were succesful\n", page, *retlen); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* + * The NAND device assumes that it is always writing to + * a cleanly erased page. Hence, it performs its internal + * write verification only on bits that transitioned from + * 1 to 0. The device does NOT verify the whole page on a + * byte by byte basis. It is possible that the page was + * not completely erased or the page is becoming unusable + * due to wear. The read with ECC would catch the error + * later when the ECC page check fails, but we would rather + * catch it early in the page write stage. Better to write + * no data than invalid data. + */ + + /* Send command to read back the page */ + if (col < mtd->eccsize) + nand_command (mtd, NAND_CMD_READ0, col, page); + else + nand_command (mtd, NAND_CMD_READ1, col - 256, page); + + /* Loop through and verify the data */ + for (i=col ; i < cnt ; i++) { + if (this->data_buf[i] != readb (this->IO_ADDR)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: " \ + "Failed write verify, page 0x%08x, " \ + "%6i bytes were succesful\n", + page, *retlen); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + } + +#ifdef CONFIG_MTD_NAND_ECC + /* + * We also want to check that the ECC bytes wrote + * correctly for the same reasons stated above. + */ + nand_command (mtd, NAND_CMD_READOOB, 0x00, page); + for (i=0 ; i < ecc_bytes ; i++) { + if ((readb (this->IO_ADDR) != ecc_code[i]) && + ecc_code[i]) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_ecc: Failed ECC write " \ + "verify, page 0x%08x, " \ + "%6i bytes were succesful\n", + page, i); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + } +#endif + +#endif + + /* + * If we are writing a large amount of data and/or it + * crosses page or half-page boundaries, we set the + * the column to zero. It simplifies the program logic. + */ + if (col) + col = 0x00; + + /* Update written bytes count */ + *retlen += cnt; + + /* Increment page address */ + page++; + } + + /* De-select the NAND device */ + nand_deselect (); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + *retlen = len; + return 0; +} + +/* + * NAND write out-of-band + */ +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int i, column, page, status; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, + (int) len); + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Mask to get column */ + column = to & 0x1f; + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow write past end of page */ + if ((column + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_oob: Attempt to write past end of page\n"); + return -EINVAL; + } + +retry: + /* Grab the lock and see if the device is available */ + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_WRITING; + spin_unlock_bh (&this->chip_lock); + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* Select the NAND device */ + nand_select (); + + /* Check the WP bit */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_oob: Device is write protected!!!\n"); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Write out desired data */ + nand_command (mtd, NAND_CMD_SEQIN, column + 512, page); + for (i=0 ; iIO_ADDR); + + /* Send command to program the OOB data */ + nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* + * Wait for program operation to complete. This could + * take up to 3000us (3ms) on some devices, so we try + * and exit as quickly as possible. + */ + status = 0; + for (i=0 ; i<24 ; i++) { + /* Delay for 125us */ + udelay (125); + + /* Check the status */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + status = (int) readb (this->IO_ADDR); + if (status & 0x40) + break; + } + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_oob: " \ + "Failed write, page 0x%08x\n", page); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + nand_command (mtd, NAND_CMD_READOOB, column, page); + + /* Loop through and verify the data */ + for (i=0 ; iIO_ADDR)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_write_oob: " \ + "Failed write verify, page 0x%08x\n", page); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + } +#endif + + /* De-select the NAND device */ + nand_deselect (); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + *retlen = len; + return 0; +} + +/* + * NAND write with iovec + */ +static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + int i, page, col, cnt, len, total_len, status; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); +#ifdef CONFIG_MTD_NAND_ECC + int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; +#endif + + /* Calculate total length of data */ + total_len = 0; + for (i=0 ; i < count ; i++) + total_len += (int) vecs[i].iov_len; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_writev: to = 0x%08x, len = %i\n", (unsigned int) to, + (unsigned int) total_len); + + /* Do not allow write past end of page */ + if ((to + total_len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: Attempted write past end of device\n"); + return -EINVAL; + } + +retry: + /* Grab the lock and see if the device is available */ + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_WRITING; + spin_unlock_bh (&this->chip_lock); + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + + /* Get the starting column */ + col = to & (mtd->oobblock - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Select the NAND device */ + nand_select (); + + /* Check the WP bit */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: Device is write protected!!!\n"); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Loop until all iovecs' data has been written */ + cnt = col; + len = 0; + while (count) { + /* Do any need pre-fill for partial page programming */ + for (i=0 ; i < cnt ; i++) + this->data_buf[i] = 0xff; + + /* + * Read data out of each tuple until we have a full page + * to write or we've read all the tuples. + */ + while ((cnt < mtd->oobblock) && count) { + this->data_buf[cnt++] = + ((u_char *) vecs->iov_base)[len++]; + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + + /* Do any need post-fill for partial page programming */ + for (i=cnt ; i < mtd->oobblock ; i++) + this->data_buf[i] = 0xff; + +#ifdef CONFIG_MTD_NAND_ECC + /* Zero out the ECC array */ + for (i=0 ; i < 6 ; i++) + this->ecc_code_buf[i] = 0x00; + + /* Calculate and write the first ECC */ + if (col >= mtd->eccsize) { + nand_command (mtd, NAND_CMD_READ0, col, page); + for (i=0 ; i < col ; i++) + this->data_buf[i] = readb (this->IO_ADDR); + nand_calculate_ecc (&this->data_buf[0], + &(this->ecc_code_buf[0])); + for (i=0 ; i<3 ; i++) + this->data_buf[(mtd->oobblock + i)] = + this->ecc_code_buf[i]; + } + + /* Calculate and write the second ECC */ + if ((mtd->oobblock == 512) && (cnt == mtd->oobblock)) { + nand_calculate_ecc (&this->data_buf[256], + &(this->ecc_code_buf[3])); + for (i=3 ; i<6 ; i++) + this->data_buf[(mtd->oobblock + i)] = + this->ecc_code_buf[i]; + } + + /* Write ones for partial page programming */ + for (i=ecc_bytes ; i < mtd->oobsize ; i++) + this->data_buf[(mtd->oobblock + i)] = 0xff; +#else + /* Write ones for partial page programming */ + for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) + this->data_buf[i] = 0xff; +#endif + /* Send command to begin auto page programming */ + nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data */ + for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) + writeb (this->data_buf[i], this->IO_ADDR); + + /* Send command to actually program the data */ + nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* + * Wait for program operation to complete. This could + * take up to 3000us (3ms) on some devices, so we try + * and exit as quickly as possible. + */ + status = 0; + for (i=0 ; i<24 ; i++) { + /* Delay for 125us */ + udelay (125); + + /* Check the status */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + status = (int) readb (this->IO_ADDR); + if (status & 0x40) + break; + } + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: " \ + "Failed write, page 0x%08x, " \ + "%6i bytes were succesful\n", page, *retlen); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* + * The NAND device assumes that it is always writing to + * a cleanly erased page. Hence, it performs its internal + * write verification only on bits that transitioned from + * 1 to 0. The device does NOT verify the whole page on a + * byte by byte basis. It is possible that the page was + * not completely erased or the page is becoming unusable + * due to wear. The read with ECC would catch the error + * later when the ECC page check fails, but we would rather + * catch it early in the page write stage. Better to write + * no data than invalid data. + */ + + /* Send command to read back the page */ + if (col < mtd->eccsize) + nand_command (mtd, NAND_CMD_READ0, col, page); + else + nand_command (mtd, NAND_CMD_READ1, col - 256, page); + + /* Loop through and verify the data */ + for (i=col ; i < cnt ; i++) { + if (this->data_buf[i] != readb (this->IO_ADDR)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: " \ + "Failed write verify, page 0x%08x, " \ + "%6i bytes were succesful\n", + page, *retlen); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + } + +#ifdef CONFIG_MTD_NAND_ECC + /* + * We also want to check that the ECC bytes wrote + * correctly for the same reasons stated above. + */ + nand_command (mtd, NAND_CMD_READOOB, 0x00, page); + for (i=0 ; i < ecc_bytes ; i++) { + if ((readb (this->IO_ADDR) != this->ecc_code_buf[i]) && + this->ecc_code_buf[i]) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_writev: Failed ECC write " \ + "verify, page 0x%08x, " \ + "%6i bytes were succesful\n", + page, i); + nand_deselect (); + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + } +#endif + +#endif + /* Update written bytes count */ + *retlen += (cnt - col); + + /* Reset written byte counter and column */ + col = cnt = 0; + + /* Increment page address */ + page++; + } + + /* De-select the NAND device */ + nand_deselect (); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + return 0; +} + +/* + * NAND erase a block + */ +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + int i, page, len, status, pages_per_block; + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_erase: start = 0x%08x, len = %i\n", + (unsigned int) instr->addr, (unsigned int) instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & (mtd->erasesize - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & (mtd->erasesize - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Erase past end of device\n"); + return -EINVAL; + } + +retry: + /* Grab the lock and see if the device is available */ + spin_lock_bh (&this->chip_lock); + + switch (this->state) { + case FL_READY: + this->state = FL_ERASING; + break; + + default: + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto retry; + }; + + /* Shift to get first page */ + page = (int) (instr->addr >> this->page_shift); + + /* Calculate pages in each block */ + pages_per_block = mtd->erasesize / mtd->oobblock; + + /* Select the NAND device */ + nand_select (); + + /* Check the WP bit */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Device is write protected!!!\n"); + nand_deselect (); + this->state = FL_READY; + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Loop through the pages */ + len = instr->len; + while (len) { + /* Send commands to erase a page */ + nand_command(mtd, NAND_CMD_ERASE1, -1, page); + nand_command(mtd, NAND_CMD_ERASE2, -1, -1); + + /* + * Wait for program operation to complete. This could + * take up to 4000us (4ms) on some devices, so we try + * and exit as quickly as possible. + */ + status = 0; + for (i=0 ; i<32 ; i++) { + /* Delay for 125us */ + udelay (125); + + /* Check the status */ + nand_command (mtd, NAND_CMD_STATUS, -1, -1); + status = (int) readb (this->IO_ADDR); + if (status & 0x40) + break; + } + + /* See if block erase succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: " \ + "Failed erase, page 0x%08x\n", page); + nand_deselect (); + this->state = FL_READY; + spin_unlock_bh (&this->chip_lock); + return -EIO; + } + + /* Increment page address and decrement length */ + len -= mtd->erasesize; + page += pages_per_block; + + /* Release the spin lock */ + spin_unlock_bh (&this->chip_lock); + +erase_retry: + /* Check the state and sleep if it changed */ + spin_lock_bh (&this->chip_lock); + if (this->state == FL_ERASING) { + continue; + } + else { + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule(); + + remove_wait_queue (&this->wq, &wait); + goto erase_retry; + } + } + spin_unlock_bh (&this->chip_lock); + + /* De-select the NAND device */ + nand_deselect (); + + /* Do call back function */ + if (instr->callback) + instr->callback (instr); + + /* The device is ready */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; + spin_unlock_bh (&this->chip_lock); + + /* Return happy */ + return 0; +} + +/* + * NAND sync + */ +static void nand_sync (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + DECLARE_WAITQUEUE(wait, current); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + +retry: + /* Grab the spinlock */ + spin_lock_bh(&this->chip_lock); + + /* See what's going on */ + switch(this->state) { + case FL_READY: + case FL_SYNCING: + this->state = FL_SYNCING; + spin_unlock_bh (&this->chip_lock); + break; + + default: + /* Not an idle state */ + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule (); + + remove_wait_queue (&this->wq, &wait); + goto retry; + } + + /* Lock the device */ + spin_lock_bh (&this->chip_lock); + + /* Set the device to be ready again */ + if (this->state == FL_SYNCING) { + this->state = FL_READY; + wake_up (&this->wq); + } + + /* Unlock the device */ + spin_unlock_bh (&this->chip_lock); +} + +/* + * Scan for the NAND device + */ +int nand_scan (struct mtd_info *mtd) +{ + int i, nand_maf_id, nand_dev_id; + struct nand_chip *this = mtd->priv; + + /* Select the device */ + nand_select (); + + /* Send the command for reading device ID */ + nand_command (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + nand_maf_id = readb (this->IO_ADDR); + nand_dev_id = readb (this->IO_ADDR); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (nand_maf_id == nand_flash_ids[i].manufacture_id && + nand_dev_id == nand_flash_ids[i].model_id) { + if (!mtd->size) { + mtd->name = nand_flash_ids[i].name; + mtd->erasesize = nand_flash_ids[i].erasesize; + mtd->size = (1 << nand_flash_ids[i].chipshift); + mtd->eccsize = 256; + if (nand_flash_ids[i].page256) { + mtd->oobblock = 256; + mtd->oobsize = 8; + this->page_shift = 8; + } + else { + mtd->oobblock = 512; + mtd->oobsize = 16; + this->page_shift = 9; + } + } + printk (KERN_INFO "NAND device: Manufacture ID:" \ + " 0x%02x, Chip ID: 0x%02x (%s)\n", + nand_maf_id, nand_dev_id, mtd->name); + break; + } + } + + /* Initialize state and spinlock */ + this->state = FL_READY; + spin_lock_init(&this->chip_lock); + + /* De-select the device */ + nand_deselect (); + + /* Print warning message for no device */ + if (!mtd->size) { + printk (KERN_WARNING "No NAND device found!!!\n"); + return 1; + } + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->module = THIS_MODULE; + mtd->ecctype = MTD_ECC_SW; + mtd->erase = nand_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = nand_read; + mtd->write = nand_write; + mtd->read_ecc = nand_read_ecc; + mtd->write_ecc = nand_write_ecc; + mtd->read_oob = nand_read_oob; + mtd->write_oob = nand_write_oob; + mtd->readv = NULL; + mtd->writev = nand_writev; + mtd->sync = nand_sync; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; + + /* Return happy */ + return 0; +} + +EXPORT_SYMBOL(nand_scan); diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c new file mode 100644 index 000000000000..75b84a741e5c --- /dev/null +++ b/drivers/mtd/nand/nand_ecc.c @@ -0,0 +1,204 @@ +/* + * drivers/mtd/nand_ecc.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * Toshiba America Electronics Components, Inc. + * + * $Id: nand_ecc.c,v 1.4 2001/01/03 20:02:20 mgadbois 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 + * published by the Free Software Foundation. + * + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + */ + +#include + +/* + * Pre-calculated 256-way 1 byte column parity + */ +const u_char nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + + +/* + * Creates non-inverted ECC code from line parity + */ +void nand_trans_result(u_char reg2, u_char reg3, + u_char *ecc_code) +{ + u_char a, b, i, tmp1, tmp2; + + /* Initialize variables */ + a = b = 0x80; + tmp1 = tmp2 = 0; + + /* Calculate first ECC byte */ + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + a >>= 1; + } + + /* Calculate second ECC byte */ + b = 0x80; + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + a >>= 1; + } + + /* Store two of the ECC bytes */ + ecc_code[0] = tmp1; + ecc_code[1] = tmp2; +} + +/* + * Calculate 3 byte ECC code for 256 byte block + */ +void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) +{ + u_char idx, reg1, reg2, reg3; + int j; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + + /* Build up column parity */ + for(j = 0; j < 256; j++) { + + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[dat[j]]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (u_char) j; + reg2 ^= ~((u_char) j); + } + } + + /* Create non-inverted ECC code from line parity */ + nand_trans_result(reg2, reg3, ecc_code); + + /* Calculate final ECC code */ + ecc_code[0] = ~ecc_code[0]; + ecc_code[1] = ~ecc_code[1]; + ecc_code[2] = ((~reg1) << 2) | 0x03; +} + +/* + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data (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 1; + } + 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 2; + } + else { + /* Uncorrectable Error */ + return -1; + } + } + } + + /* Should never happen */ + return -1; +} diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c new file mode 100644 index 000000000000..bf92ad67a414 --- /dev/null +++ b/drivers/mtd/nand/spia.c @@ -0,0 +1,129 @@ +/* + * drivers/mtd/spia.c + * + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * $Id: spia.c,v 1.9 2001/06/02 14:47:16 dwmw2 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 + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * SPIA board which utilizes the Toshiba TC58V64AFT part. This is + * a 64Mibit (8MiB x 8 bits) NAND flash device. + */ + +#include +#include +#include +#include +#include +#include + +/* + * MTD structure for SPIA board + */ +static struct mtd_info *spia_mtd = NULL; + +/* + * Module stuff + */ +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) + #define spia_init init_module + #define spia_cleanup cleanup_module +#endif + +/* + * Define partitions for flash device + */ +const static struct mtd_partition partition_info[] = { + { name: "SPIA flash partition 1", + offset: 0, + size: 2*1024*1024 }, + { name: "SPIA flash partition 2", + offset: 2*1024*1024, + size: 6*1024*1024 } +}; +#define NUM_PARTITIONS 2 + +/* + * Main initialization routine + */ +int __init spia_init (void) +{ + struct nand_chip *this; + + /* Allocate memory for MTD device structure and private data */ + spia_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!spia_mtd) { + printk ("Unable to allocate SPIA NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&spia_mtd[1]); + + /* Initialize structures */ + memset((char *) spia_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + spia_mtd->priv = this; + + /* + * Set GPIO Port E control register so that the pins are configured + * to be outputs for controlling the NAND flash. + */ + (*(volatile unsigned char *) (IO_BASE + PEDDR)) = 0x07; + + /* Set address of NAND IO lines */ + this->IO_ADDR = FIO_BASE; + this->CTRL_ADDR = IO_BASE + PEDR; + this->CLE = 0x01; + this->ALE = 0x02; + this->NCE = 0x04; + + /* Scan to find existance of the device */ + if (nand_scan (spia_mtd)) { + kfree (spia_mtd); + return -ENXIO; + } + + /* Allocate memory for internal data buffer */ + this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); + if (!this->data_buf) { + printk ("Unable to allocate NAND data buffer for SPIA.\n"); + kfree (spia_mtd); + return -ENOMEM; + } + + /* Register the partitions */ + add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); + + /* Return happy */ + return 0; +} +module_init(spia_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit spia_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &spia_mtd[1]; + + /* Unregister the device */ + del_mtd_device (spia_mtd); + + /* Free internal data buffer */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (spia_mtd); +} +module_exit(spia_cleanup); +#endif diff --git a/drivers/mtd/nftl.c b/drivers/mtd/nftlcore.c similarity index 88% rename from drivers/mtd/nftl.c rename to drivers/mtd/nftlcore.c index ee1390af50d2..d5d27d839593 100644 --- a/drivers/mtd/nftl.c +++ b/drivers/mtd/nftlcore.c @@ -1,56 +1,13 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $ */ +/* $Id: nftlcore.c,v 1.73 2001/06/09 01:09:43 dwmw2 Exp $ */ /* The contents of this file are distributed under the GNU General - Public License version 2 ("GPL"). The author places no additional - restrictions of any kind on it. However, local legislation in some - countries may restrict the use of the algorithms implemented by this - code in certain circumstances. - - The legal note below refers only to the _use_ of the code in the - affected jurisdictions, and does not in any way affect the copying, - distribution and modification of this code, which are permitted, and - indeed required, under the terms of the GPL. - - Section 0 of the GPL says: - "Activities other than copying, distribution and modification are not - covered by this License; they are outside its scope." - - You may copy, distribute and modify this code to your hearts' - content - it's just that in some jurisdictions, you may only _use_ - it under the terms of the patent grant below. This puts it in a - similar situation to the ISDN code, which you may need telco - approval to use, and indeed any code which has uses that may be - restricted in law. For example, certain malicious uses of the - networking stack may be illegal, but that doesn't prevent the - networking code from being under GPL. - - In fact the ISDN case is worse than this, because modification of - the code automatically invalidates its approval. Modification, - unlike usage, _is_ one of the rights which is protected by the - GPL. Happily, the law in those places where approval is required - doesn't actually prevent you from modifying the code - it's just - that you may not be allowed to _use_ it once you've done so - and - because usage isn't addressed by the GPL, that's just fine. - - dwmw2@infradead.org - 30/10/0 - - LEGAL NOTE: The NFTL format is patented by M-Systems. They have - granted a licence for its use with their DiskOnChip products: - - "M-Systems grants a royalty-free, non-exclusive license under - any presently existing M-Systems intellectual property rights - necessary for the design and development of NFTL-compatible - drivers, file systems and utilities to use the data formats with, - and solely to support, M-Systems' DiskOnChip products" - - A signed copy of this agreement from M-Systems is kept on file by - Red Hat UK Limited. In the unlikely event that you need access to it, - please contact dwmw2@redhat.com for assistance. */ + Public License version 2. The author places no additional + restrictions of any kind on it. + */ #define PRERELEASE @@ -67,6 +24,7 @@ #include #include #include + #ifdef CONFIG_KMOD #include #endif @@ -95,11 +53,11 @@ * encountered, except ... */ -static int nftl_sizes[256] = {0,}; -static int nftl_blocksizes[256] = {0,}; +static int nftl_sizes[256]; +static int nftl_blocksizes[256]; /* .. for the Linux partition table handling. */ -struct hd_struct part_table[256] = {{0,0},}; +struct hd_struct part_table[256]; #if LINUX_VERSION_CODE < 0x20328 static void dummy_init (struct gendisk *crap) @@ -107,22 +65,19 @@ static void dummy_init (struct gendisk *crap) #endif static struct gendisk nftl_gendisk = { - MAJOR_NR, /* Major number */ - "nftl", /* Major name */ - 4, /* Bits to shift to get real from partition */ - 15, /* Number of partitions per real */ + major: MAJOR_NR, + major_name: "nftl", + minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */ + max_p: (1<nr_sects; + nftl_gendisk.nr_real++; + /* partition check ... */ #if LINUX_VERSION_CODE < 0x20328 resetup_one_dev(&nftl_gendisk, firstfree); #else - grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects); + grok_partitions(&nftl_gendisk, firstfree, 1<nr_sects); #endif } @@ -226,6 +183,7 @@ static void NFTL_unsetup(int i) if (nftl->EUNtable) kfree(nftl->EUNtable); + nftl_gendisk.nr_real--; kfree(nftl); } @@ -654,7 +612,17 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); writeEUN = NFTL_makefreeblock(nftl, 0xffff); - + + if (writeEUN == BLOCK_NIL) { + /* OK, we accept that the above comment is + lying - there may have been free blocks + last time we called NFTL_findfreeblock(), + but they are reserved for when we're + desperate. Well, now we're desperate. + */ + DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); + writeEUN = NFTL_findfreeblock(nftl, 1); + } if (writeEUN == BLOCK_NIL) { /* Ouch. This should never happen - we should always be able to make some room somehow. @@ -806,8 +774,9 @@ static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { struct NFTLrecord *nftl; + int p; - nftl = NFTLs[MINOR(inode->i_rdev) / 16]; + nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS]; if (!nftl) return -EINVAL; @@ -837,11 +806,26 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (nftl->usecount > 1) return -EBUSY; + /* + * We have to flush all buffers and invalidate caches, + * or we won't be able to re-use the partitions, + * if there was a change and we don't want to reboot + */ + p = (1< 0) { + kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p); + if (part_table[p].nr_sects > 0) + invalidate_device (devp, 1); + + part_table[MINOR(inode->i_dev)+p].start_sect = 0; + part_table[MINOR(inode->i_dev)+p].nr_sects = 0; + } + #if LINUX_VERSION_CODE < 0x20328 - resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16); + resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS); #else - grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16, - 1<<4, nftl->nr_sects); + grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS, + 1<nr_sects); #endif return 0; @@ -886,7 +870,7 @@ void nftl_request(RQFUNC_ARG) buffer = req->buffer; res = 1; /* succeed */ - if (dev >= MAX_NFTLS * 16) { + if (dev >= MAX_NFTLS * (1<rq_dev)); @@ -894,7 +878,7 @@ void nftl_request(RQFUNC_ARG) goto repeat; } - nftl = NFTLs[dev / 16]; + nftl = NFTLs[dev / (1<mutex); DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); @@ -962,7 +946,7 @@ void nftl_request(RQFUNC_ARG) static int nftl_open(struct inode *ip, struct file *fp) { - int nftlnum = MINOR(ip->i_rdev) / 16; + int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS; struct NFTLrecord *thisNFTL; thisNFTL = NFTLs[nftlnum]; @@ -1045,17 +1029,19 @@ static struct block_device_operations nftl_fops = #define cleanup_nftl cleanup_module #endif -static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL}; +static struct mtd_notifier nftl_notifier = { + add: NFTL_notify_add, + remove: NFTL_notify_remove +}; -/* static int __init init_nftl(void) */ -int __init init_nftl(void) +static int __init init_nftl(void) { int i; printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n"); #ifdef PRERELEASE - printk(KERN_INFO"$Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $\n"); + printk(KERN_INFO"$Id: nftlcore.c,v 1.73 2001/06/09 01:09:43 dwmw2 Exp $\n"); #endif if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ @@ -1101,7 +1087,7 @@ static void __exit cleanup_nftl(void) if (*gdp == &nftl_gendisk) { gd = *gdp; *gdp = gd->next; break; - } + } } module_init(init_nftl); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 72f734d98488..7a480690082b 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.11 2000/11/17 12:24:09 ollie Exp $ + * $Id: nftlmount.c,v 1.17 2001/06/02 20:33:20 dwmw2 Exp $ * * 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 @@ -20,6 +20,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#define __NO_VERSION__ #include #include #include @@ -85,13 +87,20 @@ static int find_boot_record(struct NFTLrecord *nftl) } nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); - if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { + printk(KERN_NOTICE "Potential NFTL Media Header found, but sanity check failed:\n"); + printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n", + nftl->nb_boot_blocks, nftl->nb_blocks); goto ReplUnitTable; /* small consistency check */ + } nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize; - if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) + if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) { + printk(KERN_NOTICE "Potential NFTL Media Header found, but sanity check failed:\n"); + printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n", + nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks); goto ReplUnitTable; /* small consistency check */ - + } /* FixMe: with bad blocks, the total size available is not FormattedSize any more !!! */ nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); @@ -359,8 +368,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) { struct nftl_uci1 h1; unsigned int erase_mark; - int i, retlen; - unsigned char buf[SECTORSIZE]; + int retlen; /* check erase mark. */ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c new file mode 100644 index 000000000000..b2a5be2f6875 --- /dev/null +++ b/drivers/mtd/redboot.c @@ -0,0 +1,150 @@ +/* + * $Id: redboot.c,v 1.4 2001/05/31 20:43:18 dwmw2 Exp $ + * + * Parse RedBoot-style Flash Image System (FIS) tables and + * produce a Linux partition array to match. + */ + +#include +#include + +#include +#include + +struct fis_image_desc { + unsigned char name[16]; // Null terminated name + unsigned long flash_base; // Address within FLASH of image + unsigned long mem_base; // Address in memory where it executes + unsigned long size; // Length of image + unsigned long entry_point; // Execution entry point + unsigned long data_length; // Length of actual data + unsigned char _pad[256-(16+7*sizeof(unsigned long))]; + unsigned long desc_cksum; // Checksum over image descriptor + unsigned long file_cksum; // Checksum over image data +}; + +struct fis_list { + struct fis_image_desc *img; + struct fis_list *next; +}; + +static inline int redboot_checksum(struct fis_image_desc *img) +{ + /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ + return 1; +} + +int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + int nrparts = 0; + struct fis_image_desc *buf; + struct mtd_partition *parts; + struct fis_list *fl = NULL, *tmp_fl; + int ret, i; + size_t retlen; + char *names; + int namelen = 0; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + /* Read the start of the last erase block */ + ret = master->read(master, master->size - master->erasesize, + PAGE_SIZE, &retlen, (void *)buf); + + if (ret) + goto out; + + if (retlen != PAGE_SIZE) { + ret = -EIO; + goto out; + } + + if (memcmp(buf, "RedBoot", 8)) { + ret = 0; + goto out; + } + + for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) { + struct fis_list *new_fl, **prev; + + if (buf[i].name[0] == 0xff) + break; + if (!redboot_checksum(&buf[i])) + break; + + new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL); + namelen += strlen(buf[i].name)+1; + if (!new_fl) { + ret = -ENOMEM; + goto out; + } + new_fl->img = &buf[i]; + buf[i].flash_base &= master->size-1; + + /* I'm sure the JFFS2 code has done me permanent damage. + * I now think the following is _normal_ + */ + prev = &fl; + while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base) + prev = &(*prev)->next; + new_fl->next = *prev; + *prev = new_fl; + + nrparts++; + } + if (fl->img->flash_base) + nrparts++; + + for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) + nrparts++; + } + parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } + names = (char *)&parts[nrparts]; + memset(parts, 0, sizeof(*parts)*nrparts + namelen); + i=0; + + if (fl->img->flash_base) { + parts[0].name = "unallocated space"; + parts[0].size = fl->img->flash_base; + parts[0].offset = 0; + } + for ( ; iimg->size; + parts[i].offset = fl->img->flash_base; + parts[i].name = names; + + strcpy(names, fl->img->name); + names += strlen(names)+1; + + if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { + i++; + parts[i].offset = parts[i-1].size + parts[i-1].offset; + parts[i].size = fl->next->img->flash_base - parts[i].offset; + parts[i].name = "unallocated space"; + } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); + } + ret = nrparts; + *pparts = parts; + out: + while (fl) { + struct fis_list *old = fl; + fl = fl->next; + kfree(old); + } + kfree(buf); + return ret; +} + +EXPORT_SYMBOL(parse_redboot_partitions); diff --git a/drivers/mtd/slram.c b/drivers/mtd/slram.c deleted file mode 100644 index d2e4f8ed294c..000000000000 --- a/drivers/mtd/slram.c +++ /dev/null @@ -1,227 +0,0 @@ -/*====================================================================== - - $Id: slram.c,v 1.10 2000/07/03 10:01:38 dwmw2 Exp $ - -======================================================================*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -struct mypriv { - u_char *start; - u_char *end; -}; - -int physmem_erase (struct mtd_info *mtd, struct erase_info *instr); -int physmem_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); -void physmem_unpoint (struct mtd_info *mtd, u_char *addr); -int physmem_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -int physmem_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - - -int physmem_erase (struct mtd_info *mtd, struct erase_info *instr) -{ - struct mypriv *priv = mtd->priv; - - if (instr->addr + instr->len > mtd->size) - return -EINVAL; - - memset(priv->start + instr->addr, 0xff, instr->len); - - /* This'll catch a few races. Free the thing before returning :) - * I don't feel at all ashamed. This kind of thing is possible anyway - * with flash, but unlikely. - */ - - instr->state = MTD_ERASE_DONE; - - if (instr->callback) - (*(instr->callback))(instr); - else - kfree(instr); - - return 0; -} - - -int physmem_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) -{ - struct mypriv *priv = (struct mypriv *)mtd->priv; - - *mtdbuf = priv->start + from; - *retlen = len; - return 0; -} - -void physmem_unpoint (struct mtd_info *mtd, u_char *addr) -{ -} - -int physmem_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct mypriv *priv = (struct mypriv *)mtd->priv; - - memcpy (buf, priv->start + from, len); - - *retlen=len; - return 0; -} - -int physmem_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) -{ - struct mypriv *priv = (struct mypriv *)mtd->priv; - - memcpy (priv->start + to, buf, len); - - *retlen=len; - return 0; -} - - - - -/*====================================================================*/ - -/* Place your defaults here */ - -static u_long start = 100663296; -static u_long length = 33554432; -static u_long end = 0; - -#if LINUX_VERSION_CODE < 0x20300 -#ifdef MODULE -#define init_slram init_module -#define cleanup_slram cleanup_module -#endif -#define __exit -#endif - -#ifdef MODULE -MODULE_PARM(start,"l"); -MODULE_PARM(length,"l"); -MODULE_PARM(end,"l"); -#endif - -struct mtd_info *mymtd; - -void __init mtd_slram_setup(char *str, int *ints) -{ - if (ints[0] > 0) - start=ints[1]; - if (ints[0] > 1) - length=ints[2]; -} - -int init_slram(void) -{ - if (!start) - { - printk(KERN_NOTICE "physmem: No start address for memory device.\n"); - return -EINVAL; - } - - if (!length && !end) - { - printk(KERN_NOTICE "physmem: No length or endpointer given.\n"); - return -EINVAL; - } - - if (!end) - end = start + length; - - if (!length) - length = end - start; - - if (start + length != end) - { - printk(KERN_NOTICE "physmem: start(%lx) + length(%lx) != end(%lx) !\n", - start, length, end); - return -EINVAL; - } - - mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - - memset(mymtd, 0, sizeof(*mymtd)); - - if (mymtd) - { - memset((char *)mymtd, 0, sizeof(struct mtd_info)); - mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL); - if (!mymtd->priv) - { - kfree(mymtd); - mymtd = NULL; - } - memset(mymtd->priv, 0, sizeof(struct mypriv)); - } - - if (!mymtd) - { - printk(KERN_NOTICE "physmem: Cannot allocate new MTD device.\n"); - return -ENOMEM; - } - - - ((struct mypriv *)mymtd->priv)->start = ioremap(start, length); - ((struct mypriv *)mymtd->priv)->end = ((struct mypriv *)mymtd->priv)->start + length; - - - mymtd->name = "Raw memory"; - - mymtd->size = length; - mymtd->flags = MTD_CLEAR_BITS | MTD_SET_BITS | MTD_WRITEB_WRITEABLE | MTD_VOLATILE; - mymtd->erase = physmem_erase; - mymtd->point = physmem_point; - mymtd->unpoint = physmem_unpoint; - mymtd->read = physmem_read; - mymtd->write = physmem_write; - mymtd->module = THIS_MODULE; - mymtd->type = MTD_RAM; - mymtd->erasesize = 0x10000; - - if (add_mtd_device(mymtd)) - { - printk("Failed to register new device\n"); - iounmap(((struct mypriv *)mymtd->priv)->start); - kfree(mymtd->priv); - kfree(mymtd); - return -EAGAIN; - } - printk("Registered physmem device from %dKb to %dKb\n", - (int)(start / 1024), (int)(end / 1024)); - printk("Mapped from 0x%p to 0x%p\n",((struct mypriv *)mymtd->priv)->start, -((struct mypriv *)mymtd->priv)->end); - - return 0; -} - -static void __exit cleanup_slram(void) -{ - iounmap(((struct mypriv *)mymtd->priv)->start); - kfree (mymtd->priv); - del_mtd_device(mymtd); - kfree(mymtd); -} - -#if LINUX_VERSION_CODE > 0x20300 -module_init(init_slram); -module_exit(cleanup_slram); -#endif diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index f0ad0ac0b393..ecdacc0428f7 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -513,8 +513,8 @@ static int eepro100_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static void eepro100_remove_one (struct pci_dev *pdev); #ifdef CONFIG_EEPRO100_PM -static void eepro100_suspend (struct pci_dev *pdev); -static void eepro100_resume (struct pci_dev *pdev); +static int eepro100_suspend (struct pci_dev *pdev, u32 state); +static int eepro100_resume (struct pci_dev *pdev); #endif static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len); @@ -2130,7 +2130,7 @@ static void set_rx_mode(struct net_device *dev) } #ifdef CONFIG_EEPRO100_PM -static void eepro100_suspend(struct pci_dev *pdev) +static int eepro100_suspend(struct pci_dev *pdev, u32 state) { struct net_device *dev = pdev->driver_data; long ioaddr = dev->base_addr; @@ -2139,9 +2139,10 @@ static void eepro100_suspend(struct pci_dev *pdev) outl(PortPartialReset, ioaddr + SCBPort); /* XXX call pci_set_power_state ()? */ + return 0; } -static void eepro100_resume(struct pci_dev *pdev) +static int eepro100_resume(struct pci_dev *pdev) { struct net_device *dev = pdev->driver_data; struct speedo_private *sp = (struct speedo_private *)dev->priv; @@ -2160,6 +2161,7 @@ static void eepro100_resume(struct pci_dev *pdev) sp->rx_mode = -1; sp->flow_ctrl = sp->partner = 0; set_rx_mode(dev); + return 0; } #endif /* CONFIG_EEPRO100_PM */ diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 0f9ac4f8565c..b5c7fc767896 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -1185,7 +1185,7 @@ void tok_interrupt (int irq, void *dev_id, struct pt_regs *regs) isa_writeb(~CMD_IN_SRB, ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD); isa_writeb(~SRB_RESP_INT, ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD); - skip_reset: + skip_reset:; } /* SRB response */ if (status & ASB_FREE_INT) { /* ASB response */ diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index f9af6f247f45..3844763efe1b 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -1082,7 +1082,9 @@ static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev) } break; - default: /* to be defined */ + default: + /* to be defined */ + break; } dev_kfree_skb(skb); diff --git a/drivers/net/wan/sdla_fr.c b/drivers/net/wan/sdla_fr.c index 3e6bbfbc4091..04413757652e 100644 --- a/drivers/net/wan/sdla_fr.c +++ b/drivers/net/wan/sdla_fr.c @@ -4435,7 +4435,8 @@ int process_ARP(arphdr_1490_t *ArpPacket, sdla_t *card, netdevice_t* dev) trigger_fr_poll(dev); break; - default: // ARP's and RARP's -- Shouldn't happen. + default: + break; // ARP's and RARP's -- Shouldn't happen. } return 0; diff --git a/drivers/net/wan/sdla_x25.c b/drivers/net/wan/sdla_x25.c index 307f16ee394d..1266eb2ad2a2 100644 --- a/drivers/net/wan/sdla_x25.c +++ b/drivers/net/wan/sdla_x25.c @@ -3108,7 +3108,7 @@ dflt_1: case 0x08: /* modem failure */ #ifndef MODEM_NOT_LOG printk(KERN_INFO "%s: modem failure!\n", card->devname); -#endif MODEM_NOT_LOG +#endif /* MODEM_NOT_LOG */ api_oob_event(card,mb); break; diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6b78c7f6fae5..4d5a4186add3 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -13,9 +13,13 @@ O_TARGET := driver.o export-objs := pci.o -obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o setup-res.o +obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o obj-$(CONFIG_PROC_FS) += proc.o +ifndef CONFIG_SPARC64 +obj-$(CONFIG_PCI) += setup-res.o +endif + # # Some architectures use the generic PCI setup functions # diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a0f3389d7bbf..f65ea821d8d2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -229,49 +229,120 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) } /** - * pci_set_power_state - Set power management state of a device. - * @dev: PCI device for which PM is set - * @new_state: new power management statement (0 == D0, 3 == D3, etc.) + * pci_set_power_state - Set the power state of a PCI device + * @dev: PCI device to be suspended + * @state: Power state we're entering * - * Set power management state of a device. For transitions from state D3 - * it isn't as straightforward as one could assume since many devices forget - * their configuration space during wakeup. Returns old power state. + * Transition a device to a new power state, using the Power Management + * Capabilities in the device's config space. + * + * RETURN VALUE: + * -EINVAL if trying to enter a lower state than we're already in. + * 0 if we're already in the requested state. + * -EIO if device does not support PCI PM. + * 0 if we can successfully change the power state. */ + int -pci_set_power_state(struct pci_dev *dev, int new_state) +pci_set_power_state(struct pci_dev *dev, int state) { - u32 base[5], romaddr; - u16 pci_command, pwr_command; - u8 pci_latency, pci_cacheline; - int i, old_state; - int pm = pci_find_capability(dev, PCI_CAP_ID_PM); + int pm; + u16 pmcsr; - if (!pm) - return 0; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pwr_command); - old_state = pwr_command & PCI_PM_CTRL_STATE_MASK; - if (old_state == new_state) - return old_state; - DBG("PCI: %s goes from D%d to D%d\n", dev->slot_name, old_state, new_state); - if (old_state == 3) { - pci_read_config_word(dev, PCI_COMMAND, &pci_command); - pci_write_config_word(dev, PCI_COMMAND, pci_command & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); - for (i = 0; i < 5; i++) - pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, &base[i]); - pci_read_config_dword(dev, PCI_ROM_ADDRESS, &romaddr); - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); - pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &pci_cacheline); - pci_write_config_word(dev, pm + PCI_PM_CTRL, new_state); - for (i = 0; i < 5; i++) - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, base[i]); - pci_write_config_dword(dev, PCI_ROM_ADDRESS, romaddr); + /* bound the state we're entering */ + if (state > 3) state = 3; + + /* Validate current state: + * Can enter D0 from any state, but if we can only go deeper + * to sleep if we're already in a low power state + */ + if (state > 0 && dev->current_state > state) + return -EINVAL; + else if (dev->current_state == state) + return 0; /* we're already there */ + + /* find PCI PM capability in list */ + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + + /* abort if the device doesn't support PM capabilities */ + if (!pm) return -EIO; + + /* check if this device supports the desired state */ + if (state == 1 || state == 2) { + u16 pmc; + pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc); + if (state == 1 && !(pmc & PCI_PM_CAP_D1)) return -EIO; + else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) return -EIO; + } + + /* If we're in D3, force entire word to 0, since we can't access the + * PCI config space for the device + */ + if (dev->current_state == 3) + pmcsr = 0; + else { + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= state; + } + + /* enter specified state */ + pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + + dev->current_state = state; + + return 0; +} + +/** + * pci_save_state - save the PCI configuration space of a device before suspending + * @dev - PCI device that we're dealing with + * @buffer - buffer to hold config space context + * + * @buffer must be large enough to hold the entire PCI 2.2 config space + * (>= 64 bytes). + */ +int +pci_save_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + if (buffer) { + /* XXX: 100% dword access ok here? */ + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4,&buffer[i]); + } + return 0; +} + +/** + * pci_restore_state - Restore the saved state of a PCI device + * @dev - PCI device that we're dealing with + * @buffer - saved PCI config space + * + */ +int +pci_restore_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + + if (buffer) { + for (i = 0; i < 16; i++) + pci_write_config_dword(dev,i * 4, buffer[i]); + } + /* + * otherwise, write the context information we know from bootup. + * This works around a problem where warm-booting from Windows + * combined with a D3(hot)->D0 transition causes PCI config + * header data to be forgotten. + */ + else { + for (i = 0; i < 6; i ++) + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0 + (i * 4), + dev->resource[i].start); pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cacheline); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, pci_latency); - pci_write_config_word(dev, PCI_COMMAND, pci_command); - } else - pci_write_config_word(dev, pm + PCI_PM_CTRL, (pwr_command & ~PCI_PM_CTRL_STATE_MASK) | new_state); - return old_state; + } + return 0; } /** @@ -314,6 +385,48 @@ pci_disable_device(struct pci_dev *dev) } } +/** + * pci_enable_wake - enable device to generate PME# when suspended + * @dev - PCI device to operate on + * @enable - Flag to enable or disable generation + * + * Set the bits in the device's PM Capabilities to generate PME# when + * the system is suspended. + * + * -EIO is returned if device doesn't have PM Capabilities. + * -EINVAL is returned if device supports it, but can't generate wake events. + * 0 if operation is successful. + * + */ +int pci_enable_wake(struct pci_dev *dev, u32 state, int enable) +{ + int pm; + u16 value; + + /* find PCI PM capability in list */ + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!pm) return -EIO; /* this device cannot poweroff - up to bridge to cut power */ + + /* make sure device supports wake events (from any state) */ + pci_read_config_word(dev,pm+PCI_PM_PMC,&value); + + if (!(value & PCI_PM_CAP_PME_MASK)) return -EINVAL; /* doesn't support wake events */ + + /* + * XXX - We're assuming that device can generate wake events from whatever + * state it may be entering. + * We're not actually checking what state we're going into to. + */ + pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); + + if (enable) value |= PCI_PM_CTRL_PME_STATUS; + else value &= ~PCI_PM_CTRL_PME_STATUS; + + pci_write_config_word(dev, pm + PCI_PM_CTRL, value); + + return 0; +} + int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) { @@ -1272,40 +1385,41 @@ struct pci_bus * __init pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata * easily implement them (ie just have a suspend function that calls * the pci_set_power_state() function). */ -static int pci_pm_suspend_device(struct pci_dev *dev) +static int pci_pm_suspend_device(struct pci_dev *dev, u32 state) { + int error = 0; if (dev) { struct pci_driver *driver = dev->driver; if (driver && driver->suspend) - driver->suspend(dev); + error = driver->suspend(dev,state); } - return 0; + return error; } static int pci_pm_resume_device(struct pci_dev *dev) { + int error = 0; if (dev) { struct pci_driver *driver = dev->driver; if (driver && driver->resume) - driver->resume(dev); + error = driver->resume(dev); } - return 0; + return error; } - /* take care to suspend/resume bridges only once */ -static int pci_pm_suspend_bus(struct pci_bus *bus) +static int pci_pm_suspend_bus(struct pci_bus *bus, u32 state) { struct list_head *list; /* Walk the bus children list */ list_for_each(list, &bus->children) - pci_pm_suspend_bus(pci_bus_b(list)); + pci_pm_suspend_bus(pci_bus_b(list),state); /* Walk the device children list */ list_for_each(list, &bus->devices) - pci_pm_suspend_device(pci_dev_b(list)); + pci_pm_suspend_device(pci_dev_b(list),state); return 0; } @@ -1323,15 +1437,15 @@ static int pci_pm_resume_bus(struct pci_bus *bus) return 0; } -static int pci_pm_suspend(void) +static int pci_pm_suspend(u32 state) { struct list_head *list; struct pci_bus *bus; list_for_each(list, &pci_root_buses) { bus = pci_bus_b(list); - pci_pm_suspend_bus(bus); - pci_pm_suspend_device(bus->self); + pci_pm_suspend_bus(bus,state); + pci_pm_suspend_device(bus->self,state); } return 0; } @@ -1349,14 +1463,16 @@ static int pci_pm_resume(void) return 0; } -static int pci_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +static int +pci_pm_callback(struct pm_dev *pm_device, pm_request_t rqst, void *data) { switch (rqst) { case PM_SUSPEND: - return pci_pm_suspend(); + return pci_pm_suspend((u32)data); case PM_RESUME: return pci_pm_resume(); - } + default: break; + } return 0; } #endif @@ -1741,7 +1857,6 @@ static int __init pci_setup(char *str) __setup("pci=", pci_setup); - EXPORT_SYMBOL(pci_read_config_byte); EXPORT_SYMBOL(pci_read_config_word); EXPORT_SYMBOL(pci_read_config_dword); @@ -1761,7 +1876,6 @@ EXPORT_SYMBOL(pci_find_slot); EXPORT_SYMBOL(pci_find_subsys); EXPORT_SYMBOL(pci_set_master); EXPORT_SYMBOL(pci_set_dma_mask); -EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_assign_resource); EXPORT_SYMBOL(pci_register_driver); EXPORT_SYMBOL(pci_unregister_driver); @@ -1775,6 +1889,11 @@ EXPORT_SYMBOL(pci_insert_device); EXPORT_SYMBOL(pci_remove_device); #endif +EXPORT_SYMBOL(pci_set_power_state); +EXPORT_SYMBOL(pci_save_state); +EXPORT_SYMBOL(pci_restore_state); +EXPORT_SYMBOL(pci_enable_wake); + /* Obsolete functions */ EXPORT_SYMBOL(pcibios_present); diff --git a/drivers/pcmcia/pci_socket.c b/drivers/pcmcia/pci_socket.c index 86426f042098..1531f260b8c2 100644 --- a/drivers/pcmcia/pci_socket.c +++ b/drivers/pcmcia/pci_socket.c @@ -218,16 +218,18 @@ static void __devexit cardbus_remove (struct pci_dev *dev) dev->driver_data = 0; } -static void cardbus_suspend (struct pci_dev *dev) +static int cardbus_suspend (struct pci_dev *dev, u32 state) { pci_socket_t *socket = (pci_socket_t *) dev->driver_data; pcmcia_suspend_socket (socket->pcmcia_socket); + return 0; } -static void cardbus_resume (struct pci_dev *dev) +static int cardbus_resume (struct pci_dev *dev) { pci_socket_t *socket = (pci_socket_t *) dev->driver_data; pcmcia_resume_socket (socket->pcmcia_socket); + return 0; } diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index 9f1eba45aa93..8bc5afee96ea 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -221,7 +221,7 @@ static void *addresses[] = { (void *)0xc8000 }; #define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned )) -#endif USE_BIOS +#endif /* USE_BIOS */ /* possible i/o port addresses */ static unsigned short ports[] = @@ -244,7 +244,7 @@ struct signature { { "Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82 }, }; #define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature )) -#endif USE_BIOS +#endif /* USE_BIOS */ /* ============================================================ */ @@ -347,7 +347,7 @@ NCR53c406a_dma_residual (void) { return tmp; } -#endif USE_DMA +#endif /* USE_DMA */ #if USE_PIO static __inline__ int NCR53c406a_pio_read(unsigned char *request, @@ -455,7 +455,7 @@ static __inline__ int NCR53c406a_pio_write(unsigned char *request, } return 0; } -#endif USE_PIO +#endif /* USE_PIO */ int __init NCR53c406a_detect(Scsi_Host_Template * tpnt){ @@ -481,7 +481,7 @@ NCR53c406a_detect(Scsi_Host_Template * tpnt){ } DEB(printk("NCR53c406a BIOS found at %X\n", (unsigned int) bios_base);); -#endif USE_BIOS +#endif /* USE_BIOS */ #ifdef PORT_BASE if (!request_region(port_base, 0x10, "NCR53c406a")) /* ports already snatched */ @@ -512,7 +512,7 @@ NCR53c406a_detect(Scsi_Host_Template * tpnt){ } } } -#endif PORT_BASE +#endif /* PORT_BASE */ if(!port_base){ /* no ports found */ printk("NCR53c406a: no available ports found\n"); @@ -550,7 +550,7 @@ NCR53c406a_detect(Scsi_Host_Template * tpnt){ #if USE_DMA printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n"); goto err_release; -#endif USE_DMA +#endif /* USE_DMA */ } else { DEB(printk("NCR53c406a: Shouldn't get here!\n")); @@ -565,7 +565,7 @@ NCR53c406a_detect(Scsi_Host_Template * tpnt){ } DEB(printk("Allocated DMA channel %d\n", dma_chan)); -#endif USE_DMA +#endif /* USE_DMA */ tpnt->present = 1; tpnt->proc_name = "NCR53c406a"; @@ -820,8 +820,8 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ printk("\n"); #else printk(", pio=%02x\n", pio_status); -#endif USE_DMA -#endif NCR53C406A_DEBUG +#endif /* USE_DMA */ +#endif /* NCR53C406A_DEBUG */ if(int_reg & 0x80){ /* SCSI reset intr */ rtrc(3); @@ -840,7 +840,7 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ current_SC->scsi_done(current_SC); return; } -#endif USE_PIO +#endif /* USE_PIO */ if(status & 0x20) { /* Parity error */ printk("NCR53c406a: Warning: parity error!\n"); @@ -885,7 +885,7 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ #if USE_DMA /* No s/g support for DMA */ NCR53c406a_dma_write(current_SC->request_buffer, current_SC->request_bufflen); -#endif USE_DMA +#endif /* USE_DMA */ outb(TRANSFER_INFO | DMA_OP, CMD_REG); #if USE_PIO if (!current_SC->use_sg) /* Don't use scatter-gather */ @@ -900,7 +900,7 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ } } REG0; -#endif USE_PIO +#endif /* USE_PIO */ } break; @@ -914,7 +914,7 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ #if USE_DMA /* No s/g support for DMA */ NCR53c406a_dma_read(current_SC->request_buffer, current_SC->request_bufflen); -#endif USE_DMA +#endif /* USE_DMA */ outb(TRANSFER_INFO | DMA_OP, CMD_REG); #if USE_PIO if (!current_SC->use_sg) /* Don't use scatter-gather */ @@ -929,7 +929,7 @@ NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){ } } REG0; -#endif USE_PIO +#endif /* USE_PIO */ } break; @@ -1011,7 +1011,7 @@ static int irq_probe() return irq; } -#endif IRQ_LEV +#endif /* IRQ_LEV */ static void chip_init() { diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 0ae0dfa94306..11741b8bb24e 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -10100,7 +10100,7 @@ aic7xxx_detect(Scsi_Host_Template *template) } /* while(pdev=....) */ } /* for PCI_DEVICES */ } /* PCI BIOS present */ -#endif CONFIG_PCI +#endif /* CONFIG_PCI */ #if defined(__i386__) || defined(__alpha__) /* diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 73bd8029ea9e..8d2b7ea0b928 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -689,7 +689,7 @@ void print_sense_internal(const char * devclass, kdev_t dev) { int i, s; - int sense_class, valid, code; + int sense_class, valid, code, info; const char * error = NULL; sense_class = (sense_buffer[0] >> 4) & 0x07; @@ -701,11 +701,14 @@ void print_sense_internal(const char * devclass, if(s > SCSI_SENSE_BUFFERSIZE) s = SCSI_SENSE_BUFFERSIZE; - if (!valid) - printk("[valid=0] "); - printk("Info fld=0x%x, ", (int)((sense_buffer[3] << 24) | - (sense_buffer[4] << 16) | (sense_buffer[5] << 8) | - sense_buffer[6])); + info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) | + (sense_buffer[5] << 8) | sense_buffer[6]); + if (info || valid) { + printk("Info fld=0x%x", info); + if (!valid) /* info data not according to standard */ + printk(" (nonstd)"); + printk(", "); + } if (sense_buffer[2] & 0x80) printk( "FMK "); /* current command has read a filemark */ if (sense_buffer[2] & 0x40) diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 05edfcdae826..1c2960cd5955 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -1379,7 +1379,7 @@ static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request * SRpnt; int dev = TAPE_NR(STp->devt); - int expected __attribute__ ((__unused__)); + int expected = 0; int attempts = 1000 / skip; int flag = 1; long startwait = jiffies; @@ -3401,7 +3401,7 @@ static ssize_t osst_read(struct file * filp, char * buf, size_t count, loff_t *p if ((count % STp->block_size) != 0) { printk(KERN_WARNING - "osst%d:W: Read (%d bytes) not multiple of tape block size (%d%c).\n", dev, count, + "osst%d:W: Read (%Zd bytes) not multiple of tape block size (%d%c).\n", dev, count, STp->block_size<1024?STp->block_size:STp->block_size/1024, STp->block_size<1024?'b':'k'); } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index f47246a18a3a..dd249b9bf95a 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -2356,8 +2356,8 @@ void scsi_unregister_module(int module_type, void *ptr) /* The rest of these are not yet implemented. */ case MODULE_SCSI_CONST: case MODULE_SCSI_IOCTL: - break; default: + break; } return; } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index c3f71b84f992..25ba5a5cdd64 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -328,8 +328,8 @@ void scan_scsis(struct Scsi_Host *shpnt, } /* - * We need to increment the counter for this one device so we can track when - * things are quiet. + * We need to increment the counter for this one device so we can track + * when things are quiet. */ if (hardcoded == 1) { Scsi_Device *oldSDpnt = SDpnt; @@ -485,8 +485,8 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, SDpnt->type = -1; /* - * Assume that the device will have handshaking problems, and then fix this - * field later if it turns out it doesn't + * Assume that the device will have handshaking problems, and then fix + * this field later if it turns out it doesn't */ SDpnt->borken = 1; SDpnt->was_reset = 0; @@ -524,9 +524,21 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n", SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result)); + /* + * Now that we don't do TEST_UNIT_READY anymore, we must be prepared + * for media change conditions here, so cannot require zero result. + */ if (SRpnt->sr_result) { - scsi_release_request(SRpnt); - return 0; /* assume no peripheral if any sort of error */ + if ((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) != 0 && + (SRpnt->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION && + SRpnt->sr_sense_buffer[12] == 0x28 && + SRpnt->sr_sense_buffer[13] == 0) { + /* not-ready to ready transition - good */ + } else { + /* assume no peripheral if any other sort of error */ + scsi_release_request(SRpnt); + return 0; + } } /* diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 41d6222ce259..9b2c22e3b42e 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -861,8 +861,7 @@ static int sd_init_onedisk(int i) driver_byte(the_result) ); if (driver_byte(the_result) & DRIVER_SENSE) - printk("%s : extended sense code = %1x \n", - nbuff, SRpnt->sr_sense_buffer[2] & 0xf); + print_req_sense("sd", SRpnt); else printk("%s : sense not available. \n", nbuff); diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c index 734ada28ce69..67b97b1024fc 100644 --- a/drivers/scsi/sym53c8xx.c +++ b/drivers/scsi/sym53c8xx.c @@ -11564,6 +11564,7 @@ out_clrack: OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack)); return; out_stuck: + return; } diff --git a/drivers/sound/aci.c b/drivers/sound/aci.c index f05ef40c129b..5d8ac31e0ede 100644 --- a/drivers/sound/aci.c +++ b/drivers/sound/aci.c @@ -45,6 +45,8 @@ * ioctl bugfix, and integration of solo-mode into OSS-API, * added (OSS-limited) equalizer support, return value bugfix, * changed param aci_reset to reset, new params: ide, wss. + * 2001-04-20 Robert Siemer + * even more cleanups... */ #include @@ -95,17 +97,19 @@ MODULE_PARM(wss,"i"); MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested" " default: do nothing; for PCM1-pro only"); +#if DEBUG static void print_bits(unsigned char c) { int j; printk(KERN_DEBUG "aci: "); for (j=7; j>=0; j--) { - printk(KERN_DEBUG "%d", (c >> j) & 0x1); + printk("%d", (c >> j) & 0x1); } - printk(KERN_DEBUG "\n"); + printk("\n"); } +#endif /* * This busy wait code normally requires less than 15 loops and @@ -269,12 +273,12 @@ static int getvolume(caddr_t arg, int buf; /* left channel */ - if ((buf=aci_indexed_cmd(0xf0, left_index))<0) + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) return buf; vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0); /* right channel */ - if ((buf=aci_indexed_cmd(0xf0, right_index))<0) + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) return buf; vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; @@ -342,12 +346,12 @@ static int getequalizer(caddr_t arg, unsigned int vol; /* left channel */ - if ((buf=aci_indexed_cmd(0xf0, left_index))<0) + if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0) return buf; vol = eq_aci2oss(buf); /* right channel */ - if ((buf=aci_indexed_cmd(0xf0, right_index))<0) + if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0) return buf; vol |= eq_aci2oss(buf) << 8; @@ -399,7 +403,7 @@ static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) if (vol > 100) vol = 100; vol = SCALE(100, 3, vol); - if ((buf=aci_write_cmd(0x03, vol))<0) + if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0) return buf; aci_micpreamp = vol; vol = SCALE(3, 100, vol); @@ -416,7 +420,7 @@ static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) vol = 1; else vol = 0; - if ((buf=aci_write_cmd(0x0f, vol))<0) + if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0) return buf; aci_amp = vol; if (aci_amp) @@ -433,7 +437,7 @@ static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) /* unset solo when RECSRC for PCM is requested */ if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { vol = !(buf & SOUND_MASK_PCM); - if ((buf=aci_write_cmd(0xd2, vol))<0) + if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0) return buf; aci_solo = vol; } @@ -502,7 +506,8 @@ static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) case 'B': /* PCM12 */ case 'C': /* PCM20 radio */ if (aci_version >= 0xb0) { - if ((vol=aci_rw_cmd(0xf0, 0x00, -1))<0) + if ((vol=aci_rw_cmd(ACI_STATUS, + ACI_S_GENERAL, -1))<0) return vol; if (vol & 0x20) buf |= SOUND_MASK_PCM; @@ -555,7 +560,8 @@ static int aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) if (aci_idcode[1]=='B' || aci_idcode[1]=='C') { /* aci_micpreamp or ACI? */ if (aci_version >= 0xb0) { - if ((buf=aci_indexed_cmd(0xf0, 0x21))<0) + if ((buf=aci_indexed_cmd(ACI_STATUS, + ACI_S_READ_IGAIN))<0) return buf; } else @@ -612,17 +618,17 @@ static int __init attach_aci(void) /* force ACI into a known state */ for (i=0; i<3; i++) - if (aci_rw_cmd(0xdf, -1, -1)<0) + if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0) return -EFAULT; /* official this is one aci read call: */ - if ((aci_idcode[0]=aci_rw_cmd(0xf2, -1, -1))<0 || - (aci_idcode[1]=aci_rw_cmd(0xf2, -1, -1))<0) { + if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 || + (aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) { printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n", aci_port); return -EFAULT; } - if ((aci_version=aci_rw_cmd(0xf1, -1, -1))<0) { + if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) { printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n", aci_port); return -EFAULT; } @@ -656,22 +662,22 @@ static int __init attach_aci(void) if (reset) { /* first write()s after reset fail with my PCM20 */ - if (aci_rw_cmd(0xff, -1, -1)<0 || - aci_rw_cmd(0xdf, 0xdf, 0xdf)<0 || - aci_rw_cmd(0xdf, 0xdf, 0xdf)<0) + if (aci_rw_cmd(ACI_INIT, -1, -1)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 || + aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0) return -EBUSY; } /* the PCM20 is muted after reset (and reboot) */ - if (aci_rw_cmd(0x0d, 0x00, -1)<0) + if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0) return -EBUSY; if (ide>=0) - if (aci_rw_cmd(0xd0, !ide, -1)<0) + if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0) return -EBUSY; if (wss>=0 && aci_idcode[1]=='A') - if (aci_rw_cmd(0xd1, !!wss, -1)<0) + if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0) return -EBUSY; if (!request_region(aci_port, 3, "sound mixer (ACI)")) diff --git a/drivers/sound/aci.h b/drivers/sound/aci.h index f192cccbfef9..9060b5828456 100644 --- a/drivers/sound/aci.h +++ b/drivers/sound/aci.h @@ -6,9 +6,6 @@ extern int aci_idcode[2]; /* manufacturer and product ID */ extern int aci_version; /* ACI firmware version */ extern int aci_rw_cmd(int write1, int write2, int write3); -extern char * aci_radio_name; -extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize); - #define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1) #define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1) #define aci_read_cmd(a) aci_rw_cmd(a,-1, -1) @@ -19,15 +16,24 @@ extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasi #define RDS_REGISTER BUSY_REGISTER -#define RDS_STATUS 0x01 -#define RDS_STATIONNAME 0x02 -#define RDS_TEXT 0x03 -#define RDS_ALTFREQ 0x04 -#define RDS_TIMEDATE 0x05 -#define RDS_PI_CODE 0x06 -#define RDS_PTYTATP 0x07 -#define RDS_RESET 0x08 -#define RDS_RXVALUE 0x09 +#define ACI_SET_MUTE 0x0d +#define ACI_SET_POWERAMP 0x0f +#define ACI_SET_TUNERMUTE 0xa3 +#define ACI_SET_TUNERMONO 0xa4 +#define ACI_SET_IDE 0xd0 +#define ACI_SET_WSS 0xd1 +#define ACI_SET_SOLOMODE 0xd2 +#define ACI_WRITE_IGAIN 0x03 +#define ACI_WRITE_TUNE 0xa7 +#define ACI_READ_TUNERSTEREO 0xa8 +#define ACI_READ_TUNERSTATION 0xa9 +#define ACI_READ_VERSION 0xf1 +#define ACI_READ_IDCODE 0xf2 +#define ACI_INIT 0xff +#define ACI_STATUS 0xf0 +#define ACI_S_GENERAL 0x00 +#define ACI_S_READ_IGAIN 0x21 +#define ACI_ERROR_OP 0xdf /* * The following macro SCALE can be used to scale one integer volume @@ -48,8 +54,5 @@ extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasi #define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax)) -extern void __exit unload_aci_rds(void); -extern int __init attach_aci_rds(void); - #endif /* _ACI_H_ */ diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c index 30a5357a5d08..5e765b4d69f4 100644 --- a/drivers/sound/gus_wave.c +++ b/drivers/sound/gus_wave.c @@ -2102,6 +2102,7 @@ static void guswave_hw_control(int dev, unsigned char *event_rec) break; default: + break; } } @@ -3282,6 +3283,7 @@ static void do_loop_irq(int voice) break; default: + break; } restore_flags(flags); } @@ -3423,6 +3425,7 @@ void guswave_dma_irq(void) break; default: + break; } status = gus_look8(0x49); /* * Get Sampling IRQ Status diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c index 9cbcf3f62b75..6663f682cfb3 100644 --- a/drivers/sound/pss.c +++ b/drivers/sound/pss.c @@ -766,6 +766,7 @@ static int pss_coproc_open(void *dev_info, int sub_device) break; default: + break; } return 0; } diff --git a/drivers/sound/wf_midi.c b/drivers/sound/wf_midi.c index 23a2a8e166f8..490a1f42d1a8 100644 --- a/drivers/sound/wf_midi.c +++ b/drivers/sound/wf_midi.c @@ -231,6 +231,7 @@ wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) break; default: + break; } } else { mi->m_prev_status = midic; diff --git a/drivers/sound/ymfpci.c b/drivers/sound/ymfpci.c index 4ea2a111033e..6b2b6be35811 100644 --- a/drivers/sound/ymfpci.c +++ b/drivers/sound/ymfpci.c @@ -1829,6 +1829,7 @@ static int ymf_ioctl(struct inode *inode, struct file *file, * for instance we get SNDCTL_TMR_CONTINUE here. * XXX Is there sound_generic_ioctl() around? */ + break; } return -ENOTTY; } diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 9fa868624cf8..ea2bd825b5ea 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -709,8 +709,7 @@ static int __init acm_init(void) return -1; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index dffe94649229..114e0111e75e 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -3755,8 +3755,7 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr) static int __init usb_audio_init(void) { usb_register(&usb_audio_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c index 463320ae3d0f..dc8fe752d3d6 100644 --- a/drivers/usb/bluetooth.c +++ b/drivers/usb/bluetooth.c @@ -1,7 +1,7 @@ /* * bluetooth.c Version 0.10 * - * Copyright (c) 2000 Greg Kroah-Hartman + * Copyright (c) 2000, 2001 Greg Kroah-Hartman * Copyright (c) 2000 Mark Douglas Corner * * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B @@ -15,6 +15,10 @@ * - Added a buffer to the control_urb_pool which fixes a memory leak * when the device is removed from the system. * + * (2001/05/28) Version 0.9 gkh + * Fixed problem with bluetooth==NULL for bluetooth_read_bulk_callback + * which was found by both the CHECKER project and Mikko Rahkonen. + * * (08/04/2001) gb * Identify version on module load. * @@ -863,21 +867,6 @@ static void bluetooth_read_bulk_callback (struct urb *urb) unsigned int packet_size; int result; -#ifdef BTBUGGYHARDWARE - if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00) - && (data[2] == 0x00) && (data[3] == 0x00)) { - urb->actual_length = 0; - FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, - usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), - bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, - bluetooth_read_bulk_callback, bluetooth); - result = usb_submit_urb(bluetooth->read_urb); - if (result) - err (__FUNCTION__ " - failed resubmitting read urb, error %d", result); - - return; - } -#endif dbg(__FUNCTION__); @@ -904,6 +893,21 @@ static void bluetooth_read_bulk_callback (struct urb *urb) } printk ("\n"); } +#endif +#ifdef BTBUGGYHARDWARE + if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00) + && (data[2] == 0x00) && (data[3] == 0x00)) { + urb->actual_length = 0; + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, + usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), + bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, + bluetooth_read_bulk_callback, bluetooth); + result = usb_submit_urb(bluetooth->read_urb); + if (result) + err (__FUNCTION__ " - failed resubmitting read urb, error %d", result); + + return; + } #endif /* We add a packet type identifier to the beginning of each HCI frame. This makes the data in the tty look like a @@ -952,6 +956,9 @@ static void bluetooth_read_bulk_callback (struct urb *urb) } exit: + if (!bluetooth || !bluetooth->active) + return; + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, @@ -1314,8 +1321,7 @@ int usb_bluetooth_init(void) return -1; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c index 7d81d9e75b41..e9706158387d 100644 --- a/drivers/usb/dabusb.c +++ b/drivers/usb/dabusb.c @@ -837,8 +837,7 @@ static int __init dabusb_init (void) dbg("dabusb_init: driver registered"); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index 1f34afbfa4c1..f22ba7c27cfa 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -499,8 +499,7 @@ int __init usb_dc2xx_init(void) { if (usb_register (&camera_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c index 7c5dcfdd9e1f..beb40644bda1 100644 --- a/drivers/usb/dsbr100.c +++ b/drivers/usb/dsbr100.c @@ -342,8 +342,7 @@ static int __init dsbr100_init(void) warn("couldn't register video device"); return -EINVAL; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c index b34d7f313938..348b67fb7108 100644 --- a/drivers/usb/hid.c +++ b/drivers/usb/hid.c @@ -1559,8 +1559,7 @@ static struct usb_driver hid_driver = { static int __init hid_init(void) { usb_register(&hid_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/ibmcam.c b/drivers/usb/ibmcam.c index c2ebd0d6d1d8..952fa0c72884 100644 --- a/drivers/usb/ibmcam.c +++ b/drivers/usb/ibmcam.c @@ -3147,8 +3147,7 @@ static int __init usb_ibmcam_init(void) struct usb_ibmcam *ibmcam = &cams[u]; memset (ibmcam, 0, sizeof(struct usb_ibmcam)); } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return usb_register(&ibmcam_driver); } diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c index 9a0b7a7c3378..8a0379831ef8 100644 --- a/drivers/usb/mdc800.c +++ b/drivers/usb/mdc800.c @@ -932,8 +932,7 @@ int __init usb_mdc800_init (void) if (usb_register (&mdc800_usb_driver) < 0) goto cleanup_on_fail; - info (DRIVER_VERSION " " DRIVER_AUTHOR); - info (DRIVER_DESC); + info (DRIVER_VERSION ":" DRIVER_DESC); return 0; diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c index 5fd4b4ca8529..ed5cc1059673 100644 --- a/drivers/usb/microtek.c +++ b/drivers/usb/microtek.c @@ -1029,8 +1029,7 @@ int __init microtek_drv_init(void) MTS_DEBUG("driver registered.\n"); } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/net1080.c b/drivers/usb/net1080.c index 7de4520660ed..8fcb6165799d 100644 --- a/drivers/usb/net1080.c +++ b/drivers/usb/net1080.c @@ -599,7 +599,7 @@ static void rx_complete (struct urb *urb) } #ifdef VERBOSE dbg ("no read resubmitted"); -#endif VERBOSE +#endif /* VERBOSE */ } /*-------------------------------------------------------------------------*/ @@ -1111,8 +1111,7 @@ static int __init net1080_init (void) get_random_bytes (node_id, sizeof node_id); node_id [0] &= 0x7f; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 1918a20e350c..128941cdb7a9 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -3426,8 +3426,7 @@ static int __init usb_ov511_init(void) if (usb_register(&ov511_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 6d4262603d49..d421fbbbc231 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -935,8 +935,7 @@ static struct usb_driver pegasus_driver = { int __init pegasus_init(void) { - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return usb_register( &pegasus_driver ); } diff --git a/drivers/usb/plusb.c b/drivers/usb/plusb.c index 71cf5a2933aa..6d92d5dddc5d 100644 --- a/drivers/usb/plusb.c +++ b/drivers/usb/plusb.c @@ -1014,8 +1014,7 @@ static int __init plusb_init (void) dbg("plusb_init: driver registered"); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 7b81e9a80fec..80b9b15bd090 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -662,8 +662,7 @@ static int __init usblp_init(void) { if (usb_register(&usblp_driver)) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c index b9bfeaff491e..75cf1ece364d 100644 --- a/drivers/usb/rio500.c +++ b/drivers/usb/rio500.c @@ -492,8 +492,7 @@ int usb_rio_init(void) if (usb_register(&rio_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 3dc81ea8de9b..caa254fa3e7a 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -600,8 +600,7 @@ static int __init belkin_sa_init (void) usb_serial_register (&belkin_old_device); usb_serial_register (&peracom_device); usb_serial_register (&gocom232_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 0f9253930124..2a2d4463d76e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -2078,8 +2078,7 @@ static int __init digi_init (void) { usb_serial_register (&digi_acceleport_2_device); usb_serial_register (&digi_acceleport_4_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 9b1f13778cbf..0f4e3c664751 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -680,8 +680,7 @@ static int __init empeg_init (void) } } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 927d11610177..bb24cad7ae79 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -985,8 +985,7 @@ static int __init ftdi_sio_init (void) dbg(__FUNCTION__); usb_serial_register (&ftdi_sio_device); usb_serial_register (&ftdi_8U232AM_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 7ec0c37fd942..25bf96a476e5 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3037,8 +3037,7 @@ int __init edgeport_init(void) usb_serial_register (&edgeport_16dual_device); usb_serial_register (&edgeport_compat_id_device); usb_serial_register (&edgeport_8i_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index ed324e5d9a0c..c6e58676fd38 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -177,8 +177,7 @@ int keyspan_init (void) usb_serial_register (&keyspan_usa28x_device); usb_serial_register (&keyspan_usa49w_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 8042efbd089b..9915219b1254 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -817,8 +817,7 @@ static int __init keyspan_pda_init (void) { usb_serial_register (&keyspan_pda_fake_device); usb_serial_register (&keyspan_pda_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index bb9863e9ae23..4af643521b2f 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -870,8 +870,7 @@ static int __init mct_u232_init (void) usb_serial_register (&mct_u232_device); usb_serial_register (&mct_u232_sitecom_device); usb_serial_register (&mct_u232_du_h3sp_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index f81d87ad2a9e..67fda12e16f3 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -406,8 +406,7 @@ static void omninet_shutdown (struct usb_serial *serial) static int __init omninet_init (void) { usb_serial_register (&zyxel_omninet_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index a9120e8f2dad..bed06f08f929 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -1395,8 +1395,7 @@ int usb_serial_init(void) return -1; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index bbdec11760f5..ec9138f3c7c4 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -764,8 +764,7 @@ static int __init visor_init (void) } } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 586c5e299f16..b22939facefc 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -626,8 +626,7 @@ static int __init whiteheat_init (void) { usb_serial_register (&whiteheat_fake_device); usb_serial_register (&whiteheat_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index efd64876e59d..e8d81990e3ac 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -302,6 +302,8 @@ void usb_stor_show_sense( case 0x1C00: what="defect list not found"; break; case 0x2400: what="invalid field in CDB"; break; case 0x2703: what="associated write protect"; break; + case 0x2800: what="not ready to ready transition (media change?)"; + break; case 0x2903: what="bus device reset function occurred"; break; case 0x2904: what="device internal reset"; break; case 0x2B00: what="copy can't execute since host can't disconnect"; diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 723a3bfe0897..a5339ab2be4f 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -425,8 +425,7 @@ static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) /* Only go through the hoops if it's actually linked in */ if (list_empty(&qh->list)) { - uhci_free_qh(uhci, qh); - return; + goto list; } qh->urbp = NULL; @@ -444,6 +443,7 @@ static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) spin_unlock_irqrestore(&uhci->frame_list_lock, flags); +list: spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); /* Check to see if the remove list is empty. Set the IOC bit */ @@ -2922,15 +2922,17 @@ static void __devexit uhci_pci_remove(struct pci_dev *dev) } #ifdef CONFIG_PM -static void uhci_pci_suspend(struct pci_dev *dev) +static int uhci_pci_suspend(struct pci_dev *dev, u32 state) { reset_hc((struct uhci *) dev->driver_data); + return 0; } -static void uhci_pci_resume(struct pci_dev *dev) +static int uhci_pci_resume(struct pci_dev *dev) { reset_hc((struct uhci *) dev->driver_data); start_hc((struct uhci *) dev->driver_data); + return 0; } #endif @@ -2990,8 +2992,7 @@ static int __init uhci_hcd_init(void) if (retval) goto init_failed; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 7b1cd9fefd0a..65ae21175d0a 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -2774,8 +2774,7 @@ static int __init ohci_hcd_init (void) #ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier (&ohci_sleep_notifier); #endif - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return ret; } diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h index 8d464e084b4e..55be2faa0da3 100644 --- a/drivers/usb/usb-ohci.h +++ b/drivers/usb/usb-ohci.h @@ -116,7 +116,7 @@ struct td { dma_addr_t td_dma; dma_addr_t data_dma; __u32 unused2[2]; -} __attribute((aligned(16))); +} __attribute((aligned(32))); /* normally 16, iso needs 32 */ typedef struct td td_t; #define OHCI_ED_SKIP (1 << 14) @@ -562,7 +562,7 @@ static int ohci_mem_init (struct ohci *ohci) { ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev, sizeof (struct td), - 16 /* byte alignment */, + 32 /* byte alignment */, 0 /* no page-crossing issues */, GFP_KERNEL | OHCI_MEM_FLAGS); if (!ohci->td_cache) diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 8eb4f291647e..caa44f141df3 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -3108,8 +3108,7 @@ static int __init uhci_hcd_init (void) } #endif - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return retval; } diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 3a420cfb2b93..71b46cf3858f 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -2181,7 +2181,7 @@ int usb_new_device(struct usb_device *dev) dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; err = usb_get_device_descriptor(dev); - if (err < sizeof(dev->descriptor)) { + if (err < (signed)sizeof(dev->descriptor)) { if (err < 0) err("unable to get device descriptor (error=%d)", err); else diff --git a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c index 6581de4a8dfb..0985f01df3e8 100644 --- a/drivers/usb/usbkbd.c +++ b/drivers/usb/usbkbd.c @@ -280,8 +280,7 @@ static struct usb_driver usb_kbd_driver = { static int __init usb_kbd_init(void) { usb_register(&usb_kbd_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/usbmouse.c b/drivers/usb/usbmouse.c index 9b7e020e1546..42666c5e2c36 100644 --- a/drivers/usb/usbmouse.c +++ b/drivers/usb/usbmouse.c @@ -194,8 +194,7 @@ static struct usb_driver usb_mouse_driver = { static int __init usb_mouse_init(void) { usb_register(&usb_mouse_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c index 403024102698..b7e67b2e6c28 100644 --- a/drivers/usb/uss720.c +++ b/drivers/usb/uss720.c @@ -659,8 +659,7 @@ static int __init uss720_init(void) if (usb_register(&uss720_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c index b8708469e2b6..7c1eb7e5cddd 100644 --- a/drivers/usb/wacom.c +++ b/drivers/usb/wacom.c @@ -415,8 +415,7 @@ static struct usb_driver wacom_driver = { static int __init wacom_init(void) { usb_register(&wacom_driver); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/video/creatorfb.c b/drivers/video/creatorfb.c index a11c328b9350..0de4f555893c 100644 --- a/drivers/video/creatorfb.c +++ b/drivers/video/creatorfb.c @@ -1,4 +1,4 @@ -/* $Id: creatorfb.c,v 1.34 2001/03/16 10:22:02 davem Exp $ +/* $Id: creatorfb.c,v 1.35 2001/06/08 21:48:37 davem Exp $ * creatorfb.c: Creator/Creator3D frame buffer driver * * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) @@ -69,6 +69,15 @@ #define FFB_DAC_POFF 0x00400000UL #define FFB_PROM_POFF 0x00000000UL #define FFB_EXP_POFF 0x00200000UL +#define FFB_DFB422A_POFF 0x09000000UL +#define FFB_DFB422AD_POFF 0x09800000UL +#define FFB_DFB24B_POFF 0x0a000000UL +#define FFB_DFB422B_POFF 0x0b000000UL +#define FFB_DFB422BD_POFF 0x0b800000UL +#define FFB_SFB16Z_POFF 0x0c800000UL +#define FFB_SFB8Z_POFF 0x0c000000UL +#define FFB_SFB422_POFF 0x0d000000UL +#define FFB_SFB422D_POFF 0x0d800000UL /* Draw operations */ #define FFB_DRAWOP_DOT 0x00 @@ -330,6 +339,15 @@ static struct sbus_mmap_map ffb_mmap_map[] = { { FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 }, { FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 }, { FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 }, + { FFB_DFB422A_VOFF, FFB_DFB422A_POFF, 0x0800000 }, + { FFB_DFB422AD_VOFF, FFB_DFB422AD_POFF, 0x0800000 }, + { FFB_DFB24B_VOFF, FFB_DFB24B_POFF, 0x1000000 }, + { FFB_DFB422B_VOFF, FFB_DFB422B_POFF, 0x0800000 }, + { FFB_DFB422BD_VOFF, FFB_DFB422BD_POFF, 0x0800000 }, + { FFB_SFB16Z_VOFF, FFB_SFB16Z_POFF, 0x0800000 }, + { FFB_SFB8Z_VOFF, FFB_SFB8Z_POFF, 0x0800000 }, + { FFB_SFB422_VOFF, FFB_SFB422_POFF, 0x0800000 }, + { FFB_SFB422D_VOFF, FFB_SFB422D_POFF, 0x0800000 }, { 0, 0, 0 } }; diff --git a/fs/buffer.c b/fs/buffer.c index c4f1ca6462fd..e052447f4e44 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -646,8 +646,8 @@ void __invalidate_buffers(kdev_t dev, int destroy_dirty_buffers) /* Another device? */ if (bh->b_dev != dev) continue; - /* Part of a mapping? */ - if (bh->b_page->mapping) + /* Not hashed? */ + if (!bh->b_pprev) continue; if (buffer_locked(bh)) { atomic_inc(&bh->b_count); @@ -711,6 +711,9 @@ void set_blocksize(kdev_t dev, int size) bh_next = bh->b_next_free; if (bh->b_dev != dev || bh->b_size == size) continue; + /* Unhashed? */ + if (!bh->b_pprev) + continue; if (buffer_locked(bh)) { atomic_inc(&bh->b_count); spin_unlock(&lru_list_lock); diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 5622686366f3..0f3d5969f651 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -1,6 +1,6 @@ /* devfs (Device FileSystem) driver. - Copyright (C) 1998-2000 Richard Gooch + Copyright (C) 1998-2001 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -481,6 +481,23 @@ Simplified interface to . Work sponsored by SGI. v0.102 + 20010519 Richard Gooch + Ensure terminates string for root entry. + Exported to modules. + 20010520 Richard Gooch + Make send events to devfsd. + Cleaned up option processing in . + 20010521 Richard Gooch + Fixed bugs in handling symlinks: could leak or cause Oops. + 20010522 Richard Gooch + Cleaned up directory handling by separating fops. + v0.103 + 20010601 Richard Gooch + Fixed handling of inverted options in . + v0.104 + 20010604 Richard Gooch + Adjusted to account for fix. + v0.105 */ #include #include @@ -515,7 +532,7 @@ #include #include -#define DEVFS_VERSION "0.102 (20000622)" +#define DEVFS_VERSION "0.105 (20010604)" #define DEVFS_NAME "devfs" @@ -560,7 +577,7 @@ #define OPTION_NONE 0x00 #define OPTION_SHOW 0x01 -#define OPTION_NOMOUNT 0x02 +#define OPTION_MOUNT 0x02 #define OPTION_ONLY 0x04 #define OOPS(format, args...) {printk (format, ## args); \ @@ -694,9 +711,9 @@ static unsigned int devfs_debug = DEBUG_NONE; #endif #ifdef CONFIG_DEVFS_MOUNT -static unsigned int boot_options = OPTION_NONE; +static unsigned int boot_options = OPTION_MOUNT; #else -static unsigned int boot_options = OPTION_NOMOUNT; +static unsigned int boot_options = OPTION_NONE; #endif /* Forward function declarations */ @@ -1392,11 +1409,10 @@ static void unregister (struct devfs_entry *de) de->u.fcb.ops = NULL; return; } - if ( S_ISLNK (de->mode) ) + if (S_ISLNK (de->mode) && de->registered) { de->registered = FALSE; - if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); - de->u.symlink.linkname = NULL; + kfree (de->u.symlink.linkname); return; } if ( S_ISFIFO (de->mode) ) @@ -1441,22 +1457,9 @@ void devfs_unregister (devfs_handle_t de) unregister (de); } /* End Function devfs_unregister */ - -/** - * devfs_mk_symlink Create a symbolic link in the devfs namespace. - * @dir: The handle to the parent devfs directory entry. If this is %NULL the - * new name is relative to the root of the devfs. - * @name: The name of the entry. - * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). - * @link: The destination name. - * @handle: The handle to the symlink entry is written here. This may be %NULL. - * @info: An arbitrary pointer which will be associated with the entry. - * - * Returns 0 on success, else a negative error code is returned. - */ - -int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, - const char *link, devfs_handle_t *handle, void *info) +static int devfs_do_symlink (devfs_handle_t dir, const char *name, + unsigned int flags, const char *link, + devfs_handle_t *handle, void *info) { int is_new; unsigned int linklength; @@ -1466,16 +1469,16 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, if (handle != NULL) *handle = NULL; if (name == NULL) { - printk ("%s: devfs_mk_symlink(): NULL name pointer\n", DEVFS_NAME); + printk ("%s: devfs_do_symlink(): NULL name pointer\n", DEVFS_NAME); return -EINVAL; } #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_REGISTER) - printk ("%s: devfs_mk_symlink(%s)\n", DEVFS_NAME, name); + printk ("%s: devfs_do_symlink(%s)\n", DEVFS_NAME, name); #endif if (link == NULL) { - printk ("%s: devfs_mk_symlink(): NULL link pointer\n", DEVFS_NAME); + printk ("%s: devfs_do_symlink(): NULL link pointer\n", DEVFS_NAME); return -EINVAL; } linklength = strlen (link); @@ -1484,7 +1487,7 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, if (de == NULL) return -ENOMEM; if (!S_ISLNK (de->mode) && de->registered) { - printk ("%s: devfs_mk_symlink(): non-link entry already exists\n", + printk ("%s: devfs_do_symlink(): non-link entry already exists\n", DEVFS_NAME); return -EEXIST; } @@ -1504,7 +1507,6 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, } /* Have to create/update */ if (de->registered) return -EEXIST; - de->registered = TRUE; if ( ( newname = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL ) { struct devfs_entry *parent = de->parent; @@ -1518,11 +1520,39 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, kfree (de); return -ENOMEM; } - if (de->u.symlink.linkname != NULL) kfree (de->u.symlink.linkname); de->u.symlink.linkname = newname; memcpy (de->u.symlink.linkname, link, linklength); de->u.symlink.linkname[linklength] = '\0'; de->u.symlink.length = linklength; + de->registered = TRUE; + return 0; +} /* End Function devfs_do_symlink */ + + +/** + * devfs_mk_symlink Create a symbolic link in the devfs namespace. + * @dir: The handle to the parent devfs directory entry. If this is %NULL the + * new name is relative to the root of the devfs. + * @name: The name of the entry. + * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). + * @link: The destination name. + * @handle: The handle to the symlink entry is written here. This may be %NULL. + * @info: An arbitrary pointer which will be associated with the entry. + * + * Returns 0 on success, else a negative error code is returned. + */ + +int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags, + const char *link, devfs_handle_t *handle, void *info) +{ + int err; + devfs_handle_t de; + + if (handle != NULL) *handle = NULL; + err = devfs_do_symlink (dir, name, flags, link, &de, info); + if (err) return err; + if (handle != NULL) *handle = de; + devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT); return 0; } /* End Function devfs_mk_symlink */ @@ -1737,10 +1767,10 @@ int devfs_generate_path (devfs_handle_t de, char *path, int buflen) if (de == NULL) return -EINVAL; if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */ - if (de->parent == NULL) return buflen; /* Don't prepend root */ + path[buflen - 1] = '\0'; + if (de->parent == NULL) return buflen - 1; /* Don't prepend root */ pos = buflen - de->namelen - 1; memcpy (path + pos, de->name, de->namelen); - path[buflen - 1] = '\0'; for (de = de->parent; de->parent != NULL; de = de->parent) { if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG; @@ -1999,84 +2029,56 @@ int devfs_unregister_blkdev (unsigned int major, const char *name) static int __init devfs_setup (char *str) { - while ( (*str != '\0') && !isspace (*str) ) + static struct + { + char *name; + unsigned int mask; + unsigned int *opt; + } devfs_options_tab[] __initdata = { #ifdef CONFIG_DEVFS_DEBUG - if (strncmp (str, "dall", 4) == 0) - { - devfs_debug_init |= DEBUG_ALL; - str += 4; - } - else if (strncmp (str, "dmod", 4) == 0) - { - devfs_debug_init |= DEBUG_MODULE_LOAD; - str += 4; - } - else if (strncmp (str, "dreg", 4) == 0) - { - devfs_debug_init |= DEBUG_REGISTER; - str += 4; - } - else if (strncmp (str, "dunreg", 6) == 0) - { - devfs_debug_init |= DEBUG_UNREGISTER; - str += 6; - } - else if (strncmp (str, "diread", 6) == 0) - { - devfs_debug_init |= DEBUG_I_READ; - str += 6; - } - else if (strncmp (str, "dchange", 7) == 0) - { - devfs_debug_init |= DEBUG_SET_FLAGS; - str += 7; - } - else if (strncmp (str, "diwrite", 7) == 0) - { - devfs_debug_init |= DEBUG_I_WRITE; - str += 7; - } - else if (strncmp (str, "dimknod", 7) == 0) - { - devfs_debug_init |= DEBUG_I_MKNOD; - str += 7; - } - else if (strncmp (str, "dilookup", 8) == 0) - { - devfs_debug_init |= DEBUG_I_LOOKUP; - str += 8; - } - else if (strncmp (str, "diunlink", 8) == 0) - { - devfs_debug_init |= DEBUG_I_UNLINK; - str += 8; - } - else + {"dall", DEBUG_ALL, &devfs_debug_init}, + {"dmod", DEBUG_MODULE_LOAD, &devfs_debug_init}, + {"dreg", DEBUG_REGISTER, &devfs_debug_init}, + {"dunreg", DEBUG_UNREGISTER, &devfs_debug_init}, + {"diread", DEBUG_I_READ, &devfs_debug_init}, + {"dchange", DEBUG_SET_FLAGS, &devfs_debug_init}, + {"diwrite", DEBUG_I_WRITE, &devfs_debug_init}, + {"dimknod", DEBUG_I_MKNOD, &devfs_debug_init}, + {"dilookup", DEBUG_I_LOOKUP, &devfs_debug_init}, + {"diunlink", DEBUG_I_UNLINK, &devfs_debug_init}, #endif /* CONFIG_DEVFS_DEBUG */ - if (strncmp (str, "show", 4) == 0) - { - boot_options |= OPTION_SHOW; - str += 4; - } - else if (strncmp (str, "only", 4) == 0) - { - boot_options |= OPTION_ONLY; - str += 4; - } - else if (strncmp (str, "mount", 5) == 0) + {"show", OPTION_SHOW, &boot_options}, + {"only", OPTION_ONLY, &boot_options}, + {"mount", OPTION_MOUNT, &boot_options}, + }; + + while ( (*str != '\0') && !isspace (*str) ) + { + int i, found = 0, invert = 0; + + if (strncmp (str, "no", 2) == 0) { - boot_options &= ~OPTION_NOMOUNT; - str += 5; + invert = 1; + str += 2; } - else if (strncmp (str, "nomount", 7) == 0) + for (i = 0; i < sizeof (devfs_options_tab); i++) { - boot_options |= OPTION_NOMOUNT; - str += 7; + int len = strlen (devfs_options_tab[i].name); + + if (strncmp (str, devfs_options_tab[i].name, len) == 0) + { + if (invert) + *devfs_options_tab[i].opt &= ~devfs_options_tab[i].mask; + else + *devfs_options_tab[i].opt |= devfs_options_tab[i].mask; + str += len; + found = 1; + break; + } } - else - return 0; - if (*str != ',') return 0; + if (!found) return 0; /* No match */ + if (*str != ',') return 0; /* No more options */ ++str; } return 1; @@ -2103,6 +2105,7 @@ EXPORT_SYMBOL(devfs_get_first_child); EXPORT_SYMBOL(devfs_get_next_sibling); EXPORT_SYMBOL(devfs_auto_unregister); EXPORT_SYMBOL(devfs_get_unregister_slave); +EXPORT_SYMBOL(devfs_get_name); EXPORT_SYMBOL(devfs_register_chrdev); EXPORT_SYMBOL(devfs_register_blkdev); EXPORT_SYMBOL(devfs_unregister_chrdev); @@ -2125,15 +2128,15 @@ static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, const char *name, unsigned namelen, char buf[STRING_LENGTH]) { - int pos; + int pos = STRING_LENGTH - namelen - 1; if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) ) return -ENOENT; if ( is_devfsd_or_child (fs_info) ) return -ENOENT; if (namelen >= STRING_LENGTH) return -ENAMETOOLONG; - memcpy (buf + STRING_LENGTH - namelen - 1, name, namelen); + memcpy (buf + pos, name, namelen); buf[STRING_LENGTH - 1] = '\0'; - pos = devfs_generate_path (parent, buf, STRING_LENGTH - namelen - 1); + if (parent->parent != NULL) pos = devfs_generate_path (parent, buf, pos); if (pos < 0) return pos; buf[STRING_LENGTH - namelen - 2] = '/'; if ( !devfsd_notify_one (buf + pos, DEVFSD_NOTIFY_LOOKUP, 0, @@ -2229,6 +2232,7 @@ static int get_removable_partition (struct devfs_entry *dir, const char *name, static struct inode_operations devfs_iops; static struct inode_operations devfs_dir_iops; static struct file_operations devfs_fops; +static struct file_operations devfs_dir_fops; static struct inode_operations devfs_symlink_iops; static void devfs_read_inode (struct inode *inode) @@ -2273,7 +2277,11 @@ static void devfs_read_inode (struct inode *inode) } else if ( S_ISFIFO (de->inode.mode) ) inode->i_fop = &def_fifo_fops; else if ( S_ISREG (de->inode.mode) ) inode->i_size = de->u.fcb.u.file.size; - else if ( S_ISDIR (de->inode.mode) ) inode->i_op = &devfs_dir_iops; + else if ( S_ISDIR (de->inode.mode) ) + { + inode->i_op = &devfs_dir_iops; + inode->i_fop = &devfs_dir_fops; + } else if ( S_ISLNK (de->inode.mode) ) { inode->i_op = &devfs_symlink_iops; @@ -2399,12 +2407,6 @@ static struct inode *get_vfs_inode (struct super_block *sb, /* File operations for device entries follow */ -static ssize_t devfs_read (struct file *file, char *buf, size_t len, loff_t *ppos) -{ - if ( S_ISDIR (file->f_dentry->d_inode->i_mode) ) return -EISDIR; - return -EINVAL; -} /* End Function devfs_read */ - static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) { int err, count; @@ -2413,11 +2415,6 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) struct devfs_entry *parent, *de; struct inode *inode = file->f_dentry->d_inode; - if ( !S_ISDIR (inode->i_mode) ) - { - printk ("%s: readdir(): inode is not a directory\n", DEVFS_NAME); - return -ENOTDIR; - } fs_info = inode->i_sb->u.generic_sbp; parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode); if ( (long) file->f_pos < 0 ) return -EINVAL; @@ -2538,7 +2535,12 @@ out: static struct file_operations devfs_fops = { - read: devfs_read, + open: devfs_open, +}; + +static struct file_operations devfs_dir_fops = +{ + read: generic_read_dir, readdir: devfs_readdir, open: devfs_open, }; @@ -2723,12 +2725,6 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) /* Set up the dentry operations before anything else, to ensure cleaning up on any error */ dentry->d_op = &devfs_dops; - if (dir == NULL) - { - printk ("%s: lookup(): NULL directory inode\n", DEVFS_NAME); - return ERR_PTR (-ENOTDIR); - } - if ( !S_ISDIR (dir->i_mode) ) return ERR_PTR (-ENOTDIR); memset (txt, 0, STRING_LENGTH); memcpy (txt, dentry->d_name.name, (dentry->d_name.len >= STRING_LENGTH) ? @@ -2845,13 +2841,12 @@ static int devfs_unlink (struct inode *dir, struct dentry *dentry) } #endif - if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; - if (!dentry || !dentry->d_inode) return -ENOENT; de = get_devfs_entry_from_vfs_inode (dentry->d_inode); if (de == NULL) return -ENOENT; if (!de->registered) return -ENOENT; de->registered = FALSE; de->hide = TRUE; + if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname); free_dentries (de); return 0; } /* End Function devfs_unlink */ @@ -2864,17 +2859,16 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry, struct devfs_entry *parent, *de; struct inode *inode; - if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; fs_info = dir->i_sb->u.generic_sbp; /* First try to get the devfs entry for this directory */ parent = get_devfs_entry_from_vfs_inode (dir); if (parent == NULL) return -EINVAL; if (!parent->registered) return -ENOENT; - err = devfs_mk_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE, + err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE, symname, &de, NULL); #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_DISABLED) - printk ("%s: symlink(): errcode from : %d\n", + printk ("%s: symlink(): errcode from : %d\n", DEVFS_NAME, err); #endif if (err < 0) return err; @@ -2904,9 +2898,7 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) struct inode *inode; mode = (mode & ~S_IFMT) | S_IFDIR; - if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; fs_info = dir->i_sb->u.generic_sbp; - /* We are allowed to create the directory */ /* First try to get the devfs entry for this directory */ parent = get_devfs_entry_from_vfs_inode (dir); if (parent == NULL) return -EINVAL; @@ -2956,9 +2948,7 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry) struct devfs_entry *de, *child; struct inode *inode = dentry->d_inode; - if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL; - if (inode == dir) return -EPERM; fs_info = dir->i_sb->u.generic_sbp; de = get_devfs_entry_from_vfs_inode (inode); if (de == NULL) return -ENOENT; @@ -3000,11 +2990,7 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, } #endif - if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; fs_info = dir->i_sb->u.generic_sbp; - if ( !S_ISBLK (mode) && !S_ISCHR (mode) && !S_ISFIFO (mode) && - !S_ISSOCK (mode) ) return -EPERM; - /* We are allowed to create the node */ /* First try to get the devfs entry for this directory */ parent = get_devfs_entry_from_vfs_inode (dir); if (parent == NULL) return -EINVAL; @@ -3079,12 +3065,6 @@ static int devfs_follow_link (struct dentry *dentry, struct nameidata *nd) static struct inode_operations devfs_iops = { - link: devfs_link, - unlink: devfs_unlink, - symlink: devfs_symlink, - mkdir: devfs_mkdir, - rmdir: devfs_rmdir, - mknod: devfs_mknod, setattr: devfs_notify_change, }; @@ -3362,7 +3342,7 @@ void __init mount_devfs_fs (void) { int err; - if ( (boot_options & OPTION_NOMOUNT) ) return; + if ( !(boot_options & OPTION_MOUNT) ) return; err = do_mount ("none", "/dev", "devfs", 0, ""); if (err == 0) printk ("Mounted devfs on /dev\n"); else printk ("Warning: unable to mount devfs, err: %d\n", err); diff --git a/fs/devfs/util.c b/fs/devfs/util.c index 05b681b6c564..a25a42161116 100644 --- a/fs/devfs/util.c +++ b/fs/devfs/util.c @@ -1,6 +1,6 @@ /* devfs (Device FileSystem) utilities. - Copyright (C) 1999-2000 Richard Gooch + Copyright (C) 1999-2001 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -33,6 +33,8 @@ 20000622 Richard Gooch Took account of interface change to . Took account of interface change to . + 20010519 Richard Gooch + Documentation cleanup. */ #include #include @@ -132,9 +134,10 @@ EXPORT_SYMBOL(devfs_register_tape); /** * devfs_register_series - Register a sequence of device entries. - * @dir: The handle to the parent devfs directory entry. If this is %NULL the - * new names are relative to the root of the devfs. + * @dir: The handle to the parent devfs directory entry. If this is %NULL + * the new names are relative to the root of the devfs. * @format: The printf-style format string. A single "\%u" is allowed. + * @num_entries: The number of entries to register. * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). * @major: The major number. Not needed for regular files. * @minor_start: The starting minor number. Not needed for regular files. @@ -142,9 +145,9 @@ EXPORT_SYMBOL(devfs_register_tape); * @ops: The &file_operations or &block_device_operations structure. * This must not be externally deallocated. * @info: An arbitrary pointer which will be written to the private_data - * field of the &file structure passed to the device driver. You can set - * this to whatever you like, and change it once the file is opened (the next - * file opened will not see this change). + * field of the &file structure passed to the device driver. You + * can set this to whatever you like, and change it once the file + * is opened (the next file opened will not see this change). */ void devfs_register_series (devfs_handle_t dir, const char *format, diff --git a/fs/dquot.c b/fs/dquot.c index 166126a75685..75d0d2b1b099 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -325,7 +325,7 @@ void clear_dquot(struct dquot *dquot) memset(&dquot->dq_dqb, 0, sizeof(struct dqblk)); } -void invalidate_dquots(kdev_t dev, short type) +static void invalidate_dquots(kdev_t dev, short type) { struct dquot *dquot, *next; int need_restart; @@ -651,8 +651,6 @@ static int dqinit_needed(struct inode *inode, short type) { int cnt; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) - return 0; if (is_quotafile(inode)) return 0; if (type != -1) @@ -1022,9 +1020,6 @@ void dquot_initialize(struct inode *inode, short type) unsigned int id = 0; short cnt; - if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) && - !S_ISLNK(inode->i_mode)) - return; lock_kernel(); /* We don't want to have quotas on quota files - nasty deadlocks possible */ if (is_quotafile(inode)) { @@ -1388,7 +1383,7 @@ static inline void reset_enable_flags(struct quota_mount_options *dqopt, short t } /* Function in inode.c - remove pointers to dquots in icache */ -extern void remove_dquot_ref(kdev_t, short); +extern void remove_dquot_ref(struct super_block *, short); /* * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) @@ -1413,7 +1408,7 @@ int quota_off(struct super_block *sb, short type) reset_enable_flags(dqopt, cnt); /* Note: these are blocking operations */ - remove_dquot_ref(sb->s_dev, cnt); + remove_dquot_ref(sb, cnt); invalidate_dquots(sb->s_dev, cnt); /* Wait for any pending IO - remove me as soon as invalidate is more polite */ diff --git a/fs/inode.c b/fs/inode.c index 0922cf06f5c3..eb92db84a07a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1164,14 +1164,13 @@ void update_atime (struct inode *inode) void put_dquot_list(struct list_head *); int remove_inode_dquot_ref(struct inode *, short, struct list_head *); -void remove_dquot_ref(kdev_t dev, short type) +void remove_dquot_ref(struct super_block *sb, short type) { - struct super_block *sb = get_super(dev); struct inode *inode; struct list_head *act_head; LIST_HEAD(tofree_head); - if (!sb || !sb->dq_op) + if (!sb->dq_op) return; /* nothing to do */ /* We have to be protected against other CPUs */ diff --git a/fs/namei.c b/fs/namei.c index f19c2f346664..8a6c100d2b53 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -15,21 +15,15 @@ */ #include -#include -#include -#include +#include +#include #include #include -#include #include - -#include -#include -#include -#include -#include +#include #include +#include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e998b8452aa7..68513e6afbdc 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -73,7 +73,7 @@ typedef struct { struct file *file; struct page *page; unsigned long page_index; - unsigned page_offset; + u32 *ptr; u64 target; struct nfs_entry *entry; decode_dirent_t decode; @@ -100,18 +100,17 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); void *buffer = kmap(page); - int plus = NFS_USE_READDIRPLUS(inode); int error; dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); again: error = NFS_PROTO(inode)->readdir(inode, cred, desc->entry->cookie, buffer, - NFS_SERVER(inode)->dtsize, plus); + NFS_SERVER(inode)->dtsize, desc->plus); /* We requested READDIRPLUS, but the server doesn't grok it */ if (desc->plus && error == -ENOTSUPP) { NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; - plus = 0; + desc->plus = 0; goto again; } if (error < 0) @@ -135,6 +134,26 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) return -EIO; } +static inline +int dir_decode(nfs_readdir_descriptor_t *desc) +{ + u32 *p = desc->ptr; + p = desc->decode(p, desc->entry, desc->plus); + if (IS_ERR(p)) + return PTR_ERR(p); + desc->ptr = p; + return 0; +} + +static inline +void dir_page_release(nfs_readdir_descriptor_t *desc) +{ + kunmap(desc->page); + page_cache_release(desc->page); + desc->page = NULL; + desc->ptr = NULL; +} + /* * Given a pointer to a buffer that has already been filled by a call * to readdir, find the next entry. @@ -147,18 +166,10 @@ static inline int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) { struct nfs_entry *entry = desc->entry; - char *start = kmap(page), - *p = start; int loop_count = 0, - status = 0; + status; - for(;;) { - p = (char *)desc->decode((u32*)p, entry, desc->plus); - if (IS_ERR(p)) { - status = PTR_ERR(p); - break; - } - desc->page_offset = p - start; + while((status = dir_decode(desc)) == 0) { dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); if (entry->prev_cookie == desc->target) break; @@ -167,7 +178,6 @@ int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page) schedule(); } } - kunmap(page); dfprintk(VFS, "NFS: find_dirent() returns %d\n", status); return status; } @@ -181,17 +191,12 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) { struct inode *inode = desc->file->f_dentry->d_inode; struct page *page; - unsigned long index = desc->page_index; int status; dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); - if (desc->page) { - page_cache_release(desc->page); - desc->page = NULL; - } - - page = read_cache_page(&inode->i_data, index, + desc->plus = NFS_USE_READDIRPLUS(inode); + page = read_cache_page(&inode->i_data, desc->page_index, (filler_t *)nfs_readdir_filler, desc); if (IS_ERR(page)) { status = PTR_ERR(page); @@ -201,12 +206,11 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) goto read_error; /* NOTE: Someone else may have changed the READDIRPLUS flag */ - desc->plus = NFS_USE_READDIRPLUS(inode); + desc->page = page; + desc->ptr = kmap(page); status = find_dirent(desc, page); - if (status >= 0) - desc->page = page; - else - page_cache_release(page); + if (status < 0) + dir_page_release(desc); out: dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); return status; @@ -224,8 +228,8 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) static inline int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) { - int res = 0; int loop_count = 0; + int res; dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); for (;;) { @@ -233,7 +237,6 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) if (res != -EAGAIN) break; /* Align to beginning of next page */ - desc->page_offset = 0; desc->page_index ++; if (loop_count++ > 200) { loop_count = 0; @@ -253,11 +256,9 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, { struct file *file = desc->file; struct nfs_entry *entry = desc->entry; - char *start = kmap(desc->page), - *p = start + desc->page_offset; unsigned long fileid; int loop_count = 0, - res = 0; + res; dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); @@ -270,23 +271,16 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, if (res < 0) break; file->f_pos = desc->target = entry->cookie; - p = (char *)desc->decode((u32 *)p, entry, desc->plus); - if (IS_ERR(p)) { - if (PTR_ERR(p) == -EAGAIN) { - desc->page_offset = 0; - desc->page_index ++; - } + if (dir_decode(desc) != 0) { + desc->page_index ++; break; } - desc->page_offset = p - start; if (loop_count++ > 200) { loop_count = 0; schedule(); } } - kunmap(desc->page); - page_cache_release(desc->page); - desc->page = NULL; + dir_page_release(desc); dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); return res; @@ -312,49 +306,40 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; - u32 *p; - int status = -EIO; + int status; dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); - if (desc->page) { - page_cache_release(desc->page); - desc->page = NULL; - } page = alloc_page(GFP_HIGHUSER); if (!page) { status = -ENOMEM; goto out; } - p = kmap(page); - status = NFS_PROTO(inode)->readdir(inode, cred, desc->target, p, - NFS_SERVER(inode)->dtsize, 0); - if (status >= 0) { - p = desc->decode(p, desc->entry, 0); - if (IS_ERR(p)) - status = PTR_ERR(p); - else + desc->page = page; + desc->ptr = kmap(page); + desc->error = NFS_PROTO(inode)->readdir(inode, cred, desc->target, + desc->ptr, + NFS_SERVER(inode)->dtsize, + desc->plus); + if (desc->error >= 0) { + if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = desc->target; - } - kunmap(page); + } else + status = -EIO; if (status < 0) goto out_release; - desc->page_index = 0; - desc->page_offset = 0; - desc->page = page; status = nfs_do_filldir(desc, dirent, filldir); /* Reset read descriptor so it searches the page cache from * the start upon the next call to readdir_search_pagecache() */ desc->page_index = 0; - desc->page_offset = 0; memset(desc->entry, 0, sizeof(*desc->entry)); out: dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); return status; out_release: - page_cache_release(page); + dir_page_release(desc); goto out; } @@ -392,16 +377,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) res = readdir_search_pagecache(desc); if (res == -EBADCOOKIE) { /* This means either end of directory */ - if (desc->entry->cookie == desc->target) { - res = 0; - break; + if (desc->entry->cookie != desc->target) { + /* Or that the server has 'lost' a cookie */ + res = uncached_readdir(desc, dirent, filldir); + if (res >= 0) + continue; } - /* Or that the server has 'lost' a cookie */ - res = uncached_readdir(desc, dirent, filldir); - if (res >= 0) - continue; - } - if (res < 0) + res = 0; + break; + } else if (res < 0) break; res = nfs_do_filldir(desc, dirent, filldir); @@ -410,8 +394,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) break; } } - if (desc->page) - page_cache_release(desc->page); if (desc->error < 0) return desc->error; if (res < 0) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e88bd823d61f..2d79aefe1940 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -100,6 +100,8 @@ nfs_read_inode(struct inode * inode) inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; + /* We can't support UPDATE_ATIME(), since the server will reset it */ + inode->i_flags |= S_NOATIME; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; NFS_FLAGS(inode) = 0; @@ -973,12 +975,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) NFS_CACHE_CTIME(inode) = fattr->ctime; inode->i_ctime = nfs_time_to_secs(fattr->ctime); - /* If we've been messing around with atime, don't - * update it. Save the server value in NFS_CACHE_ATIME. - */ + NFS_CACHE_ATIME(inode) = fattr->atime; - if (time_before(inode->i_atime, nfs_time_to_secs(fattr->atime))) - inode->i_atime = nfs_time_to_secs(fattr->atime); + inode->i_atime = nfs_time_to_secs(fattr->atime); NFS_CACHE_MTIME(inode) = new_mtime; inode->i_mtime = nfs_time_to_secs(new_mtime); diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 3db60bcc2d8a..0153c66f149a 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -213,7 +213,8 @@ exp_export(struct nfsctl_export *nxp) err = -EINVAL; if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) || - inode->i_sb->s_op->read_inode == NULL) { + (inode->i_sb->s_op->read_inode == NULL + && inode->i_sb->s_op->fh_to_dentry == NULL)) { dprintk("exp_export: export of invalid fs type.\n"); goto finish; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ea8e9a6e3432..86b82cc5ffdc 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -134,7 +134,11 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 struct inode *inode; struct list_head *lp; struct dentry *result; + if (ino == 0) + return ERR_PTR(-ESTALE); inode = iget(sb, ino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); if (is_bad_inode(inode) || (generation && inode->i_generation != generation) ) { @@ -169,10 +173,34 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 return ERR_PTR(-ENOMEM); } result->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(result); /* so a dput won't loose it */ return result; } +static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh, + int len, int fhtype, int parent) +{ + if (sb->s_op->fh_to_dentry) + return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent); + switch (fhtype) { + case 1: + if (len < 2) + break; + if (parent) + break; + return nfsd_iget(sb, fh[0], fh[1]); + + case 2: + if (len < 3) + break; + if (parent) + return nfsd_iget(sb,fh[2],0); + return nfsd_iget(sb,fh[0],fh[1]); + default: break; + } + return ERR_PTR(-EINVAL); +} + + /* this routine links an IS_ROOT dentry into the dcache tree. It gains "parent" * as a parent and "name" as a name * It should possibly go in dcache.c @@ -196,7 +224,7 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) * make it an IS_ROOT instead */ spin_lock(&dcache_lock); - list_del(&tdentry->d_child); + list_del_init(&tdentry->d_child); tdentry->d_parent = tdentry; spin_unlock(&dcache_lock); d_rehash(target); @@ -270,10 +298,10 @@ struct dentry *nfsd_findparent(struct dentry *child) } spin_unlock(&dcache_lock); if (pdentry == NULL) { - pdentry = d_alloc_root(igrab(tdentry->d_inode)); + pdentry = d_alloc_root(tdentry->d_inode); if (pdentry) { + igrab(tdentry->d_inode); pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED; - d_rehash(pdentry); } } if (pdentry == NULL) @@ -347,11 +375,10 @@ static struct dentry *splice(struct dentry *child, struct dentry *parent) * connection if made. */ static struct dentry * -find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, int needpath) +find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath) { struct dentry *dentry, *result = NULL; struct dentry *tmp; - int found =0; int err = -ESTALE; /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free) * dcache path ever exists, as otherwise two partial paths might get @@ -367,7 +394,7 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, */ retry: down(&sb->s_nfsd_free_path_sem); - result = nfsd_iget(sb, ino, generation); + result = nfsd_get_dentry(sb, datap, len, fhtype, 0); if (IS_ERR(result) || !(result->d_flags & DCACHE_NFSD_DISCONNECTED) || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) { @@ -384,16 +411,12 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, /* It's a directory, or we are required to confirm the file's * location in the tree. */ - dprintk("nfs_fh: need to look harder for %d/%ld\n",sb->s_dev,ino); + dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]); - found = 0; if (!S_ISDIR(result->d_inode->i_mode)) { nfsdstats.fh_nocache_nondir++; - if (dirino == 0) - goto err_result; /* don't know how to find parent */ - else { /* need to iget dirino and make sure this inode is in that directory */ - dentry = nfsd_iget(sb, dirino, 0); + dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto err_result; @@ -402,8 +425,6 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, || !S_ISDIR(dentry->d_inode->i_mode)) { goto err_dentry; } - if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) - found = 1; tmp = splice(result, dentry); err = PTR_ERR(tmp); if (IS_ERR(tmp)) @@ -413,15 +434,13 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, d_drop(result); dput(result); result = tmp; - /* If !found, then this is really weird, but it shouldn't hurt */ } - } } else { nfsdstats.fh_nocache_dir++; dentry = dget(result); } - while(!found) { + while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) { /* LOOP INVARIANT */ /* haven't found a place in the tree yet, but we do have a free path * from dentry down to result, and dentry is a directory. @@ -440,12 +459,9 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, goto err_dentry; } - if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) - found = 1; - tmp = splice(dentry, pdentry); if (tmp != dentry) { - /* Something wrong. We need to drop thw whole dentry->result path + /* Something wrong. We need to drop the whole dentry->result path * whatever it was */ struct dentry *d; @@ -580,31 +596,23 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) case 0: dentry = dget(exp->ex_dentry); break; - case 1: - if ((data_left-=2)<0) goto out; - dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - datap[0], datap[1], - 0, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); - break; - case 2: - if ((data_left-=3)<0) goto out; + default: dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - datap[0], datap[1], - datap[2], + datap, data_left, fh->fh_fileid_type, !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); - break; - default: goto out; } } else { - + __u32 tfh[3]; + tfh[0] = fh->ofh_ino; + tfh[1] = fh->ofh_generation; + tfh[2] = fh->ofh_dirino; dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb, - fh->ofh_ino, fh->ofh_generation, - fh->ofh_dirino, + tfh, 3, fh->ofh_dirino?2:1, !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } if (IS_ERR(dentry)) { - error = nfserrno(PTR_ERR(dentry)); + if (PTR_ERR(dentry) != EINVAL) + error = nfserrno(PTR_ERR(dentry)); goto out; } #ifdef NFSD_PARANOIA @@ -709,9 +717,20 @@ inline int _fh_update(struct dentry *dentry, struct svc_export *exp, __u32 **datapp, int maxsize) { __u32 *datap= *datapp; + struct super_block *sb = dentry->d_inode->i_sb; + if (dentry == exp->ex_dentry) return 0; - /* if super_operations provides dentry_to_fh lookup, should use that */ + + if (sb->s_op->dentry_to_fh) { + int need_parent = !S_ISDIR(dentry->d_inode->i_mode) && + !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); + + int type = sb->s_op->dentry_to_fh(dentry, datap, &maxsize, need_parent); + datap += maxsize; + *datapp = datap; + return type; + } *datap++ = ino_t_to_u32(dentry->d_inode->i_ino); *datap++ = dentry->d_inode->i_generation; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 887984424d2d..725b2a4f1601 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -418,11 +418,10 @@ void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size) blk_size[dev->major] = NULL; dev->part[first_minor].nr_sects = size; - /* No Such Agen^Wdevice or no minors to use for partitions */ + /* No such device or no minors to use for partitions */ if (!size || minors == 1) return; - blk_size[dev->major] = NULL; check_partition(dev, MKDEV(dev->major, first_minor), 1 + first_minor); /* diff --git a/fs/proc/array.c b/fs/proc/array.c index 6b08c3910d5f..a1eb02f157a5 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -421,13 +421,12 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned ++*total; if (!pte_present(page)) continue; + ptpage = pte_page(page); + if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage)) + continue; ++*pages; if (pte_dirty(page)) ++*dirty; - ptpage = pte_page(page); - if ((!VALID_PAGE(ptpage)) || - PageReserved(ptpage)) - continue; if (page_count(pte_page(page)) > 1) ++*shared; } while (address < end); diff --git a/fs/proc/base.c b/fs/proc/base.c index b9c0d02c7e88..90b78687d227 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -635,15 +635,14 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_ino = fake_ino(task->pid, ino); - inode->u.proc_i.file = NULL; + if (!task->pid) + goto out_unlock; + /* * grab the reference to task. */ - inode->u.proc_i.task = task; get_task_struct(task); - if (!task->pid) - goto out_unlock; - + inode->u.proc_i.task = task; inode->i_uid = 0; inode->i_gid = 0; if (ino == PROC_PID_INO || task->dumpable) { diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c index 321c98459cef..37354b0e1f76 100644 --- a/fs/reiserfs/fix_node.c +++ b/fs/reiserfs/fix_node.c @@ -936,6 +936,7 @@ static int get_empty_nodes( if (p_s_tb->FEB[p_s_tb->cur_blknum]) BUG(); + mark_buffer_journal_new(p_s_new_bh) ; p_s_tb->FEB[p_s_tb->cur_blknum++] = p_s_new_bh; } @@ -2719,12 +2720,6 @@ void unfix_nodes (struct tree_balance * tb) { int i; -#ifdef CONFIG_REISERFS_CHECK - if ( ! tb->vn_buf ) - reiserfs_panic (tb->tb_sb, - "PAP-16050: unfix_nodes: pointer to the virtual node is NULL"); -#endif - /* Release path buffers. */ pathrelse_and_restore (tb->tb_sb, tb->tb_path); @@ -2781,7 +2776,8 @@ void unfix_nodes (struct tree_balance * tb) } } #endif /* 0 */ - reiserfs_kfree (tb->vn_buf, tb->vn_buf_size, tb->tb_sb); + if (tb->vn_buf) + reiserfs_kfree (tb->vn_buf, tb->vn_buf_size, tb->tb_sb); } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index c2c3222791ba..c21e4b1805c4 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1163,7 +1163,7 @@ void reiserfs_read_inode2 (struct inode * inode, void *p) return; } if (retval != ITEM_FOUND) { - reiserfs_warning ("vs-13042: reiserfs_read_inode2: %K not found\n", &key); + /* a stale NFS handle can trigger this without it being an error */ pathrelse (&path_to_sd); make_bad_inode(inode) ; return; @@ -1185,23 +1185,86 @@ struct inode * reiserfs_iget (struct super_block * s, struct cpu_key * key) if (!inode) return inode ; - if (is_bad_inode (inode)) { - reiserfs_warning ("vs-13048: reiserfs_iget: " - "bad_inode. Stat data of (%lu %lu) not found\n", - key->on_disk_key.k_dir_id, key->on_disk_key.k_objectid); - iput (inode); - inode = 0; - } else if (comp_short_keys (INODE_PKEY (inode), key)) { - reiserfs_warning ("vs-13049: reiserfs_iget: " - "Looking for (%lu %lu), found inode of (%lu %lu)\n", - key->on_disk_key.k_dir_id, key->on_disk_key.k_objectid, - INODE_PKEY (inode)->k_dir_id, INODE_PKEY (inode)->k_objectid); + if (comp_short_keys (INODE_PKEY (inode), key) || is_bad_inode (inode)) { + /* either due to i/o error or a stale NFS handle */ iput (inode); inode = 0; } return inode; } +struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data, + int len, int fhtype, int parent) { + struct cpu_key key ; + struct inode *inode = NULL ; + struct list_head *lp; + struct dentry *result; + + if (fhtype < 2 || (parent && fhtype < 4)) + goto out ; + + if (! parent) { + /* this works for handles from old kernels because the default + ** reiserfs generation number is the packing locality. + */ + key.on_disk_key.k_objectid = data[0] ; + key.on_disk_key.k_dir_id = data[1] ; + inode = reiserfs_iget(sb, &key) ; + } else { + key.on_disk_key.k_objectid = data[2] ; + key.on_disk_key.k_dir_id = data[3] ; + inode = reiserfs_iget(sb, &key) ; + } +out: + if (!inode) + return ERR_PTR(-ESTALE) ; + + /* now to find a dentry. + * If possible, get a well-connected one + */ + spin_lock(&dcache_lock); + for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { + result = list_entry(lp,struct dentry, d_alias); + if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { + dget_locked(result); + result->d_vfs_flags |= DCACHE_REFERENCED; + spin_unlock(&dcache_lock); + iput(inode); + return result; + } + } + spin_unlock(&dcache_lock); + result = d_alloc_root(inode); + if (result == NULL) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + result->d_flags |= DCACHE_NFSD_DISCONNECTED; + return result; + +} + +int reiserfs_dentry_to_fh(struct dentry *dentry, __u32 *data, int *lenp, int need_parent) { + struct inode *inode = dentry->d_inode ; + int maxlen = *lenp; + + if (maxlen < 2) + return 255 ; + + data[0] = inode->i_ino ; + data[1] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ; + *lenp = 2; + /* no room for directory info? return what we've stored so far */ + if (maxlen < 4 || ! need_parent) + return 2 ; + + inode = dentry->d_parent->d_inode ; + data[2] = inode->i_ino ; + data[3] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ; + *lenp = 4; + return 4; +} + // // initially this function was derived from minix or ext2's analog and diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index bf77724ef03c..396beb24a2d4 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -2553,6 +2553,7 @@ int journal_mark_freed(struct reiserfs_transaction_handle *th, struct super_bloc bh = get_hash_table(p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ; /* if it is journal new, we just remove it from this transaction */ if (bh && buffer_journal_new(bh)) { + mark_buffer_notjournal_new(bh) ; clear_prepared_bits(bh) ; cleaned = remove_from_transaction(p_s_sb, blocknr, cleaned) ; } else { diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index 19c375138007..a312999ee70c 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -1724,7 +1724,7 @@ int reiserfs_cut_from_item (struct reiserfs_transaction_handle *th, continue; reiserfs_warning ("PAP-5610: reiserfs_cut_from_item: item %K not found\n", p_s_item_key); - pathrelse (p_s_path); + unfix_nodes (&s_cut_balance); return (n_ret_value == IO_ERROR) ? -EIO : -ENOENT; } /* while */ @@ -1994,12 +1994,14 @@ int reiserfs_paste_into_item (struct reiserfs_transaction_handle *th, while ( (retval = fix_nodes(M_PASTE, &s_paste_balance, NULL, p_c_body)) == REPEAT_SEARCH ) { /* file system changed while we were in the fix_nodes */ retval = search_for_position_by_key (th->t_super, p_s_key, p_s_search_path); - if (retval == IO_ERROR) - return -EIO; + if (retval == IO_ERROR) { + retval = -EIO ; + goto error_out ; + } if (retval == POSITION_FOUND) { reiserfs_warning ("PAP-5710: reiserfs_paste_into_item: entry or pasted byte (%K) exists", p_s_key); - pathrelse (p_s_search_path); - return -EEXIST; + retval = -EEXIST ; + goto error_out ; } #ifdef CONFIG_REISERFS_CHECK @@ -2013,9 +2015,11 @@ int reiserfs_paste_into_item (struct reiserfs_transaction_handle *th, do_balance(&s_paste_balance, NULL/*ih*/, p_c_body, M_PASTE); return 0; } - + retval = (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO; +error_out: + /* this also releases the path */ unfix_nodes(&s_paste_balance); - return (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO; + return retval ; } @@ -2040,14 +2044,15 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th, while ( (retval = fix_nodes(M_INSERT, &s_ins_balance, p_s_ih, p_c_body)) == REPEAT_SEARCH) { /* file system changed while we were in the fix_nodes */ retval = search_item (th->t_super, key, p_s_path); - if (retval == IO_ERROR) - return -EIO; - + if (retval == IO_ERROR) { + retval = -EIO; + goto error_out ; + } if (retval == ITEM_FOUND) { reiserfs_warning ("PAP-5760: reiserfs_insert_item: " "key %K already exists in the tree\n", key); - pathrelse (p_s_path); - return -EEXIST; + retval = -EEXIST ; + goto error_out; } } @@ -2057,8 +2062,11 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th, return 0; } + retval = (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO; +error_out: + /* also releases the path */ unfix_nodes(&s_ins_balance); - return (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO; + return retval; } diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index a9f2c6d962e7..cf590d6f44c2 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -149,6 +149,9 @@ struct super_operations reiserfs_sops = statfs: reiserfs_statfs, remount_fs: reiserfs_remount, + fh_to_dentry: reiserfs_fh_to_dentry, + dentry_to_fh: reiserfs_dentry_to_fh, + }; /* this was (ext2)parse_options */ diff --git a/fs/super.c b/fs/super.c index b6bfcfe9d053..de1e501fd84e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1242,14 +1242,14 @@ static int do_loopback(struct nameidata *nd, char *old_name) if (err) return err; + down(&mount_sem); err = -ENOMEM; mnt = clone_mnt(old_nd.mnt, old_nd.dentry); if (mnt) { - down(&mount_sem); err = graft_tree(mnt, nd); - up(&mount_sem); mntput(mnt); } + up(&mount_sem); path_release(&old_nd); return err; } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 1fb7beaa8e2d..786fad71a881 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -442,7 +442,7 @@ static struct super_block *sysv_read_super(struct super_block *sb, brelse(bh); printk("SysV FS: cannot read superblock in %d byte mode\n", sb->sv_block_size); goto failed; - superblock_ok: + superblock_ok:; } } else { /* Switch to 512 block size. Unfortunately, we have to diff --git a/include/asm-alpha/delay.h b/include/asm-alpha/delay.h index 5d3329f4dcce..ed2e38b81c00 100644 --- a/include/asm-alpha/delay.h +++ b/include/asm-alpha/delay.h @@ -40,6 +40,7 @@ __udelay(unsigned long usecs, unsigned long lpj) } #ifdef CONFIG_SMP +#include /* for smp_processor_id */ #define udelay(u) __udelay((u), cpu_data[smp_processor_id()].loops_per_jiffy) #else #define udelay(u) __udelay((u), loops_per_jiffy) diff --git a/include/asm-i386/mca_dma.h b/include/asm-i386/mca_dma.h index a9e780cc4345..4b3b526c5a3f 100644 --- a/include/asm-i386/mca_dma.h +++ b/include/asm-i386/mca_dma.h @@ -199,4 +199,4 @@ static __inline__ void mca_set_dma_mode(unsigned int dmanr, unsigned int mode) outb(mode, MCA_DMA_REG_EXE); } -#endif MCA_DMA_H +#endif /* MCA_DMA_H */ diff --git a/include/asm-m68k/linux_logo.h b/include/asm-m68k/linux_logo.h index 7cc75c94af38..61a2d0f387b3 100644 --- a/include/asm-m68k/linux_logo.h +++ b/include/asm-m68k/linux_logo.h @@ -26,880 +26,899 @@ #ifdef CONFIG_MAC -#define LINUX_LOGO_COLORS 95 +#define __HAVE_ARCH_LINUX_LOGO + +#define LINUX_LOGO_COLORS 185 #ifdef INCLUDE_LINUX_LOGO_DATA unsigned char linux_logo_red[] __initdata = { - 0x02, 0x82, 0xEA, 0x42, 0xC2, 0x82, 0xE2, 0xA2, - 0xDA, 0xC2, 0x22, 0x62, 0xB2, 0x92, 0xD2, 0x8A, - 0xB2, 0xFA, 0xDA, 0x32, 0x72, 0x12, 0xF2, 0x52, - 0xF2, 0xEA, 0xFA, 0xAA, 0xCA, 0x9A, 0xE2, 0xAA, - 0x8A, 0xEA, 0xD2, 0x92, 0xEA, 0xDA, 0x2A, 0x6A, - 0xDA, 0xBA, 0xD2, 0x52, 0x7A, 0x2A, 0x5A, 0x0A, - 0x6A, 0xEA, 0xE2, 0xC6, 0x96, 0xF2, 0x3A, 0x1A, - 0xB2, 0xBA, 0xF2, 0xDA, 0x0A, 0x86, 0x4A, 0xCA, - 0x8A, 0xE2, 0xA6, 0xDA, 0x66, 0xBA, 0x92, 0xDA, - 0xA2, 0xB6, 0x76, 0x12, 0xF2, 0xFA, 0xEA, 0xAE, - 0xCE, 0x9E, 0xB2, 0x8E, 0xF2, 0xD2, 0xA2, 0x6E, - 0xBE, 0xD6, 0x7E, 0x5E, 0xC2, 0xFA, 0x3A + 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, + 0x12, 0x00, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, + 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x02, 0xfa, + 0xf2, 0xf6, 0xe7, 0x74, 0x65, 0x7b, 0xea, 0xdd, + 0xd6, 0x5e, 0xbe, 0x5a, 0xe2, 0xda, 0xee, 0xb6, + 0xce, 0x65, 0x6e, 0x6a, 0xd2, 0xc6, 0x90, 0xca, + 0x9e, 0xbb, 0xb2, 0x8a, 0xa2, 0x9a, 0x86, 0xc3, + 0xfd, 0xae, 0x3e, 0xaa, 0x95, 0x80, 0x76, 0x79, + 0x62, 0x36, 0x9a, 0xe2, 0xec, 0xe1, 0xb8, 0xd7, + 0xaf, 0x25, 0xbc, 0xc0, 0xef, 0xea, 0xe8, 0xe8, + 0xf5, 0xf1, 0xda, 0xd3, 0x79, 0xdb, 0xf4, 0xf6, + 0xf6, 0xf6, 0xe2, 0x3d, 0xb4, 0xce, 0xe6, 0xee, + 0xf6, 0xa6, 0x68, 0xd8, 0xec, 0xf5, 0xc6, 0xc8, + 0x9c, 0x89, 0xd2, 0xee, 0xcb, 0xb9, 0xd2, 0x66, + 0x5e, 0x8b, 0xbe, 0xa8, 0xd5, 0xca, 0xb6, 0xae, + 0x9c, 0xc5, 0x8d, 0xbe, 0xbe, 0xb2, 0x9a, 0xa8, + 0x16, 0x12, 0x4a, 0x8e, 0xf2, 0xf6, 0xe4, 0xf1, + 0x26, 0x9a, 0xea, 0xf6, 0xe0, 0xd2, 0x9a, 0x2e, + 0x70, 0xd6, 0x46, 0x7c, 0xb4, 0x62, 0xd6, 0xa3, + 0x74, 0xa7, 0xa2, 0xca, 0xe0, 0xae, 0xbe, 0xce, + 0xa3, 0x8e, 0x6d, 0x8e, 0x32, 0xaf, 0x50, 0x9e, + 0x5b, 0x8a, 0x98, 0x82, 0x7a, 0x82, 0x56, 0x7c, + 0x8a, 0x56, 0x5e, 0x86, 0x6a, 0x52, 0x59, 0x64, + 0x5e, }; unsigned char linux_logo_green[] __initdata = { - 0x02, 0x82, 0xC2, 0x42, 0x8A, 0x56, 0xE2, 0xA2, - 0xAA, 0xC2, 0x22, 0x62, 0x86, 0x92, 0x9E, 0x6E, - 0xB2, 0xB2, 0xD2, 0x32, 0x72, 0x12, 0xD2, 0x52, - 0xF2, 0xB2, 0xC2, 0xAA, 0xCA, 0x9A, 0xA2, 0x7E, - 0x8A, 0xCA, 0x92, 0x66, 0xEA, 0xB2, 0x2A, 0x6A, - 0xA2, 0xBA, 0xD2, 0x36, 0x7A, 0x1A, 0x5A, 0x0A, - 0x4A, 0xE6, 0xAE, 0xC6, 0x96, 0xBA, 0x3A, 0x1A, - 0xAA, 0x7A, 0xCA, 0xDA, 0x02, 0x86, 0x4A, 0x8A, - 0x5E, 0xE2, 0xA6, 0xAE, 0x66, 0x82, 0x92, 0x9A, - 0x72, 0xB6, 0x76, 0x12, 0xD2, 0xFA, 0xB2, 0xAE, - 0xCE, 0x9E, 0x7A, 0x8E, 0xCA, 0x92, 0x6A, 0x6E, - 0xBE, 0xD6, 0x7E, 0x5E, 0xC6, 0xBA, 0x3E + 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, + 0x12, 0x00, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, + 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x02, 0xfa, + 0xf2, 0xf6, 0xe7, 0x74, 0x65, 0x7b, 0xea, 0xdd, + 0xd6, 0x5e, 0xbe, 0x5a, 0xe2, 0xda, 0xee, 0xb6, + 0xce, 0x62, 0x6e, 0x6a, 0xd2, 0xc6, 0x90, 0xca, + 0x9e, 0xbb, 0xb2, 0x8a, 0xa2, 0x9a, 0x86, 0xc3, + 0xfd, 0xae, 0x3e, 0xaa, 0x95, 0x80, 0x62, 0x5c, + 0x4e, 0x26, 0x72, 0xaa, 0xba, 0xaf, 0x90, 0xae, + 0x92, 0x1a, 0xa4, 0x85, 0xb6, 0xbe, 0xc3, 0xc8, + 0xcf, 0xd0, 0xc2, 0xce, 0x57, 0xa2, 0xd6, 0xda, + 0xda, 0xd7, 0xb8, 0x2a, 0x7b, 0x91, 0xae, 0xca, + 0xda, 0xa6, 0x45, 0x9e, 0xb2, 0xd7, 0x9b, 0x90, + 0x76, 0x5c, 0xa2, 0xbe, 0xa6, 0x85, 0x96, 0x4e, + 0x46, 0x66, 0x92, 0x7a, 0x9a, 0x96, 0x9d, 0x9a, + 0x6b, 0x8a, 0x8d, 0x8e, 0xb2, 0xa6, 0x79, 0x7c, + 0x12, 0x0e, 0x36, 0x86, 0xba, 0xbe, 0xb8, 0xc4, + 0x1e, 0x8e, 0xae, 0xba, 0xb2, 0xa6, 0x7a, 0x20, + 0x64, 0xaa, 0x2f, 0x70, 0x85, 0x46, 0xa6, 0x6e, + 0x51, 0x72, 0x92, 0xa2, 0xa6, 0x87, 0x96, 0xa2, + 0x85, 0x7a, 0x6a, 0x6e, 0x22, 0x76, 0x36, 0x76, + 0x3c, 0x6e, 0x63, 0x53, 0x66, 0x62, 0x42, 0x50, + 0x56, 0x42, 0x56, 0x56, 0x56, 0x3e, 0x51, 0x52, + 0x56, }; unsigned char linux_logo_blue[] __initdata = { - 0x04, 0x84, 0x04, 0x44, 0x04, 0x04, 0xDC, 0xA4, - 0x0C, 0xC4, 0x1C, 0x64, 0x04, 0x8C, 0x04, 0x34, - 0xB4, 0x0C, 0xAC, 0x34, 0x74, 0x04, 0x0C, 0x4C, - 0xF4, 0x0C, 0x0C, 0xAC, 0xCC, 0x9C, 0x0C, 0x04, - 0x8C, 0x0C, 0x04, 0x04, 0xEC, 0x2C, 0x2C, 0x6C, - 0x04, 0xBC, 0xD4, 0x04, 0x7C, 0x04, 0x5C, 0x0C, - 0x04, 0xEC, 0x04, 0xC4, 0x94, 0x14, 0x3C, 0x1C, - 0xA4, 0x04, 0x24, 0xDC, 0x04, 0x84, 0x4C, 0x0C, - 0x04, 0xE4, 0xA4, 0x04, 0x64, 0x04, 0x94, 0x14, - 0x0C, 0xB4, 0x74, 0x14, 0x24, 0xFC, 0x14, 0xAC, - 0xCC, 0x9C, 0x0C, 0x8C, 0x14, 0x14, 0x04, 0x6C, - 0xBC, 0xD4, 0x7C, 0x5C, 0xD4, 0x14, 0x3C + 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, + 0x12, 0x01, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, + 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x06, 0xfa, + 0xf2, 0xf6, 0xe7, 0x74, 0x65, 0x7b, 0xea, 0xdd, + 0xd6, 0x5e, 0xbe, 0x5a, 0xe2, 0xda, 0xee, 0xb6, + 0xce, 0x59, 0x6e, 0x6a, 0xd2, 0xc6, 0x90, 0xca, + 0x9e, 0xbb, 0xb2, 0x8a, 0xa2, 0x9a, 0x86, 0xc3, + 0xfd, 0xae, 0x3e, 0xaa, 0x95, 0x80, 0x2e, 0x08, + 0x0a, 0x06, 0x0a, 0x0b, 0x0b, 0x0f, 0x0c, 0x0f, + 0x3d, 0x09, 0x73, 0x09, 0x0d, 0x0a, 0x10, 0x1e, + 0x2d, 0x13, 0x86, 0xba, 0x19, 0x0a, 0x36, 0x3c, + 0x26, 0x14, 0x0d, 0x06, 0x07, 0x0a, 0x0b, 0x0f, + 0x4a, 0xa6, 0x06, 0x0a, 0x0c, 0x2b, 0x0a, 0x0b, + 0x0a, 0x06, 0x0a, 0x0a, 0x11, 0x0b, 0x0a, 0x0a, + 0x1e, 0x0f, 0x0d, 0x0a, 0x0b, 0x22, 0x6a, 0x72, + 0x0b, 0x0b, 0x8d, 0x22, 0x90, 0x92, 0x3c, 0x2c, + 0x06, 0x06, 0x0e, 0x6a, 0x0e, 0x0e, 0x3e, 0x0e, + 0x0a, 0x5a, 0x0d, 0x0e, 0x3e, 0x0a, 0x2e, 0x06, + 0x4e, 0x36, 0x06, 0x58, 0x24, 0x06, 0x3a, 0x08, + 0x08, 0x07, 0x5e, 0x45, 0x0a, 0x32, 0x2e, 0x2a, + 0x43, 0x48, 0x5f, 0x2e, 0x06, 0x06, 0x07, 0x24, + 0x06, 0x32, 0x06, 0x06, 0x46, 0x2e, 0x22, 0x06, + 0x06, 0x1e, 0x4c, 0x06, 0x3a, 0x22, 0x42, 0x34, + 0x42, }; unsigned char linux_logo[] __initdata = { - 0x53, 0x3D, 0x40, 0x73, 0x71, 0x3B, 0x3B, 0x71, - 0x3D, 0x54, 0x73, 0x40, 0x73, 0x3D, 0x27, 0x71, - 0x40, 0x6A, 0x7A, 0x3D, 0x3B, 0x30, 0x30, 0x62, - 0x40, 0x6A, 0x21, 0x62, 0x78, 0x29, 0x49, 0x30, - 0x6F, 0x27, 0x54, 0x3D, 0x62, 0x27, 0x54, 0x66, - 0x71, 0x6F, 0x6F, 0x6F, 0x78, 0x53, 0x29, 0x29, - 0x53, 0x70, 0x53, 0x3D, 0x40, 0x73, 0x71, 0x3B, - 0x3B, 0x71, 0x3D, 0x54, 0x73, 0x40, 0x73, 0x3D, - 0x27, 0x71, 0x40, 0x6A, 0x7A, 0x3D, 0x3B, 0x30, - 0x30, 0x62, 0x40, 0x6A, 0x21, 0x62, 0x78, 0x29, - 0x71, 0x4C, 0x6A, 0x40, 0x71, 0x62, 0x27, 0x71, - 0x54, 0x66, 0x73, 0x40, 0x73, 0x3D, 0x3D, 0x40, - 0x6A, 0x77, 0x7A, 0x71, 0x30, 0x69, 0x6F, 0x71, - 0x54, 0x73, 0x3D, 0x30, 0x49, 0x30, 0x3B, 0x62, - 0x27, 0x3D, 0x54, 0x3D, 0x71, 0x71, 0x27, 0x62, - 0x62, 0x3B, 0x62, 0x27, 0x3B, 0x69, 0x69, 0x69, - 0x69, 0x30, 0x71, 0x4C, 0x6A, 0x40, 0x71, 0x62, - 0x27, 0x71, 0x54, 0x66, 0x73, 0x40, 0x73, 0x3D, - 0x3D, 0x40, 0x6A, 0x77, 0x7A, 0x71, 0x30, 0x69, - 0x6F, 0x71, 0x54, 0x73, 0x3D, 0x30, 0x49, 0x30, - 0x40, 0x34, 0x34, 0x40, 0x27, 0x6F, 0x62, 0x3D, - 0x54, 0x66, 0x40, 0x73, 0x66, 0x66, 0x2D, 0x5D, - 0x7A, 0x5D, 0x3D, 0x6F, 0x69, 0x69, 0x30, 0x27, - 0x27, 0x27, 0x62, 0x6F, 0x30, 0x3B, 0x62, 0x27, - 0x3D, 0x54, 0x2D, 0x54, 0x27, 0x71, 0x3D, 0x27, - 0x62, 0x62, 0x62, 0x62, 0x3B, 0x30, 0x6F, 0x6F, - 0x6F, 0x71, 0x40, 0x34, 0x34, 0x40, 0x27, 0x6F, - 0x62, 0x3D, 0x54, 0x66, 0x40, 0x73, 0x66, 0x66, - 0x2D, 0x5D, 0x7A, 0x5D, 0x3D, 0x6F, 0x69, 0x69, - 0x30, 0x27, 0x27, 0x27, 0x62, 0x6F, 0x30, 0x6F, - 0x4C, 0x77, 0x6A, 0x2D, 0x3B, 0x6F, 0x3B, 0x71, - 0x54, 0x66, 0x2D, 0x73, 0x66, 0x54, 0x73, 0x73, - 0x73, 0x3D, 0x62, 0x6F, 0x69, 0x30, 0x6F, 0x27, - 0x3D, 0x3D, 0x27, 0x62, 0x62, 0x62, 0x27, 0x71, - 0x54, 0x73, 0x73, 0x54, 0x3D, 0x3D, 0x20, 0x20, - 0x20, 0x20, 0x3B, 0x62, 0x3B, 0x62, 0x71, 0x71, - 0x3D, 0x2D, 0x4C, 0x77, 0x6A, 0x66, 0x3B, 0x6F, - 0x3B, 0x71, 0x54, 0x66, 0x2D, 0x73, 0x66, 0x54, - 0x73, 0x73, 0x73, 0x3D, 0x62, 0x6F, 0x69, 0x30, - 0x6F, 0x27, 0x3D, 0x3D, 0x27, 0x62, 0x62, 0x27, - 0x4C, 0x4C, 0x5D, 0x71, 0x30, 0x30, 0x3B, 0x27, - 0x54, 0x66, 0x73, 0x66, 0x66, 0x54, 0x54, 0x3D, - 0x27, 0x62, 0x6F, 0x30, 0x30, 0x6F, 0x27, 0x3D, - 0x54, 0x54, 0x3D, 0x71, 0x3D, 0x71, 0x3D, 0x3D, - 0x66, 0x73, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x71, 0x3D, - 0x66, 0x40, 0x4C, 0x4C, 0x5D, 0x71, 0x30, 0x30, - 0x3B, 0x27, 0x54, 0x66, 0x73, 0x66, 0x66, 0x54, - 0x54, 0x3D, 0x27, 0x62, 0x6F, 0x30, 0x30, 0x6F, - 0x27, 0x3D, 0x54, 0x54, 0x3D, 0x71, 0x71, 0x71, - 0x5D, 0x5D, 0x54, 0x62, 0x69, 0x69, 0x3B, 0x3D, - 0x66, 0x40, 0x73, 0x66, 0x54, 0x54, 0x71, 0x62, - 0x3B, 0x6F, 0x6F, 0x6F, 0x3B, 0x27, 0x3D, 0x2D, - 0x66, 0x54, 0x71, 0x71, 0x27, 0x71, 0x71, 0x3D, - 0x3D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, - 0x73, 0x40, 0x5D, 0x5D, 0x54, 0x62, 0x69, 0x69, - 0x3B, 0x3D, 0x66, 0x40, 0x73, 0x66, 0x54, 0x54, - 0x71, 0x62, 0x3B, 0x6F, 0x6F, 0x6F, 0x3B, 0x27, - 0x3D, 0x66, 0x66, 0x54, 0x71, 0x71, 0x27, 0x71, - 0x66, 0x73, 0x3D, 0x27, 0x6F, 0x6F, 0x62, 0x54, - 0x40, 0x21, 0x5D, 0x73, 0x3D, 0x27, 0x62, 0x6F, - 0x6F, 0x3B, 0x3B, 0x62, 0x27, 0x71, 0x3D, 0x3D, - 0x3D, 0x71, 0x62, 0x62, 0x62, 0x27, 0x27, 0x71, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x4F, 0x46, 0x2A, 0x20, 0x20, - 0x20, 0x73, 0x66, 0x73, 0x3D, 0x27, 0x6F, 0x6F, - 0x62, 0x54, 0x40, 0x21, 0x5D, 0x73, 0x3D, 0x27, - 0x62, 0x6F, 0x6F, 0x3B, 0x3B, 0x62, 0x27, 0x71, - 0x3D, 0x3D, 0x3D, 0x71, 0x62, 0x27, 0x62, 0x27, - 0x3D, 0x40, 0x66, 0x27, 0x3B, 0x3B, 0x71, 0x66, - 0x7A, 0x21, 0x40, 0x54, 0x27, 0x3B, 0x6F, 0x3B, - 0x62, 0x62, 0x27, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x38, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x38, - 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x6F, 0x3B, 0x62, 0x62, 0x27, 0x27, 0x27, 0x27, - 0x71, 0x3D, 0x71, 0x71, 0x71, 0x71, 0x3D, 0x3D, - 0x27, 0x40, 0x54, 0x62, 0x30, 0x30, 0x27, 0x40, - 0x7A, 0x5D, 0x54, 0x3D, 0x62, 0x30, 0x30, 0x3B, - 0x71, 0x3D, 0x71, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x30, 0x3B, 0x71, 0x3D, 0x71, 0x27, 0x71, 0x71, - 0x3D, 0x66, 0x73, 0x40, 0x73, 0x66, 0x2D, 0x66, - 0x30, 0x66, 0x71, 0x6F, 0x69, 0x6F, 0x54, 0x21, - 0x7A, 0x66, 0x3D, 0x3B, 0x6F, 0x6F, 0x3B, 0x71, - 0x54, 0x66, 0x3D, 0x4C, 0x44, 0x51, 0x44, 0x44, - 0x44, 0x38, 0x44, 0x38, 0x44, 0x38, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x38, 0x51, - 0x3B, 0x71, 0x54, 0x66, 0x3D, 0x3D, 0x3D, 0x2D, - 0x40, 0x40, 0x5D, 0x40, 0x73, 0x66, 0x66, 0x3D, - 0x69, 0x27, 0x3B, 0x30, 0x69, 0x3B, 0x73, 0x7A, - 0x21, 0x3D, 0x62, 0x3B, 0x6F, 0x3B, 0x27, 0x66, - 0x73, 0x73, 0x54, 0x4E, 0x44, 0x26, 0x26, 0x5B, - 0x26, 0x44, 0x44, 0x44, 0x44, 0x44, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x6D, 0x26, - 0x27, 0x66, 0x73, 0x73, 0x66, 0x2D, 0x66, 0x2D, - 0x66, 0x54, 0x54, 0x71, 0x27, 0x27, 0x27, 0x62, - 0x69, 0x3B, 0x6F, 0x6F, 0x62, 0x54, 0x40, 0x21, - 0x73, 0x27, 0x3B, 0x3B, 0x71, 0x54, 0x66, 0x66, - 0x73, 0x73, 0x2D, 0x37, 0x44, 0x51, 0x38, 0x38, - 0x44, 0x6D, 0x38, 0x6D, 0x38, 0x38, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x38, 0x6D, 0x38, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x38, 0x6D, 0x70, - 0x2D, 0x66, 0x73, 0x66, 0x66, 0x54, 0x54, 0x54, - 0x27, 0x62, 0x6F, 0x6F, 0x6F, 0x6F, 0x3B, 0x62, - 0x3B, 0x27, 0x27, 0x27, 0x3D, 0x40, 0x21, 0x40, - 0x54, 0x62, 0x3B, 0x71, 0x73, 0x5D, 0x40, 0x73, - 0x66, 0x66, 0x2D, 0x33, 0x6D, 0x26, 0x44, 0x4F, - 0x5E, 0x5E, 0x37, 0x5E, 0x37, 0x46, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x44, 0x26, 0x26, 0x44, - 0x70, 0x38, 0x26, 0x38, 0x38, 0x44, 0x6D, 0x53, - 0x40, 0x73, 0x66, 0x2D, 0x66, 0x73, 0x66, 0x54, - 0x27, 0x6F, 0x30, 0x69, 0x30, 0x6F, 0x62, 0x62, - 0x62, 0x71, 0x3D, 0x54, 0x73, 0x5D, 0x5D, 0x66, - 0x27, 0x62, 0x71, 0x54, 0x5D, 0x5D, 0x66, 0x3D, - 0x3D, 0x73, 0x40, 0x37, 0x44, 0x44, 0x51, 0x20, - 0x6F, 0x6F, 0x62, 0x27, 0x27, 0x20, 0x20, 0x20, - 0x4F, 0x20, 0x2A, 0x2A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x71, 0x3D, 0x73, 0x5D, - 0x40, 0x54, 0x27, 0x3B, 0x2D, 0x6D, 0x38, 0x3C, - 0x66, 0x3D, 0x54, 0x66, 0x40, 0x40, 0x73, 0x54, - 0x62, 0x6F, 0x30, 0x6F, 0x62, 0x71, 0x71, 0x71, - 0x62, 0x3D, 0x66, 0x73, 0x40, 0x5D, 0x73, 0x71, - 0x62, 0x27, 0x54, 0x73, 0x5D, 0x73, 0x27, 0x62, - 0x71, 0x40, 0x21, 0x37, 0x26, 0x51, 0x44, 0x4E, - 0x3B, 0x71, 0x2D, 0x73, 0x2D, 0x20, 0x20, 0x20, - 0x35, 0x73, 0x77, 0x4F, 0x6B, 0x20, 0x20, 0x4F, - 0x20, 0x73, 0x73, 0x73, 0x58, 0x20, 0x77, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2D, 0x40, 0x40, 0x5D, - 0x73, 0x71, 0x62, 0x27, 0x71, 0x38, 0x44, 0x49, - 0x27, 0x62, 0x3D, 0x40, 0x21, 0x40, 0x54, 0x27, - 0x3B, 0x6F, 0x3B, 0x71, 0x54, 0x66, 0x66, 0x3D, - 0x62, 0x54, 0x40, 0x21, 0x7A, 0x40, 0x3D, 0x62, - 0x62, 0x71, 0x66, 0x40, 0x66, 0x71, 0x62, 0x3B, - 0x54, 0x5D, 0x73, 0x23, 0x51, 0x26, 0x26, 0x30, - 0x3B, 0x3D, 0x66, 0x73, 0x66, 0x20, 0x20, 0x4F, - 0x58, 0x7C, 0x62, 0x34, 0x57, 0x20, 0x20, 0x20, - 0x73, 0x58, 0x49, 0x7C, 0x79, 0x73, 0x20, 0x4F, - 0x20, 0x20, 0x20, 0x20, 0x40, 0x21, 0x7A, 0x40, - 0x3D, 0x27, 0x62, 0x71, 0x49, 0x44, 0x6D, 0x78, - 0x62, 0x3B, 0x3D, 0x5D, 0x40, 0x3D, 0x3B, 0x69, - 0x49, 0x49, 0x3B, 0x71, 0x66, 0x73, 0x66, 0x54, - 0x27, 0x66, 0x5D, 0x7A, 0x21, 0x73, 0x71, 0x62, - 0x27, 0x54, 0x73, 0x66, 0x3D, 0x27, 0x62, 0x62, - 0x54, 0x73, 0x71, 0x37, 0x26, 0x5B, 0x44, 0x5B, - 0x27, 0x54, 0x40, 0x2D, 0x54, 0x20, 0x20, 0x77, - 0x78, 0x6D, 0x6D, 0x6D, 0x20, 0x20, 0x20, 0x20, - 0x79, 0x38, 0x38, 0x6D, 0x6D, 0x69, 0x2D, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7A, 0x7A, 0x40, - 0x71, 0x62, 0x27, 0x54, 0x79, 0x26, 0x38, 0x49, - 0x3B, 0x27, 0x54, 0x2D, 0x71, 0x6F, 0x49, 0x53, - 0x78, 0x30, 0x27, 0x54, 0x40, 0x73, 0x54, 0x3D, - 0x71, 0x66, 0x5D, 0x7A, 0x21, 0x66, 0x27, 0x3B, - 0x27, 0x66, 0x73, 0x54, 0x27, 0x27, 0x27, 0x3D, - 0x3D, 0x71, 0x6F, 0x5E, 0x26, 0x79, 0x26, 0x78, - 0x5D, 0x4C, 0x4C, 0x21, 0x73, 0x20, 0x20, 0x2D, - 0x35, 0x5C, 0x46, 0x38, 0x77, 0x20, 0x4F, 0x20, - 0x51, 0x6D, 0x20, 0x2D, 0x20, 0x6D, 0x6D, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7A, 0x21, 0x54, - 0x62, 0x3B, 0x27, 0x2D, 0x29, 0x26, 0x44, 0x70, - 0x27, 0x3D, 0x3D, 0x71, 0x6F, 0x78, 0x53, 0x29, - 0x69, 0x3D, 0x21, 0x4C, 0x4C, 0x5D, 0x66, 0x54, - 0x54, 0x73, 0x21, 0x6A, 0x21, 0x3D, 0x3B, 0x3B, - 0x71, 0x66, 0x66, 0x3D, 0x27, 0x27, 0x71, 0x3D, - 0x27, 0x6F, 0x49, 0x7E, 0x61, 0x5B, 0x44, 0x26, - 0x34, 0x34, 0x6A, 0x21, 0x73, 0x66, 0x20, 0x32, - 0x20, 0x20, 0x2D, 0x6D, 0x77, 0x2A, 0x37, 0x20, - 0x6D, 0x20, 0x20, 0x20, 0x7E, 0x38, 0x38, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x4C, 0x21, 0x3D, - 0x3B, 0x3B, 0x71, 0x73, 0x69, 0x44, 0x38, 0x71, - 0x27, 0x3D, 0x71, 0x3B, 0x49, 0x29, 0x29, 0x6F, - 0x54, 0x7A, 0x34, 0x77, 0x6A, 0x21, 0x66, 0x66, - 0x4C, 0x7A, 0x21, 0x4C, 0x21, 0x71, 0x3B, 0x3B, - 0x54, 0x73, 0x3D, 0x62, 0x3B, 0x62, 0x62, 0x62, - 0x62, 0x69, 0x29, 0x2B, 0x79, 0x79, 0x26, 0x38, - 0x47, 0x6A, 0x5D, 0x54, 0x27, 0x3D, 0x20, 0x29, - 0x57, 0x20, 0x2D, 0x61, 0x37, 0x5C, 0x20, 0x20, - 0x29, 0x20, 0x20, 0x20, 0x20, 0x29, 0x6D, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x6A, 0x21, 0x3D, - 0x3B, 0x3B, 0x54, 0x66, 0x78, 0x26, 0x26, 0x30, - 0x62, 0x62, 0x3B, 0x69, 0x78, 0x78, 0x6F, 0x66, - 0x6A, 0x64, 0x47, 0x4C, 0x5D, 0x54, 0x71, 0x71, - 0x4C, 0x5D, 0x5D, 0x21, 0x5D, 0x71, 0x3B, 0x62, - 0x54, 0x66, 0x27, 0x3B, 0x6F, 0x6F, 0x3B, 0x3B, - 0x6F, 0x49, 0x78, 0x23, 0x4A, 0x79, 0x4A, 0x6D, - 0x34, 0x21, 0x66, 0x3D, 0x62, 0x27, 0x20, 0x20, - 0x38, 0x20, 0x20, 0x52, 0x3A, 0x52, 0x63, 0x36, - 0x48, 0x4D, 0x20, 0x20, 0x20, 0x6D, 0x38, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x5D, 0x3D, - 0x3B, 0x62, 0x2D, 0x66, 0x79, 0x26, 0x51, 0x54, - 0x3B, 0x62, 0x6F, 0x49, 0x78, 0x30, 0x66, 0x34, - 0x2B, 0x2B, 0x34, 0x21, 0x66, 0x71, 0x62, 0x62, - 0x3D, 0x3D, 0x54, 0x5D, 0x40, 0x71, 0x3B, 0x3B, - 0x54, 0x54, 0x3B, 0x69, 0x30, 0x6F, 0x3B, 0x27, - 0x6F, 0x78, 0x78, 0x46, 0x26, 0x5B, 0x4A, 0x51, - 0x5D, 0x54, 0x71, 0x62, 0x3B, 0x3B, 0x20, 0x20, - 0x6D, 0x20, 0x72, 0x55, 0x3A, 0x74, 0x41, 0x39, - 0x45, 0x5A, 0x24, 0x2F, 0x6D, 0x38, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x5D, 0x40, 0x27, - 0x3B, 0x62, 0x3D, 0x54, 0x30, 0x5B, 0x26, 0x27, - 0x3B, 0x62, 0x6F, 0x78, 0x78, 0x3B, 0x21, 0x64, - 0x7B, 0x77, 0x5D, 0x54, 0x71, 0x62, 0x62, 0x3B, - 0x53, 0x62, 0x71, 0x73, 0x73, 0x27, 0x6F, 0x3B, - 0x3D, 0x3D, 0x3B, 0x49, 0x30, 0x62, 0x27, 0x27, - 0x6F, 0x78, 0x49, 0x46, 0x79, 0x53, 0x4A, 0x38, - 0x2D, 0x71, 0x62, 0x6F, 0x6F, 0x6F, 0x20, 0x20, - 0x20, 0x24, 0x52, 0x3A, 0x22, 0x41, 0x5A, 0x45, - 0x45, 0x63, 0x41, 0x22, 0x36, 0x28, 0x4D, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x73, 0x27, - 0x6F, 0x3B, 0x3D, 0x3D, 0x49, 0x51, 0x61, 0x69, - 0x27, 0x27, 0x30, 0x78, 0x69, 0x71, 0x4C, 0x64, - 0x34, 0x5D, 0x66, 0x71, 0x62, 0x6F, 0x6F, 0x6F, - 0x79, 0x27, 0x66, 0x73, 0x66, 0x62, 0x6F, 0x6F, - 0x3D, 0x3D, 0x3B, 0x30, 0x30, 0x62, 0x3D, 0x71, - 0x6F, 0x69, 0x6F, 0x23, 0x5B, 0x4A, 0x4A, 0x38, - 0x66, 0x3D, 0x3B, 0x6F, 0x6F, 0x3B, 0x20, 0x4D, - 0x43, 0x48, 0x39, 0x55, 0x22, 0x22, 0x41, 0x45, - 0x45, 0x45, 0x45, 0x41, 0x22, 0x45, 0x5A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x2D, 0x62, - 0x30, 0x3B, 0x3D, 0x3D, 0x27, 0x26, 0x61, 0x27, - 0x3D, 0x27, 0x6F, 0x69, 0x6F, 0x54, 0x4C, 0x34, - 0x7A, 0x40, 0x66, 0x54, 0x62, 0x30, 0x6F, 0x3B, - 0x53, 0x7A, 0x7A, 0x73, 0x54, 0x62, 0x30, 0x3B, - 0x3D, 0x3D, 0x62, 0x30, 0x30, 0x3B, 0x71, 0x62, - 0x30, 0x30, 0x27, 0x7E, 0x70, 0x70, 0x3C, 0x6D, - 0x21, 0x66, 0x3B, 0x69, 0x3B, 0x71, 0x20, 0x72, - 0x24, 0x67, 0x22, 0x22, 0x36, 0x36, 0x45, 0x45, - 0x45, 0x22, 0x41, 0x41, 0x3F, 0x42, 0x52, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x54, 0x62, - 0x30, 0x3B, 0x3D, 0x54, 0x62, 0x26, 0x79, 0x3B, - 0x71, 0x27, 0x30, 0x30, 0x27, 0x5D, 0x4C, 0x6A, - 0x7A, 0x7A, 0x5D, 0x54, 0x3B, 0x30, 0x6F, 0x71, - 0x27, 0x64, 0x77, 0x40, 0x71, 0x62, 0x3B, 0x62, - 0x3D, 0x54, 0x27, 0x3B, 0x6F, 0x3B, 0x27, 0x27, - 0x62, 0x3B, 0x3D, 0x23, 0x26, 0x5B, 0x3C, 0x38, - 0x5D, 0x71, 0x69, 0x69, 0x62, 0x54, 0x20, 0x50, - 0x5F, 0x48, 0x3A, 0x55, 0x41, 0x63, 0x70, 0x22, - 0x22, 0x45, 0x3F, 0x42, 0x48, 0x48, 0x45, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x71, 0x62, - 0x3B, 0x62, 0x3D, 0x54, 0x30, 0x26, 0x61, 0x71, - 0x27, 0x27, 0x62, 0x62, 0x54, 0x5D, 0x7A, 0x4C, - 0x4C, 0x4C, 0x5D, 0x71, 0x30, 0x69, 0x62, 0x54, - 0x7A, 0x64, 0x34, 0x73, 0x71, 0x27, 0x62, 0x62, - 0x71, 0x54, 0x71, 0x3B, 0x6F, 0x3B, 0x71, 0x3D, - 0x3D, 0x71, 0x2D, 0x7E, 0x79, 0x53, 0x3C, 0x38, - 0x3D, 0x6F, 0x78, 0x49, 0x71, 0x73, 0x20, 0x20, - 0x25, 0x3F, 0x3A, 0x41, 0x5A, 0x45, 0x41, 0x45, - 0x3F, 0x50, 0x24, 0x28, 0x28, 0x3F, 0x4D, 0x20, - 0x20, 0x77, 0x77, 0x20, 0x20, 0x20, 0x71, 0x27, - 0x62, 0x62, 0x71, 0x54, 0x78, 0x79, 0x38, 0x71, - 0x71, 0x3D, 0x71, 0x71, 0x66, 0x5D, 0x5D, 0x21, - 0x21, 0x21, 0x54, 0x30, 0x78, 0x69, 0x27, 0x66, - 0x7A, 0x4C, 0x40, 0x3D, 0x27, 0x62, 0x62, 0x3B, - 0x62, 0x71, 0x62, 0x30, 0x69, 0x6F, 0x71, 0x54, - 0x3D, 0x3D, 0x54, 0x23, 0x4A, 0x3C, 0x3C, 0x38, - 0x6F, 0x29, 0x53, 0x30, 0x54, 0x66, 0x20, 0x57, - 0x7C, 0x25, 0x4B, 0x3F, 0x43, 0x4B, 0x4B, 0x2C, - 0x2E, 0x2E, 0x2E, 0x24, 0x58, 0x58, 0x78, 0x20, - 0x20, 0x20, 0x34, 0x77, 0x20, 0x20, 0x20, 0x62, - 0x62, 0x3B, 0x62, 0x71, 0x29, 0x79, 0x61, 0x27, - 0x27, 0x54, 0x54, 0x71, 0x54, 0x54, 0x66, 0x54, - 0x66, 0x71, 0x6F, 0x78, 0x53, 0x69, 0x54, 0x73, - 0x73, 0x73, 0x3D, 0x27, 0x27, 0x27, 0x62, 0x3B, - 0x62, 0x71, 0x71, 0x3B, 0x6F, 0x3B, 0x27, 0x54, - 0x66, 0x3D, 0x3D, 0x37, 0x53, 0x78, 0x49, 0x38, - 0x78, 0x29, 0x78, 0x3B, 0x66, 0x73, 0x20, 0x20, - 0x7C, 0x69, 0x68, 0x68, 0x52, 0x2E, 0x42, 0x67, - 0x5F, 0x45, 0x2C, 0x69, 0x78, 0x32, 0x78, 0x78, - 0x20, 0x20, 0x34, 0x77, 0x35, 0x20, 0x20, 0x27, - 0x62, 0x3B, 0x62, 0x71, 0x30, 0x26, 0x61, 0x73, - 0x27, 0x3D, 0x66, 0x3D, 0x3D, 0x27, 0x27, 0x62, - 0x62, 0x6F, 0x78, 0x53, 0x78, 0x62, 0x54, 0x66, - 0x27, 0x71, 0x3D, 0x71, 0x62, 0x62, 0x27, 0x3B, - 0x62, 0x71, 0x71, 0x27, 0x27, 0x27, 0x27, 0x3D, - 0x54, 0x3D, 0x27, 0x7E, 0x29, 0x29, 0x29, 0x6D, - 0x49, 0x49, 0x6F, 0x54, 0x73, 0x54, 0x20, 0x20, - 0x29, 0x7C, 0x69, 0x43, 0x76, 0x72, 0x2C, 0x76, - 0x68, 0x62, 0x78, 0x29, 0x6D, 0x38, 0x6D, 0x32, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, - 0x27, 0x3B, 0x62, 0x71, 0x78, 0x61, 0x6D, 0x3C, - 0x27, 0x3D, 0x54, 0x3D, 0x27, 0x62, 0x3B, 0x6F, - 0x6F, 0x69, 0x49, 0x49, 0x6F, 0x3D, 0x73, 0x66, - 0x3D, 0x3D, 0x3D, 0x3D, 0x71, 0x27, 0x71, 0x62, - 0x62, 0x71, 0x3D, 0x71, 0x71, 0x71, 0x27, 0x71, - 0x54, 0x3D, 0x27, 0x46, 0x49, 0x78, 0x49, 0x6D, - 0x6F, 0x27, 0x54, 0x73, 0x40, 0x20, 0x20, 0x20, - 0x44, 0x32, 0x29, 0x49, 0x77, 0x2F, 0x73, 0x62, - 0x29, 0x32, 0x29, 0x51, 0x6D, 0x38, 0x6D, 0x38, - 0x56, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x71, 0x62, 0x62, 0x71, 0x6F, 0x70, 0x38, 0x71, - 0x27, 0x71, 0x54, 0x3D, 0x62, 0x3B, 0x3B, 0x6F, - 0x6F, 0x6F, 0x3B, 0x27, 0x54, 0x40, 0x73, 0x66, - 0x40, 0x40, 0x66, 0x3D, 0x71, 0x3D, 0x71, 0x71, - 0x27, 0x27, 0x3D, 0x54, 0x3D, 0x3D, 0x71, 0x54, - 0x73, 0x73, 0x3D, 0x46, 0x78, 0x49, 0x78, 0x44, - 0x66, 0x73, 0x5D, 0x5D, 0x35, 0x20, 0x20, 0x78, - 0x6D, 0x51, 0x78, 0x49, 0x58, 0x29, 0x29, 0x49, - 0x29, 0x79, 0x38, 0x38, 0x6D, 0x6D, 0x38, 0x6D, - 0x6D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x6B, 0x71, 0x27, 0x27, 0x29, 0x3C, 0x44, 0x40, - 0x3D, 0x54, 0x73, 0x73, 0x54, 0x71, 0x71, 0x3D, - 0x3D, 0x3D, 0x66, 0x40, 0x5D, 0x5D, 0x5D, 0x40, - 0x21, 0x5D, 0x73, 0x66, 0x3D, 0x3D, 0x71, 0x71, - 0x27, 0x27, 0x3D, 0x54, 0x54, 0x66, 0x54, 0x73, - 0x5D, 0x21, 0x40, 0x33, 0x69, 0x49, 0x30, 0x38, - 0x7A, 0x7A, 0x7A, 0x21, 0x6B, 0x20, 0x20, 0x6D, - 0x38, 0x6D, 0x38, 0x7C, 0x49, 0x29, 0x69, 0x78, - 0x38, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x71, 0x27, 0x71, 0x78, 0x3C, 0x26, 0x30, - 0x66, 0x66, 0x5D, 0x21, 0x5D, 0x73, 0x73, 0x40, - 0x5D, 0x21, 0x7A, 0x4C, 0x21, 0x5D, 0x21, 0x21, - 0x5D, 0x54, 0x54, 0x54, 0x3D, 0x71, 0x3D, 0x71, - 0x27, 0x27, 0x71, 0x54, 0x66, 0x54, 0x66, 0x66, - 0x5D, 0x21, 0x5D, 0x7E, 0x29, 0x69, 0x49, 0x6D, - 0x4C, 0x7A, 0x5D, 0x20, 0x20, 0x20, 0x51, 0x38, - 0x6D, 0x6D, 0x6D, 0x44, 0x69, 0x78, 0x5B, 0x6D, - 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x38, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x27, 0x3C, 0x69, 0x38, 0x71, - 0x54, 0x73, 0x5D, 0x21, 0x40, 0x73, 0x66, 0x73, - 0x21, 0x4C, 0x4C, 0x7A, 0x5D, 0x5D, 0x5D, 0x4C, - 0x66, 0x62, 0x62, 0x27, 0x71, 0x71, 0x71, 0x27, - 0x27, 0x27, 0x71, 0x3D, 0x54, 0x54, 0x54, 0x54, - 0x73, 0x73, 0x3D, 0x57, 0x29, 0x69, 0x30, 0x38, - 0x73, 0x73, 0x20, 0x20, 0x20, 0x2D, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x38, 0x6D, 0x6D, - 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x6D, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x27, 0x5B, 0x53, 0x6D, 0x73, - 0x54, 0x54, 0x73, 0x73, 0x3D, 0x27, 0x27, 0x71, - 0x66, 0x40, 0x73, 0x40, 0x66, 0x73, 0x40, 0x21, - 0x62, 0x30, 0x6F, 0x62, 0x27, 0x71, 0x3D, 0x71, - 0x27, 0x71, 0x71, 0x71, 0x3D, 0x3D, 0x3D, 0x3D, - 0x3D, 0x27, 0x58, 0x46, 0x69, 0x30, 0x6F, 0x6D, - 0x3D, 0x71, 0x20, 0x20, 0x20, 0x44, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, - 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, - 0x6D, 0x6D, 0x2F, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x70, 0x53, 0x38, 0x27, - 0x3D, 0x3D, 0x3D, 0x71, 0x3B, 0x30, 0x62, 0x3D, - 0x66, 0x54, 0x3D, 0x71, 0x3D, 0x3D, 0x66, 0x66, - 0x3B, 0x69, 0x69, 0x6F, 0x62, 0x27, 0x3D, 0x71, - 0x27, 0x27, 0x27, 0x27, 0x71, 0x3D, 0x3D, 0x3D, - 0x3D, 0x27, 0x3B, 0x46, 0x62, 0x3B, 0x49, 0x38, - 0x3D, 0x20, 0x20, 0x20, 0x34, 0x44, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, - 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, - 0x38, 0x6D, 0x26, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x21, 0x49, 0x79, 0x51, 0x5D, - 0x3D, 0x3D, 0x71, 0x27, 0x62, 0x62, 0x3D, 0x73, - 0x40, 0x66, 0x3D, 0x3D, 0x54, 0x3D, 0x3D, 0x71, - 0x78, 0x49, 0x69, 0x30, 0x3B, 0x62, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x71, 0x71, 0x71, 0x3D, 0x3D, - 0x3D, 0x27, 0x3D, 0x33, 0x49, 0x69, 0x62, 0x44, - 0x20, 0x20, 0x20, 0x20, 0x2D, 0x32, 0x6D, 0x38, - 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x38, 0x6D, 0x6D, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x38, 0x51, - 0x26, 0x61, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x6A, 0x70, 0x6F, 0x6D, 0x21, - 0x71, 0x3D, 0x3D, 0x71, 0x3D, 0x66, 0x40, 0x5D, - 0x40, 0x73, 0x66, 0x73, 0x66, 0x54, 0x3D, 0x71, - 0x70, 0x78, 0x49, 0x30, 0x6F, 0x6F, 0x62, 0x62, - 0x62, 0x27, 0x71, 0x3D, 0x54, 0x54, 0x54, 0x3D, - 0x3D, 0x71, 0x3D, 0x2A, 0x30, 0x2D, 0x3B, 0x26, - 0x38, 0x20, 0x20, 0x20, 0x2D, 0x62, 0x32, 0x26, - 0x38, 0x6D, 0x6D, 0x38, 0x5B, 0x38, 0x6D, 0x38, - 0x6D, 0x6D, 0x6D, 0x26, 0x32, 0x29, 0x29, 0x29, - 0x53, 0x29, 0x61, 0x6D, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x44, 0x3D, 0x3C, 0x62, 0x79, 0x7A, - 0x54, 0x54, 0x71, 0x27, 0x3D, 0x66, 0x73, 0x40, - 0x73, 0x66, 0x66, 0x73, 0x2D, 0x54, 0x71, 0x71, - 0x4A, 0x3B, 0x62, 0x3B, 0x3B, 0x3B, 0x3B, 0x27, - 0x27, 0x27, 0x71, 0x3D, 0x2D, 0x73, 0x73, 0x54, - 0x3D, 0x71, 0x71, 0x33, 0x30, 0x71, 0x5D, 0x38, - 0x6D, 0x6D, 0x38, 0x38, 0x38, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x44, 0x38, 0x6F, 0x7A, 0x64, 0x64, - 0x23, 0x23, 0x56, 0x23, 0x23, 0x7B, 0x47, 0x64, - 0x54, 0x29, 0x44, 0x38, 0x38, 0x6D, 0x38, 0x38, - 0x6D, 0x38, 0x38, 0x6D, 0x53, 0x49, 0x6D, 0x34, - 0x73, 0x54, 0x3D, 0x71, 0x71, 0x3D, 0x54, 0x3D, - 0x3D, 0x54, 0x66, 0x66, 0x66, 0x54, 0x54, 0x3D, - 0x49, 0x3D, 0x54, 0x54, 0x3D, 0x71, 0x27, 0x27, - 0x71, 0x71, 0x71, 0x3D, 0x54, 0x54, 0x54, 0x3D, - 0x3D, 0x71, 0x71, 0x33, 0x29, 0x3D, 0x3D, 0x6D, - 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x38, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, - 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x38, 0x6D, 0x44, - 0x6D, 0x38, 0x38, 0x6D, 0x69, 0x78, 0x61, 0x73, - 0x54, 0x3D, 0x3D, 0x71, 0x71, 0x3D, 0x3D, 0x3D, - 0x71, 0x3D, 0x3D, 0x3D, 0x3D, 0x54, 0x3D, 0x71, - 0x3B, 0x66, 0x73, 0x73, 0x2D, 0x2D, 0x54, 0x3D, - 0x71, 0x71, 0x3D, 0x71, 0x71, 0x71, 0x27, 0x27, - 0x27, 0x71, 0x71, 0x33, 0x3B, 0x62, 0x27, 0x3D, - 0x27, 0x3B, 0x3B, 0x27, 0x62, 0x3B, 0x3D, 0x3D, - 0x30, 0x27, 0x62, 0x62, 0x62, 0x71, 0x30, 0x27, - 0x3B, 0x6F, 0x30, 0x30, 0x3B, 0x30, 0x3B, 0x62, - 0x3B, 0x69, 0x49, 0x30, 0x29, 0x29, 0x29, 0x29, - 0x49, 0x29, 0x30, 0x29, 0x29, 0x29, 0x51, 0x21, - 0x27, 0x27, 0x71, 0x71, 0x71, 0x3D, 0x3D, 0x71, - 0x71, 0x71, 0x71, 0x3D, 0x71, 0x71, 0x71, 0x62, - 0x3B, 0x54, 0x66, 0x66, 0x66, 0x54, 0x3D, 0x54, - 0x66, 0x54, 0x3D, 0x27, 0x62, 0x62, 0x3B, 0x3B, - 0x3B, 0x62, 0x27, 0x33, 0x30, 0x6F, 0x71, 0x3B, - 0x62, 0x3B, 0x62, 0x27, 0x27, 0x30, 0x62, 0x27, - 0x62, 0x27, 0x3B, 0x49, 0x3B, 0x30, 0x29, 0x3B, - 0x3B, 0x30, 0x30, 0x69, 0x30, 0x6F, 0x30, 0x49, - 0x3B, 0x6F, 0x49, 0x29, 0x49, 0x49, 0x3C, 0x29, - 0x49, 0x49, 0x69, 0x70, 0x70, 0x29, 0x51, 0x27, - 0x3B, 0x3B, 0x3B, 0x62, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x3D, 0x3D, 0x3D, 0x71, 0x27, 0x27, 0x27, - 0x69, 0x71, 0x3D, 0x54, 0x71, 0x62, 0x27, 0x71, - 0x54, 0x2D, 0x3D, 0x27, 0x62, 0x3B, 0x3B, 0x3B, - 0x3B, 0x62, 0x62, 0x33, 0x27, 0x27, 0x3B, 0x71, - 0x27, 0x71, 0x27, 0x62, 0x71, 0x6F, 0x27, 0x71, - 0x3B, 0x62, 0x62, 0x6F, 0x62, 0x6F, 0x6F, 0x6F, - 0x6F, 0x69, 0x62, 0x49, 0x69, 0x49, 0x6F, 0x62, - 0x62, 0x49, 0x69, 0x71, 0x6F, 0x6F, 0x6F, 0x69, - 0x69, 0x69, 0x30, 0x29, 0x30, 0x69, 0x44, 0x7B, - 0x3B, 0x3B, 0x3B, 0x62, 0x62, 0x62, 0x62, 0x62, - 0x27, 0x3D, 0x3D, 0x54, 0x71, 0x3D, 0x3D, 0x54, - 0x69, 0x71, 0x3D, 0x71, 0x62, 0x3B, 0x3B, 0x27, - 0x54, 0x54, 0x3D, 0x71, 0x71, 0x71, 0x27, 0x62, - 0x62, 0x62, 0x27, 0x2A, 0x3D, 0x71, 0x3D, 0x71, - 0x3D, 0x62, 0x27, 0x30, 0x30, 0x62, 0x3B, 0x71, - 0x3B, 0x30, 0x30, 0x49, 0x29, 0x30, 0x30, 0x30, - 0x27, 0x49, 0x62, 0x30, 0x6F, 0x30, 0x3B, 0x3B, - 0x6F, 0x3B, 0x49, 0x30, 0x30, 0x3C, 0x3B, 0x49, - 0x30, 0x69, 0x6F, 0x78, 0x30, 0x62, 0x44, 0x7B, - 0x27, 0x62, 0x62, 0x62, 0x71, 0x71, 0x3D, 0x54, - 0x3D, 0x73, 0x66, 0x73, 0x66, 0x73, 0x73, 0x66, - 0x62, 0x66, 0x66, 0x3D, 0x27, 0x3B, 0x3B, 0x71, - 0x3D, 0x3D, 0x3D, 0x54, 0x54, 0x71, 0x3D, 0x3D, - 0x54, 0x73, 0x5D, 0x33, 0x62, 0x27, 0x54, 0x27, - 0x71, 0x3B, 0x71, 0x71, 0x62, 0x3B, 0x54, 0x3B, - 0x71, 0x6F, 0x62, 0x62, 0x62, 0x62, 0x69, 0x71, - 0x71, 0x6F, 0x3B, 0x71, 0x30, 0x62, 0x71, 0x6F, - 0x3B, 0x62, 0x6F, 0x62, 0x6F, 0x69, 0x6F, 0x69, - 0x6F, 0x30, 0x49, 0x3C, 0x69, 0x3B, 0x79, 0x21, - 0x20, 0x3D, 0x54, 0x73, 0x5D, 0x5D, 0x5D, 0x40, - 0x40, 0x73, 0x73, 0x73, 0x2D, 0x66, 0x66, 0x3D, - 0x3D, 0x54, 0x54, 0x3D, 0x71, 0x27, 0x62, 0x27, - 0x71, 0x71, 0x3D, 0x54, 0x54, 0x3D, 0x3D, 0x54, - 0x5D, 0x6A, 0x77, 0x46, 0x71, 0x2D, 0x54, 0x27, - 0x54, 0x3B, 0x3B, 0x3B, 0x6F, 0x3B, 0x71, 0x27, - 0x3B, 0x27, 0x3B, 0x3B, 0x27, 0x27, 0x3B, 0x3B, - 0x3B, 0x62, 0x3D, 0x62, 0x3D, 0x27, 0x3B, 0x54, - 0x3B, 0x2D, 0x49, 0x3B, 0x3B, 0x29, 0x49, 0x3C, - 0x53, 0x69, 0x53, 0x3C, 0x78, 0x3D, 0x78, 0x5D, - 0x20, 0x66, 0x5D, 0x6A, 0x47, 0x77, 0x4C, 0x5D, - 0x66, 0x3D, 0x3D, 0x66, 0x73, 0x66, 0x3D, 0x62, - 0x62, 0x62, 0x71, 0x3D, 0x71, 0x27, 0x27, 0x27, - 0x71, 0x71, 0x71, 0x3D, 0x3D, 0x71, 0x71, 0x73, - 0x7A, 0x77, 0x47, 0x46, 0x27, 0x73, 0x27, 0x54, - 0x3D, 0x71, 0x62, 0x6F, 0x27, 0x71, 0x27, 0x71, - 0x71, 0x71, 0x62, 0x62, 0x71, 0x71, 0x71, 0x62, - 0x62, 0x3B, 0x69, 0x49, 0x62, 0x6F, 0x62, 0x3D, - 0x6F, 0x6F, 0x62, 0x78, 0x2A, 0x20, 0x6B, 0x20, - 0x2A, 0x20, 0x20, 0x2A, 0x3B, 0x6F, 0x3C, 0x4C, - 0x20, 0x20, 0x7A, 0x77, 0x47, 0x6A, 0x5D, 0x54, - 0x27, 0x6F, 0x3B, 0x54, 0x40, 0x2D, 0x71, 0x6F, - 0x49, 0x6F, 0x27, 0x3D, 0x71, 0x62, 0x62, 0x3B, - 0x62, 0x27, 0x71, 0x3D, 0x3D, 0x71, 0x71, 0x66, - 0x7A, 0x34, 0x6A, 0x46, 0x27, 0x5D, 0x3D, 0x54, - 0x3D, 0x3D, 0x3D, 0x62, 0x27, 0x71, 0x27, 0x3D, - 0x3B, 0x3D, 0x30, 0x27, 0x27, 0x3B, 0x27, 0x3D, - 0x20, 0x20, 0x2A, 0x46, 0x46, 0x2A, 0x35, 0x2A, - 0x46, 0x46, 0x23, 0x2A, 0x7A, 0x4F, 0x4F, 0x6B, - 0x6B, 0x4F, 0x4F, 0x62, 0x3B, 0x62, 0x78, 0x20, - 0x20, 0x20, 0x7A, 0x34, 0x34, 0x40, 0x54, 0x71, - 0x3B, 0x69, 0x6F, 0x40, 0x7A, 0x66, 0x62, 0x69, - 0x3C, 0x3B, 0x71, 0x3D, 0x27, 0x3B, 0x6F, 0x27, - 0x71, 0x3D, 0x3D, 0x66, 0x2D, 0x3D, 0x62, 0x27, - 0x2D, 0x4C, 0x7A, 0x33, 0x27, 0x3D, 0x54, 0x2D, - 0x54, 0x62, 0x54, 0x27, 0x54, 0x27, 0x54, 0x71, - 0x62, 0x71, 0x71, 0x62, 0x62, 0x54, 0x71, 0x62, - 0x7A, 0x6B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x35, 0x57, - 0x6B, 0x20, 0x20, 0x30, 0x6F, 0x27, 0x29, 0x20, - 0x20, 0x20, 0x66, 0x4C, 0x7A, 0x54, 0x62, 0x3B, - 0x6F, 0x30, 0x71, 0x7A, 0x4C, 0x3D, 0x69, 0x78, - 0x53, 0x3D, 0x73, 0x2D, 0x71, 0x62, 0x3B, 0x71, - 0x3D, 0x2D, 0x2D, 0x40, 0x73, 0x3D, 0x27, 0x71, - 0x40, 0x6A, 0x20, 0x20, 0x71, 0x2D, 0x62, 0x2D, - 0x3D, 0x3B, 0x71, 0x27, 0x54, 0x27, 0x3D, 0x3D, - 0x27, 0x2D, 0x27, 0x3D, 0x3B, 0x2D, 0x3D, 0x3B, - 0x34, 0x2D, 0x77, 0x6A, 0x77, 0x2D, 0x6A, 0x7A, - 0x5D, 0x6A, 0x5D, 0x54, 0x71, 0x44, 0x6D, 0x6D, - 0x6D, 0x38, 0x26, 0x30, 0x54, 0x62, 0x20, 0x20, - 0x20, 0x20, 0x40, 0x6A, 0x4C, 0x54, 0x6F, 0x69, - 0x30, 0x62, 0x40, 0x6A, 0x21, 0x62, 0x49, 0x29, - 0x71, 0x4C, 0x34, 0x5D, 0x71, 0x3B, 0x27, 0x71, - 0x54, 0x54, 0x40, 0x40, 0x73, 0x3D, 0x3D, 0x40, - 0x6A, 0x20, 0x20, 0x33, 0x2D, 0x73, 0x40, 0x4E, - 0x77, 0x7A, 0x3D, 0x54, 0x2D, 0x54, 0x71, 0x54, - 0x62, 0x71, 0x71, 0x62, 0x71, 0x71, 0x71, 0x71, - 0x2D, 0x3B, 0x27, 0x3B, 0x49, 0x6F, 0x3B, 0x3B, - 0x27, 0x3B, 0x3B, 0x30, 0x49, 0x53, 0x6F, 0x6F, - 0x69, 0x3B, 0x6F, 0x53, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x6A, 0x77, 0x21, 0x27, 0x30, 0x30, - 0x6F, 0x71, 0x66, 0x73, 0x3D, 0x30, 0x49, 0x30, - 0x5D, 0x34, 0x34, 0x40, 0x27, 0x6F, 0x62, 0x3D, - 0x54, 0x66, 0x40, 0x73, 0x2D, 0x66, 0x2D, 0x5D, - 0x7A, 0x20, 0x20, 0x56, 0x20, 0x54, 0x5D, 0x5E, - 0x33, 0x71, 0x3D, 0x62, 0x27, 0x3B, 0x27, 0x30, - 0x3B, 0x3D, 0x27, 0x3D, 0x3D, 0x3D, 0x3B, 0x73, - 0x54, 0x62, 0x62, 0x62, 0x30, 0x6F, 0x71, 0x6F, - 0x6F, 0x6F, 0x6F, 0x71, 0x62, 0x3B, 0x3B, 0x49, - 0x3B, 0x3B, 0x3B, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7A, 0x21, 0x54, 0x3B, 0x69, 0x69, - 0x30, 0x62, 0x27, 0x71, 0x62, 0x30, 0x30, 0x6F, - 0x4C, 0x77, 0x6A, 0x66, 0x62, 0x6F, 0x62, 0x71, - 0x54, 0x66, 0x2D, 0x73, 0x66, 0x54, 0x73, 0x73, - 0x73, 0x20, 0x20, 0x7E, 0x20, 0x3D, 0x27, 0x6B, - 0x35, 0x21, 0x54, 0x3D, 0x71, 0x71, 0x54, 0x62, - 0x62, 0x71, 0x71, 0x69, 0x71, 0x54, 0x54, 0x30, - 0x27, 0x3B, 0x3B, 0x3B, 0x6F, 0x30, 0x3B, 0x30, - 0x3B, 0x30, 0x30, 0x27, 0x30, 0x6F, 0x62, 0x69, - 0x6F, 0x6F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x3D, 0x62, 0x6F, 0x69, 0x69, - 0x6F, 0x71, 0x3D, 0x71, 0x27, 0x62, 0x62, 0x27, - 0x4C, 0x4C, 0x5D, 0x71, 0x30, 0x69, 0x3B, 0x71, - 0x54, 0x66, 0x73, 0x66, 0x66, 0x66, 0x54, 0x3D, - 0x71, 0x20, 0x20, 0x7E, 0x20, 0x20, 0x21, 0x62, - 0x69, 0x27, 0x5D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3B, - 0x3D, 0x3D, 0x27, 0x3B, 0x27, 0x3D, 0x71, 0x6F, - 0x54, 0x62, 0x6F, 0x30, 0x6F, 0x6F, 0x62, 0x6F, - 0x62, 0x62, 0x62, 0x62, 0x3B, 0x3B, 0x27, 0x3B, - 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x27, 0x3B, 0x6F, 0x30, 0x6F, 0x6F, - 0x62, 0x3D, 0x66, 0x54, 0x54, 0x71, 0x71, 0x71, - 0x5D, 0x5D, 0x54, 0x3B, 0x69, 0x69, 0x3B, 0x71, - 0x54, 0x40, 0x73, 0x66, 0x54, 0x3D, 0x71, 0x62, - 0x6F, 0x20, 0x20, 0x39, 0x20, 0x20, 0x20, 0x2D, - 0x2D, 0x73, 0x40, 0x54, 0x54, 0x54, 0x71, 0x73, - 0x54, 0x73, 0x71, 0x54, 0x54, 0x54, 0x27, 0x3B, - 0x3D, 0x3B, 0x27, 0x62, 0x3B, 0x3B, 0x3B, 0x27, - 0x27, 0x3B, 0x3B, 0x27, 0x62, 0x62, 0x71, 0x62, - 0x71, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x3B, 0x6F, 0x30, 0x6F, 0x3B, 0x27, - 0x3D, 0x66, 0x66, 0x54, 0x71, 0x71, 0x27, 0x71, - 0x66, 0x73, 0x54, 0x27, 0x6F, 0x6F, 0x27, 0x54, - 0x40, 0x21, 0x5D, 0x73, 0x3D, 0x27, 0x62, 0x3B, - 0x3B, 0x42, 0x74, 0x52, 0x52, 0x6E, 0x20, 0x20, - 0x40, 0x54, 0x3D, 0x3D, 0x3D, 0x40, 0x27, 0x3B, - 0x30, 0x40, 0x27, 0x27, 0x27, 0x71, 0x54, 0x6F, - 0x5D, 0x6F, 0x3B, 0x71, 0x71, 0x6F, 0x73, 0x6F, - 0x54, 0x6F, 0x54, 0x27, 0x39, 0x6E, 0x6E, 0x3B, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x3B, 0x3B, 0x3B, 0x62, 0x27, 0x71, - 0x3D, 0x3D, 0x3D, 0x27, 0x62, 0x27, 0x62, 0x27, - 0x3D, 0x40, 0x54, 0x27, 0x3B, 0x3B, 0x27, 0x73, - 0x7A, 0x21, 0x40, 0x54, 0x71, 0x62, 0x6F, 0x6F, - 0x3B, 0x67, 0x3A, 0x3A, 0x5A, 0x48, 0x3A, 0x20, - 0x20, 0x53, 0x6D, 0x38, 0x38, 0x6D, 0x38, 0x38, - 0x6D, 0x38, 0x79, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x67, 0x52, 0x41, 0x22, 0x2F, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4F, 0x6B, - 0x43, 0x3A, 0x3B, 0x27, 0x27, 0x62, 0x27, 0x71, - 0x71, 0x71, 0x71, 0x3D, 0x71, 0x71, 0x3D, 0x3D, - 0x27, 0x40, 0x54, 0x27, 0x30, 0x30, 0x27, 0x40, - 0x7A, 0x5D, 0x54, 0x71, 0x3B, 0x30, 0x30, 0x3B, - 0x42, 0x67, 0x67, 0x3E, 0x3A, 0x48, 0x22, 0x5A, - 0x4F, 0x20, 0x2D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x6D, 0x26, 0x6D, 0x38, 0x6D, 0x38, 0x6D, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x38, 0x6D, 0x38, - 0x6D, 0x38, 0x38, 0x3E, 0x55, 0x6C, 0x22, 0x73, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4D, - 0x5A, 0x45, 0x36, 0x3D, 0x71, 0x27, 0x27, 0x71, - 0x54, 0x66, 0x73, 0x40, 0x73, 0x66, 0x2D, 0x66, - 0x30, 0x66, 0x71, 0x30, 0x69, 0x6F, 0x3D, 0x21, - 0x7A, 0x66, 0x3D, 0x62, 0x3B, 0x6F, 0x3B, 0x28, - 0x67, 0x52, 0x5A, 0x74, 0x41, 0x3A, 0x74, 0x3A, - 0x52, 0x20, 0x20, 0x7E, 0x38, 0x6D, 0x6D, 0x6D, - 0x6D, 0x38, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, - 0x6D, 0x6D, 0x6D, 0x2F, 0x52, 0x22, 0x28, 0x50, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2E, - 0x41, 0x5A, 0x5A, 0x66, 0x54, 0x3D, 0x54, 0x66, - 0x73, 0x40, 0x40, 0x40, 0x73, 0x66, 0x66, 0x3D, - 0x69, 0x27, 0x3B, 0x30, 0x30, 0x62, 0x73, 0x7A, - 0x21, 0x3D, 0x3B, 0x6F, 0x6F, 0x62, 0x2F, 0x75, - 0x28, 0x55, 0x22, 0x3A, 0x31, 0x3A, 0x41, 0x3A, - 0x5A, 0x2E, 0x20, 0x4F, 0x20, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x6D, 0x78, 0x2F, 0x31, 0x55, 0x2E, 0x3F, - 0x50, 0x20, 0x20, 0x20, 0x20, 0x4D, 0x24, 0x52, - 0x22, 0x22, 0x31, 0x2D, 0x66, 0x54, 0x66, 0x66, - 0x66, 0x66, 0x54, 0x71, 0x27, 0x27, 0x27, 0x62, - 0x30, 0x3B, 0x3B, 0x6F, 0x3B, 0x3D, 0x40, 0x21, - 0x73, 0x71, 0x5F, 0x6E, 0x2E, 0x2E, 0x67, 0x52, - 0x52, 0x31, 0x7D, 0x48, 0x3A, 0x3A, 0x74, 0x74, - 0x55, 0x39, 0x20, 0x20, 0x20, 0x20, 0x38, 0x6D, - 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x6D, 0x29, 0x29, 0x2F, 0x55, 0x52, 0x2E, 0x24, - 0x72, 0x68, 0x25, 0x76, 0x68, 0x3F, 0x2E, 0x39, - 0x52, 0x74, 0x3A, 0x73, 0x66, 0x66, 0x54, 0x54, - 0x27, 0x3B, 0x6F, 0x6F, 0x6F, 0x6F, 0x3B, 0x62, - 0x3B, 0x62, 0x27, 0x71, 0x54, 0x40, 0x21, 0x40, - 0x3D, 0x2E, 0x48, 0x6E, 0x55, 0x55, 0x6E, 0x55, - 0x3A, 0x74, 0x3E, 0x55, 0x74, 0x5A, 0x22, 0x3A, - 0x3A, 0x36, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x38, - 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x38, 0x6D, 0x38, 0x38, 0x6D, 0x38, 0x6D, 0x38, - 0x6D, 0x32, 0x78, 0x62, 0x3E, 0x52, 0x28, 0x42, - 0x65, 0x24, 0x5F, 0x24, 0x5F, 0x2E, 0x55, 0x22, - 0x3A, 0x41, 0x74, 0x31, 0x54, 0x73, 0x66, 0x54, - 0x27, 0x6F, 0x30, 0x69, 0x30, 0x6F, 0x62, 0x62, - 0x62, 0x71, 0x3D, 0x54, 0x73, 0x5D, 0x5D, 0x66, - 0x71, 0x2E, 0x22, 0x31, 0x55, 0x3A, 0x31, 0x5A, - 0x3A, 0x3A, 0x74, 0x5A, 0x74, 0x3E, 0x31, 0x3A, - 0x55, 0x22, 0x22, 0x35, 0x20, 0x20, 0x20, 0x20, - 0x34, 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x38, 0x6D, - 0x6D, 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x38, 0x79, 0x29, 0x2F, 0x42, 0x52, 0x28, 0x48, - 0x48, 0x2E, 0x2E, 0x48, 0x3E, 0x52, 0x3A, 0x74, - 0x7D, 0x3A, 0x3A, 0x3E, 0x40, 0x40, 0x40, 0x54, - 0x27, 0x6F, 0x30, 0x6F, 0x62, 0x71, 0x71, 0x71, - 0x62, 0x3D, 0x66, 0x73, 0x40, 0x5D, 0x73, 0x71, - 0x62, 0x28, 0x55, 0x5A, 0x5A, 0x55, 0x3A, 0x41, - 0x55, 0x3A, 0x3A, 0x31, 0x55, 0x55, 0x5A, 0x74, - 0x3A, 0x31, 0x22, 0x48, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, - 0x6D, 0x6D, 0x29, 0x2F, 0x24, 0x28, 0x28, 0x52, - 0x52, 0x48, 0x48, 0x28, 0x39, 0x52, 0x74, 0x48, - 0x74, 0x55, 0x22, 0x41, 0x5A, 0x40, 0x54, 0x27, - 0x3B, 0x6F, 0x3B, 0x71, 0x54, 0x66, 0x66, 0x3D, - 0x62, 0x54, 0x40, 0x21, 0x7A, 0x40, 0x3D, 0x62, - 0x62, 0x48, 0x52, 0x55, 0x6C, 0x5A, 0x31, 0x31, - 0x5A, 0x41, 0x31, 0x3A, 0x3A, 0x7D, 0x31, 0x3A, - 0x41, 0x41, 0x22, 0x36, 0x42, 0x20, 0x20, 0x20, - 0x20, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x6D, 0x6D, 0x29, 0x25, 0x59, 0x2E, 0x39, 0x39, - 0x55, 0x39, 0x39, 0x39, 0x31, 0x22, 0x3A, 0x74, - 0x5A, 0x3E, 0x6C, 0x3E, 0x31, 0x3E, 0x3A, 0x69, - 0x49, 0x49, 0x3B, 0x71, 0x66, 0x73, 0x66, 0x54, - 0x27, 0x66, 0x5D, 0x7A, 0x21, 0x73, 0x71, 0x62, - 0x27, 0x75, 0x39, 0x41, 0x3A, 0x36, 0x7D, 0x74, - 0x74, 0x41, 0x55, 0x55, 0x3A, 0x3A, 0x3A, 0x3A, - 0x31, 0x31, 0x5A, 0x22, 0x52, 0x20, 0x20, 0x20, - 0x26, 0x38, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x38, - 0x6D, 0x38, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, - 0x38, 0x6D, 0x20, 0x60, 0x24, 0x48, 0x39, 0x3A, - 0x55, 0x55, 0x31, 0x55, 0x41, 0x74, 0x41, 0x22, - 0x7D, 0x3A, 0x22, 0x3E, 0x41, 0x5A, 0x3A, 0x74, - 0x78, 0x30, 0x27, 0x54, 0x40, 0x73, 0x54, 0x3D, - 0x71, 0x54, 0x5D, 0x7A, 0x21, 0x66, 0x62, 0x3B, - 0x71, 0x5F, 0x52, 0x3E, 0x41, 0x5A, 0x5A, 0x22, - 0x3E, 0x3A, 0x74, 0x3E, 0x55, 0x55, 0x3A, 0x31, - 0x41, 0x3A, 0x48, 0x55, 0x41, 0x42, 0x6D, 0x38, - 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, - 0x38, 0x6D, 0x6D, 0x38, 0x6D, 0x38, 0x6D, 0x6D, - 0x6D, 0x20, 0x20, 0x25, 0x24, 0x28, 0x52, 0x3A, - 0x5A, 0x5A, 0x5A, 0x5A, 0x74, 0x74, 0x7D, 0x74, - 0x3A, 0x74, 0x3A, 0x41, 0x7D, 0x41, 0x3A, 0x3A, - 0x69, 0x3D, 0x21, 0x4C, 0x4C, 0x5D, 0x66, 0x54, - 0x66, 0x73, 0x21, 0x6A, 0x21, 0x3D, 0x3B, 0x6F, - 0x71, 0x75, 0x48, 0x31, 0x5A, 0x3A, 0x3E, 0x48, - 0x74, 0x7D, 0x3A, 0x7D, 0x3A, 0x3A, 0x55, 0x74, - 0x5A, 0x3A, 0x41, 0x55, 0x22, 0x22, 0x3F, 0x6D, - 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x38, - 0x20, 0x20, 0x20, 0x60, 0x42, 0x28, 0x39, 0x3A, - 0x3A, 0x31, 0x41, 0x3A, 0x22, 0x55, 0x74, 0x55, - 0x74, 0x74, 0x74, 0x3A, 0x3A, 0x74, 0x3A, 0x67, - 0x54, 0x7A, 0x34, 0x77, 0x6A, 0x21, 0x66, 0x66, - 0x7A, 0x21, 0x21, 0x4C, 0x21, 0x3D, 0x3B, 0x62, - 0x66, 0x67, 0x28, 0x55, 0x41, 0x31, 0x55, 0x3A, - 0x74, 0x41, 0x31, 0x3A, 0x3A, 0x41, 0x3A, 0x36, - 0x5A, 0x5A, 0x31, 0x31, 0x39, 0x22, 0x24, 0x43, - 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x38, 0x6D, 0x6D, - 0x6D, 0x38, 0x6D, 0x6D, 0x6D, 0x38, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x60, 0x24, 0x63, 0x39, 0x55, - 0x31, 0x5A, 0x3A, 0x74, 0x3A, 0x31, 0x3A, 0x31, - 0x5A, 0x48, 0x3A, 0x7D, 0x48, 0x41, 0x31, 0x3E, - 0x6A, 0x64, 0x47, 0x4C, 0x5D, 0x54, 0x71, 0x71, - 0x6A, 0x5D, 0x5D, 0x21, 0x5D, 0x3D, 0x3B, 0x62, - 0x66, 0x42, 0x39, 0x3A, 0x41, 0x3A, 0x31, 0x3A, - 0x7D, 0x3A, 0x74, 0x41, 0x31, 0x31, 0x3E, 0x41, - 0x5A, 0x41, 0x3A, 0x31, 0x39, 0x52, 0x48, 0x25, - 0x62, 0x6D, 0x38, 0x38, 0x6D, 0x38, 0x6D, 0x6D, - 0x6D, 0x6D, 0x6D, 0x6D, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4D, 0x43, 0x5F, 0x28, 0x52, 0x3E, - 0x22, 0x31, 0x3A, 0x3A, 0x55, 0x3A, 0x3E, 0x31, - 0x74, 0x67, 0x3E, 0x3A, 0x3E, 0x67, 0x54, 0x34, - 0x2B, 0x2B, 0x34, 0x21, 0x66, 0x71, 0x62, 0x62, - 0x3D, 0x3D, 0x54, 0x5D, 0x40, 0x27, 0x6F, 0x3B, - 0x67, 0x48, 0x48, 0x39, 0x52, 0x7D, 0x7D, 0x22, - 0x74, 0x3A, 0x5A, 0x5A, 0x3A, 0x55, 0x31, 0x3A, - 0x41, 0x7D, 0x3A, 0x22, 0x55, 0x48, 0x42, 0x76, - 0x4B, 0x20, 0x37, 0x6D, 0x6D, 0x6D, 0x38, 0x78, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4D, 0x76, 0x42, 0x48, 0x55, 0x74, - 0x41, 0x6C, 0x48, 0x31, 0x31, 0x3A, 0x5A, 0x74, - 0x31, 0x6C, 0x22, 0x3E, 0x67, 0x62, 0x7A, 0x64, - 0x7B, 0x77, 0x5D, 0x54, 0x71, 0x62, 0x62, 0x3B, - 0x53, 0x62, 0x71, 0x73, 0x73, 0x27, 0x6F, 0x3B, - 0x67, 0x2E, 0x5F, 0x48, 0x48, 0x52, 0x52, 0x52, - 0x52, 0x52, 0x31, 0x41, 0x74, 0x41, 0x74, 0x31, - 0x74, 0x3A, 0x74, 0x74, 0x48, 0x48, 0x42, 0x72, - 0x4B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4B, 0x68, 0x42, 0x28, 0x55, 0x74, - 0x5A, 0x3A, 0x48, 0x55, 0x5A, 0x31, 0x55, 0x55, - 0x39, 0x67, 0x2F, 0x49, 0x69, 0x27, 0x4C, 0x64, - 0x34, 0x5D, 0x66, 0x71, 0x62, 0x6F, 0x6F, 0x6F, - 0x79, 0x27, 0x66, 0x73, 0x66, 0x27, 0x6F, 0x3B, - 0x54, 0x24, 0x5F, 0x59, 0x24, 0x24, 0x42, 0x2E, - 0x48, 0x67, 0x28, 0x39, 0x52, 0x39, 0x31, 0x3E, - 0x55, 0x3A, 0x3A, 0x31, 0x39, 0x48, 0x24, 0x76, - 0x50, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4B, 0x76, 0x24, 0x42, 0x52, 0x55, - 0x41, 0x31, 0x31, 0x39, 0x52, 0x52, 0x48, 0x67, - 0x72, 0x71, 0x6F, 0x69, 0x58, 0x2D, 0x4C, 0x34, - 0x7A, 0x40, 0x66, 0x54, 0x62, 0x30, 0x6F, 0x3B, - 0x53, 0x7A, 0x7A, 0x73, 0x3D, 0x62, 0x30, 0x6F, - 0x3D, 0x3D, 0x3B, 0x60, 0x2F, 0x76, 0x59, 0x59, - 0x59, 0x24, 0x24, 0x5F, 0x42, 0x2E, 0x28, 0x55, - 0x3A, 0x39, 0x39, 0x48, 0x48, 0x65, 0x68, 0x25, - 0x4B, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x4B, 0x25, 0x72, 0x65, 0x2E, 0x28, - 0x52, 0x28, 0x48, 0x48, 0x2E, 0x24, 0x3F, 0x4B, - 0x71, 0x27, 0x30, 0x30, 0x27, 0x5D, 0x4C, 0x6A, - 0x7A, 0x7A, 0x5D, 0x54, 0x3B, 0x30, 0x6F, 0x71, - 0x27, 0x64, 0x34, 0x40, 0x3D, 0x62, 0x3B, 0x27, - 0x3D, 0x54, 0x71, 0x3B, 0x3B, 0x62, 0x71, 0x4B, - 0x43, 0x43, 0x76, 0x76, 0x72, 0x59, 0x24, 0x24, - 0x42, 0x2E, 0x42, 0x24, 0x2C, 0x76, 0x60, 0x50, - 0x4D, 0x20, 0x20, 0x20, 0x20, 0x62, 0x27, 0x3D, - 0x3D, 0x27, 0x62, 0x62, 0x27, 0x27, 0x62, 0x30, - 0x20, 0x20, 0x4B, 0x25, 0x76, 0x59, 0x24, 0x24, - 0x42, 0x42, 0x42, 0x65, 0x3F, 0x60, 0x6F, 0x62, - 0x27, 0x27, 0x62, 0x3B, 0x3D, 0x5D, 0x7A, 0x4C, - 0x4C, 0x4C, 0x5D, 0x71, 0x30, 0x69, 0x62, 0x54, - 0x7A, 0x2B, 0x34, 0x2D, 0x71, 0x27, 0x58, 0x62, - 0x71, 0x3D, 0x71, 0x6F, 0x30, 0x6F, 0x27, 0x54, - 0x3D, 0x71, 0x66, 0x4B, 0x25, 0x60, 0x76, 0x76, - 0x72, 0x72, 0x3F, 0x76, 0x76, 0x60, 0x50, 0x4B, - 0x20, 0x73, 0x3D, 0x62, 0x3B, 0x27, 0x71, 0x3D, - 0x3D, 0x71, 0x27, 0x62, 0x62, 0x27, 0x62, 0x3B, - 0x30, 0x27, 0x4D, 0x4B, 0x25, 0x76, 0x72, 0x2C, - 0x59, 0x2C, 0x3F, 0x76, 0x25, 0x62, 0x30, 0x3B, - 0x71, 0x3D, 0x71, 0x71, 0x66, 0x5D, 0x5D, 0x21, - 0x21, 0x21, 0x54, 0x30, 0x78, 0x69, 0x27, 0x66, - 0x7A, 0x4C, 0x5D, 0x3D, 0x27, 0x62, 0x62, 0x3B, - 0x62, 0x3D, 0x27, 0x6F, 0x30, 0x3B, 0x71, 0x54, - 0x3D, 0x3D, 0x54, 0x66, 0x66, 0x66, 0x4B, 0x25, - 0x25, 0x25, 0x25, 0x60, 0x25, 0x50, 0x4B, 0x71, - 0x54, 0x54, 0x71, 0x27, 0x3D, 0x54, 0x54, 0x3D, - 0x3D, 0x71, 0x3B, 0x3B, 0x62, 0x3B, 0x62, 0x3B, - 0x27, 0x54, 0x4C, 0x4D, 0x4B, 0x25, 0x76, 0x76, - 0x68, 0x43, 0x25, 0x50, 0x27, 0x30, 0x30, 0x58, - 0x27, 0x54, 0x54, 0x3D, 0x54, 0x54, 0x66, 0x54, - 0x66, 0x71, 0x6F, 0x78, 0x53, 0x69, 0x54, 0x73, - 0x66, 0x66, 0x54, 0x27, 0x27, 0x27, 0x62, 0x3B, - 0x3B, 0x27, 0x27, 0x3B, 0x6F, 0x62, 0x27, 0x54, - 0x66, 0x3D, 0x3D, 0x27, 0x27, 0x27, 0x62, 0x6F, - 0x78, 0x53, 0x78, 0x62, 0x66, 0x73, 0x3D, 0x3D, - 0x66, 0x2D, 0x54, 0x54, 0x73, 0x73, 0x54, 0x71, - 0x71, 0x27, 0x3B, 0x6F, 0x3B, 0x3B, 0x62, 0x3B, - 0x3B, 0x71, 0x73, 0x73, 0x54, 0x71, 0x62, 0x27, - 0x27, 0x58, 0x62, 0x71, 0x71, 0x6F, 0x6F, 0x62, - 0x27, 0x54, 0x66, 0x3D, 0x3D, 0x27, 0x27, 0x62, - 0x62, 0x6F, 0x78, 0x53, 0x78, 0x62, 0x54, 0x66, - 0x71, 0x3D, 0x71, 0x71, 0x62, 0x27, 0x27, 0x62, - 0x62, 0x71, 0x3D, 0x27, 0x27, 0x62, 0x27, 0x3D, - 0x54, 0x3D, 0x27, 0x62, 0x3B, 0x6F, 0x6F, 0x69, - 0x78, 0x78, 0x6F, 0x54, 0x73, 0x66, 0x54, 0x54, - 0x40, 0x5D, 0x40, 0x40, 0x40, 0x66, 0x3D, 0x71, - 0x27, 0x27, 0x3B, 0x30, 0x6F, 0x3B, 0x3B, 0x62, - 0x3B, 0x3B, 0x27, 0x71, 0x71, 0x27, 0x62, 0x62, - 0x62, 0x62, 0x62, 0x71, 0x71, 0x71, 0x62, 0x62, - 0x27, 0x71, 0x54, 0x3D, 0x27, 0x62, 0x3B, 0x6F, - 0x6F, 0x69, 0x49, 0x49, 0x6F, 0x3D, 0x73, 0x66 + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, + 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x26, 0x26, 0x25, 0x28, 0x23, 0x22, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x25, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, + 0x2d, 0x2e, 0x2c, 0x2b, 0x2a, 0x25, 0x28, 0x22, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x24, 0x2a, 0x2c, 0x2f, 0x2c, 0x30, 0x30, 0x24, + 0x25, 0x27, 0x2b, 0x2c, 0x2f, 0x31, 0x32, 0x25, + 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, + 0x33, 0x34, 0x35, 0x21, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x21, 0x2b, 0x2f, 0x2c, + 0x30, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x33, + 0x2d, 0x27, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x31, + 0x2d, 0x32, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x37, 0x37, 0x37, 0x37, 0x38, + 0x37, 0x37, 0x39, 0x37, 0x39, 0x38, 0x39, 0x3a, + 0x32, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x23, 0x32, 0x27, 0x21, 0x36, + 0x34, 0x38, 0x38, 0x39, 0x38, 0x37, 0x38, 0x39, + 0x38, 0x37, 0x38, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x38, 0x37, 0x37, 0x37, 0x37, + 0x38, 0x39, 0x37, 0x37, 0x37, 0x37, 0x38, 0x3b, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x25, 0x2f, 0x3c, 0x32, 0x22, + 0x36, 0x3d, 0x38, 0x37, 0x39, 0x37, 0x38, 0x37, + 0x39, 0x37, 0x38, 0x37, 0x38, 0x37, 0x37, 0x37, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3d, 0x3e, 0x3a, 0x3e, 0x3e, + 0x3e, 0x3f, 0x3e, 0x3a, 0x3e, 0x3e, 0x40, 0x22, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x26, 0x41, 0x41, 0x35, 0x25, + 0x36, 0x22, 0x42, 0x38, 0x38, 0x37, 0x37, 0x38, + 0x39, 0x37, 0x37, 0x38, 0x37, 0x38, 0x38, 0x3a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x43, 0x3e, 0x44, 0x44, 0x45, + 0x44, 0x40, 0x3a, 0x3f, 0x3a, 0x3f, 0x3b, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x25, 0x2b, 0x30, 0x28, 0x22, + 0x36, 0x36, 0x35, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x37, 0x44, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x34, 0x3e, 0x3a, 0x38, 0x38, + 0x3e, 0x3e, 0x46, 0x3e, 0x46, 0x46, 0x33, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x22, 0x22, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x47, 0x38, 0x37, 0x37, 0x37, + 0x38, 0x37, 0x37, 0x37, 0x38, 0x38, 0x37, 0x48, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x37, 0x44, 0x3e, 0x22, + 0x2d, 0x2c, 0x49, 0x43, 0x4a, 0x4b, 0x22, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x49, 0x4c, 0x46, 0x44, 0x46, + 0x4c, 0x38, 0x44, 0x38, 0x38, 0x3e, 0x37, 0x4d, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x34, 0x3e, 0x3e, 0x3a, 0x36, + 0x20, 0x20, 0x20, 0x23, 0x2a, 0x34, 0x36, 0x36, + 0x36, 0x21, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x21, 0x23, 0x22, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x28, 0x34, 0x27, 0x22, 0x20, + 0x21, 0x20, 0x20, 0x20, 0x4e, 0x37, 0x38, 0x4f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x34, 0x44, 0x3a, 0x3e, 0x43, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x36, + 0x21, 0x21, 0x24, 0x27, 0x21, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x28, 0x27, 0x22, 0x33, 0x24, 0x36, + 0x36, 0x36, 0x36, 0x22, 0x2f, 0x2a, 0x23, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x50, 0x38, 0x3e, 0x51, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2c, 0x3a, 0x44, 0x44, 0x52, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x36, + 0x30, 0x3b, 0x41, 0x24, 0x24, 0x36, 0x36, 0x36, + 0x23, 0x2f, 0x53, 0x54, 0x55, 0x30, 0x25, 0x21, + 0x36, 0x36, 0x36, 0x36, 0x2f, 0x32, 0x23, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x51, 0x3e, 0x37, 0x42, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x34, 0x44, 0x45, 0x3e, 0x45, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x23, + 0x56, 0x4d, 0x57, 0x3b, 0x22, 0x36, 0x36, 0x21, + 0x49, 0x51, 0x4c, 0x45, 0x40, 0x56, 0x23, 0x21, + 0x36, 0x36, 0x36, 0x36, 0x2f, 0x33, 0x28, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x44, 0x38, 0x51, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2e, 0x44, 0x40, 0x44, 0x42, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x2b, + 0x45, 0x57, 0x44, 0x39, 0x35, 0x36, 0x36, 0x26, + 0x4c, 0x58, 0x59, 0x3d, 0x3f, 0x3e, 0x2e, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x31, 0x35, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x57, 0x44, 0x3e, 0x48, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5a, 0x44, 0x45, 0x3e, 0x44, + 0x20, 0x20, 0x20, 0x23, 0x32, 0x34, 0x36, 0x4b, + 0x5b, 0x25, 0x2f, 0x44, 0x3d, 0x22, 0x23, 0x32, + 0x3a, 0x42, 0x21, 0x31, 0x43, 0x46, 0x50, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x31, 0x35, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x47, 0x3e, 0x38, 0x50, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x20, 0x29, 0x29, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x49, 0x40, 0x40, 0x44, 0x38, + 0x20, 0x20, 0x20, 0x23, 0x2a, 0x2f, 0x21, 0x3b, + 0x4b, 0x21, 0x31, 0x5c, 0x5d, 0x28, 0x30, 0x2b, + 0x3f, 0x4b, 0x36, 0x23, 0x32, 0x42, 0x4d, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x2e, 0x5a, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x42, 0x44, 0x44, 0x52, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x20, 0x29, 0x20, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2c, 0x4c, 0x40, 0x4c, 0x37, + 0x20, 0x20, 0x20, 0x23, 0x2a, 0x41, 0x23, 0x3c, + 0x5d, 0x36, 0x28, 0x3b, 0x5e, 0x5f, 0x5f, 0x60, + 0x54, 0x4b, 0x36, 0x36, 0x36, 0x57, 0x57, 0x21, + 0x36, 0x36, 0x36, 0x36, 0x2e, 0x5a, 0x24, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x44, 0x3a, 0x5c, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x44, 0x45, 0x4c, 0x3a, + 0x20, 0x20, 0x20, 0x22, 0x30, 0x43, 0x23, 0x35, + 0x4c, 0x25, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x36, 0x31, 0x39, 0x53, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x31, 0x2c, 0x25, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x52, 0x45, 0x44, 0x54, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x40, 0x4d, 0x4c, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x23, 0x22, + 0x57, 0x6a, 0x6b, 0x65, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x6e, 0x66, 0x72, 0x73, 0x2a, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x33, 0x2e, 0x26, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x51, 0x3a, 0x44, 0x47, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2c, 0x45, 0x4c, 0x4c, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x27, 0x2f, 0x23, 0x36, + 0x74, 0x6b, 0x75, 0x6c, 0x64, 0x6e, 0x71, 0x76, + 0x77, 0x78, 0x79, 0x71, 0x71, 0x7a, 0x74, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x33, 0x34, 0x27, 0x22, + 0x20, 0x20, 0x20, 0x20, 0x54, 0x44, 0x44, 0x54, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5a, 0x48, 0x48, 0x4f, 0x37, + 0x20, 0x20, 0x20, 0x22, 0x27, 0x34, 0x26, 0x7b, + 0x7c, 0x7d, 0x7e, 0x6c, 0x6d, 0x7f, 0x71, 0x80, + 0x78, 0x79, 0x79, 0x79, 0x7a, 0x67, 0x66, 0x21, + 0x36, 0x36, 0x36, 0x36, 0x25, 0x41, 0x2a, 0x23, + 0x20, 0x20, 0x20, 0x20, 0x81, 0x44, 0x40, 0x5b, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2c, 0x44, 0x45, 0x4f, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0x33, 0x82, + 0x6b, 0x83, 0x84, 0x64, 0x6e, 0x71, 0x76, 0x85, + 0x79, 0x79, 0x71, 0x86, 0x87, 0x83, 0x88, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x21, 0x43, 0x2b, 0x28, + 0x21, 0x20, 0x20, 0x20, 0x52, 0x44, 0x44, 0x50, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5a, 0x40, 0x4d, 0x4f, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0x32, 0x24, + 0x89, 0x8a, 0x6c, 0x8b, 0x7f, 0x71, 0x79, 0x79, + 0x71, 0x8c, 0x8d, 0x8e, 0x83, 0x8e, 0x8f, 0x36, + 0x21, 0x2b, 0x23, 0x36, 0x36, 0x5a, 0x2e, 0x26, + 0x22, 0x20, 0x20, 0x20, 0x42, 0x40, 0x38, 0x50, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2c, 0x4c, 0x4f, 0x4f, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x27, 0x2d, 0x33, 0x21, + 0x90, 0x91, 0x92, 0x7a, 0x6f, 0x6e, 0x67, 0x92, + 0x93, 0x6b, 0x8e, 0x94, 0x95, 0x96, 0x49, 0x36, + 0x36, 0x2d, 0x3b, 0x35, 0x36, 0x24, 0x43, 0x32, + 0x28, 0x21, 0x20, 0x20, 0x57, 0x40, 0x44, 0x54, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x34, 0x4d, 0x42, 0x51, 0x38, + 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x33, 0x21, + 0x5d, 0x97, 0x98, 0x93, 0x86, 0x66, 0x99, 0x87, + 0x7d, 0x7d, 0x99, 0x6a, 0x57, 0x4d, 0x59, 0x23, + 0x36, 0x24, 0x3b, 0x3b, 0x24, 0x36, 0x2e, 0x31, + 0x26, 0x22, 0x20, 0x20, 0x52, 0x44, 0x44, 0x9a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5a, 0x57, 0x57, 0x57, 0x37, + 0x20, 0x20, 0x21, 0x28, 0x33, 0x3c, 0x25, 0x22, + 0x53, 0x42, 0x97, 0x98, 0x99, 0x87, 0x99, 0x6b, + 0x7c, 0x9b, 0x9c, 0x51, 0x4f, 0x3f, 0x40, 0x2c, + 0x36, 0x36, 0x33, 0x5a, 0x21, 0x36, 0x22, 0x43, + 0x33, 0x28, 0x21, 0x20, 0x42, 0x44, 0x37, 0x4f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x51, 0x42, 0x51, 0x37, + 0x20, 0x20, 0x22, 0x27, 0x2e, 0x2e, 0x36, 0x21, + 0x4e, 0x4d, 0x42, 0x9d, 0x9e, 0x98, 0x98, 0x9f, + 0x97, 0x51, 0x42, 0x4c, 0x39, 0x58, 0x58, 0x47, + 0x21, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x5a, + 0x2e, 0x27, 0x23, 0x20, 0x59, 0x48, 0x38, 0x50, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x42, 0x51, 0x42, 0x3e, + 0x20, 0x22, 0x24, 0x2b, 0x41, 0x28, 0x36, 0x32, + 0x3e, 0x3f, 0x42, 0x42, 0x42, 0x51, 0x51, 0x42, + 0x42, 0x57, 0x40, 0x38, 0x58, 0x58, 0x58, 0x58, + 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, + 0x2f, 0x2b, 0x24, 0x21, 0x57, 0x4f, 0x3e, 0x53, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x47, 0x51, 0x52, 0x38, + 0x21, 0x28, 0x32, 0x43, 0x32, 0x28, 0x21, 0x47, + 0x58, 0x39, 0x48, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x48, 0x3a, 0x37, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x4f, 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x2a, 0x2f, 0x2a, 0x28, 0x42, 0x4f, 0x44, 0x52, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5a, 0x57, 0x47, 0x51, 0x37, + 0x23, 0x30, 0x2e, 0x2c, 0x36, 0x21, 0x43, 0x37, + 0x58, 0x58, 0x46, 0x4d, 0x42, 0x42, 0x57, 0x3f, + 0x39, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2d, 0x31, 0x27, 0x4f, 0x47, 0x38, 0x50, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x25, 0x57, 0x47, 0x52, 0x38, + 0x27, 0x2c, 0x2d, 0x21, 0x36, 0x28, 0x45, 0x58, + 0x58, 0x58, 0x58, 0x39, 0x44, 0x3a, 0x39, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x52, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x28, 0x43, 0x5a, 0x45, 0x4d, 0x37, 0x9a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x47, 0x52, 0x59, 0x37, + 0x35, 0x43, 0x28, 0x36, 0x36, 0x4a, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x37, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x3a, 0x28, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x28, 0x41, 0x48, 0x4d, 0x38, 0x54, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x81, 0x5b, 0x51, 0x38, + 0x43, 0x25, 0x36, 0x36, 0x23, 0x57, 0x37, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x37, 0x38, 0x2b, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x30, 0x51, 0x40, 0x3a, 0x56, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x51, 0x47, 0x81, 0x3e, + 0x27, 0x36, 0x36, 0x36, 0x2a, 0x57, 0x39, 0x58, + 0x58, 0x58, 0x58, 0x37, 0x38, 0x38, 0x37, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x37, 0x39, 0x46, + 0x44, 0x3a, 0x3c, 0x21, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x48, 0x59, 0x37, 0x5d, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x27, 0x52, 0x4e, 0x5b, 0x44, + 0x36, 0x36, 0x36, 0x21, 0x31, 0x5b, 0x48, 0x3e, + 0x39, 0x37, 0x37, 0x46, 0x44, 0x3a, 0x46, 0x37, + 0x37, 0x37, 0x39, 0x3a, 0x40, 0x48, 0x4f, 0x4f, + 0x4d, 0x4f, 0x47, 0x28, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x4f, 0x81, 0x40, 0x5d, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x52, 0x50, 0x56, 0x38, + 0x36, 0x36, 0x36, 0x22, 0x41, 0x47, 0x45, 0x38, + 0x37, 0x58, 0x58, 0x37, 0x3e, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x39, 0x46, 0x44, 0x4c, 0x4f, + 0x57, 0x57, 0x4c, 0x50, 0x21, 0x23, 0x33, 0x23, + 0x36, 0x36, 0x36, 0x36, 0x4d, 0x51, 0x39, 0x3b, + 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x57, 0x55, 0x55, 0x37, + 0x37, 0x37, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x38, 0x37, 0x37, 0x37, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x39, 0x3e, 0x37, 0x38, 0x38, + 0x38, 0x37, 0x38, 0x38, 0x38, 0x37, 0x38, 0x38, + 0x37, 0x38, 0x38, 0x37, 0x47, 0x42, 0x48, 0x9a, + 0x25, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x5b, 0x81, 0x5d, 0x37, + 0x38, 0x37, 0x37, 0x38, 0x37, 0x38, 0x38, 0x37, + 0x38, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x37, + 0x38, 0x37, 0x38, 0x37, 0x37, 0x38, 0x37, 0x37, + 0x38, 0x37, 0x37, 0x37, 0x38, 0x38, 0x37, 0x3e, + 0x37, 0x38, 0x38, 0x37, 0x57, 0x57, 0x48, 0x5d, + 0x2a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x33, 0x52, 0x59, 0x50, 0x5b, + 0x54, 0x5b, 0x5b, 0x54, 0x81, 0x5b, 0x55, 0x55, + 0x52, 0x54, 0x81, 0x81, 0x81, 0x50, 0x52, 0x54, + 0x5b, 0x59, 0x52, 0x52, 0x5b, 0x52, 0x5b, 0x81, + 0x5b, 0x47, 0x51, 0x52, 0x57, 0x57, 0x57, 0x57, + 0x51, 0x57, 0x52, 0x57, 0x48, 0x57, 0x4c, 0x3b, + 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x33, 0x54, 0x54, 0x5b, 0x50, + 0x54, 0x50, 0x54, 0x81, 0x50, 0x59, 0x54, 0x50, + 0x5b, 0x81, 0x81, 0x59, 0x81, 0x59, 0x59, 0x59, + 0x59, 0x47, 0x81, 0x51, 0x47, 0x51, 0x59, 0x81, + 0x81, 0x51, 0x47, 0x50, 0x59, 0x59, 0x59, 0x47, + 0x47, 0x47, 0x52, 0x57, 0x52, 0x47, 0x59, 0x26, + 0x2d, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x22, 0x27, 0x55, 0x50, 0x55, 0x50, + 0x55, 0x81, 0x54, 0x52, 0x52, 0x81, 0x5b, 0x50, + 0x5b, 0x52, 0x52, 0x51, 0x57, 0x52, 0x52, 0x52, + 0x54, 0x51, 0x81, 0x52, 0x59, 0x52, 0x5b, 0x5b, + 0x59, 0x5b, 0x51, 0x52, 0x52, 0x4f, 0x5b, 0x51, + 0x52, 0x47, 0x59, 0x42, 0x47, 0x81, 0x5c, 0x21, + 0x2f, 0x32, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x23, 0x33, 0x81, 0x54, 0x5c, 0x54, + 0x50, 0x5b, 0x50, 0x50, 0x81, 0x5b, 0x5c, 0x5b, + 0x50, 0x59, 0x81, 0x81, 0x81, 0x81, 0x47, 0x50, + 0x50, 0x59, 0x5b, 0x50, 0x52, 0x81, 0x50, 0x59, + 0x5b, 0x81, 0x59, 0x81, 0x59, 0x47, 0x59, 0x47, + 0x59, 0x52, 0x51, 0x48, 0x51, 0x52, 0x5d, 0x36, + 0x2e, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x22, 0x25, 0x2a, 0x50, 0x4e, 0x5c, 0x54, + 0x5c, 0x5b, 0x5b, 0x5b, 0x59, 0x5b, 0x50, 0x54, + 0x5b, 0x54, 0x5b, 0x5b, 0x54, 0x54, 0x5b, 0x5b, + 0x5b, 0x81, 0x55, 0x81, 0x55, 0x54, 0x5b, 0x5c, + 0x5b, 0x4e, 0x51, 0x5b, 0x5b, 0x57, 0x51, 0x4f, + 0x4d, 0x47, 0x4f, 0x4c, 0x51, 0x52, 0x4b, 0x36, + 0x2a, 0x2e, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x2a, 0x2a, 0x50, 0x9a, 0x54, 0x5c, + 0x55, 0x50, 0x81, 0x59, 0x54, 0x50, 0x54, 0x50, + 0x50, 0x50, 0x81, 0x81, 0x50, 0x50, 0x50, 0x81, + 0x81, 0x5b, 0x47, 0x51, 0x81, 0x59, 0x81, 0x55, + 0x59, 0x59, 0x81, 0x42, 0x27, 0x36, 0x28, 0x36, + 0x27, 0x36, 0x28, 0x2d, 0x47, 0x52, 0x2d, 0x36, + 0x22, 0x2f, 0x30, 0x22, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x25, 0x2c, 0x26, 0x53, 0x4e, 0x55, 0x5c, + 0x55, 0x55, 0x55, 0x81, 0x54, 0x50, 0x54, 0x55, + 0x5b, 0x55, 0x52, 0x54, 0x54, 0x5b, 0x54, 0x55, + 0x36, 0x36, 0x27, 0x2a, 0x2a, 0x27, 0xa0, 0x27, + 0x2a, 0x2a, 0x2c, 0x27, 0x5d, 0x22, 0x22, 0x28, + 0x28, 0x22, 0x25, 0x59, 0x51, 0x53, 0x27, 0x36, + 0x21, 0x2f, 0x32, 0x23, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x28, 0x32, 0x2f, 0x21, 0x4a, 0x54, 0x5c, 0x4e, + 0x5c, 0x81, 0x5c, 0x54, 0x5c, 0x54, 0x5c, 0x50, + 0x81, 0x50, 0x50, 0x81, 0x81, 0x5c, 0x50, 0x81, + 0x5d, 0x28, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xa0, 0x25, + 0x28, 0x21, 0x28, 0x81, 0x59, 0x4a, 0x28, 0x36, + 0x36, 0x34, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x25, 0x2c, 0x5a, 0x36, 0x32, 0x9a, 0x81, 0x4e, + 0x55, 0x5b, 0x50, 0x54, 0x5c, 0x54, 0x55, 0x55, + 0x54, 0x4e, 0x54, 0x55, 0x5b, 0x4e, 0x55, 0x5b, + 0x3b, 0x4e, 0x4a, 0x3b, 0x4a, 0x4e, 0x3b, 0x5d, + 0x56, 0x3b, 0x56, 0x5c, 0x50, 0x3a, 0x3e, 0x40, + 0x4f, 0x57, 0x81, 0x4e, 0x4e, 0x27, 0x36, 0x36, + 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x30, 0x2d, 0x21, 0x36, 0x23, 0x3d, 0x53, 0x43, + 0x4a, 0x5d, 0x55, 0x5c, 0x4e, 0x5c, 0x50, 0x5c, + 0x81, 0x50, 0x50, 0x81, 0x50, 0x50, 0x50, 0x50, + 0x4e, 0x5b, 0x54, 0x5b, 0x51, 0x59, 0x5b, 0x5b, + 0x54, 0x5b, 0x5b, 0x52, 0x51, 0x42, 0x5b, 0x5c, + 0x5c, 0x53, 0x5b, 0x3b, 0x28, 0x36, 0x36, 0x36, + 0x36, 0x2c, 0x5a, 0x24, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0x2e, 0x36, 0x36, 0x23, 0x41, 0x56, 0x2e, + 0x33, 0x50, 0x55, 0x81, 0x54, 0x5b, 0x54, 0x52, + 0x5b, 0x55, 0x54, 0x55, 0x55, 0x55, 0x5b, 0x9a, + 0x5c, 0x81, 0x81, 0x81, 0x52, 0x59, 0x50, 0x59, + 0x59, 0x59, 0x59, 0x50, 0x81, 0x81, 0x5b, 0x5b, + 0x5b, 0x54, 0x34, 0x22, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2c, 0x5a, 0x24, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x2b, 0x5a, 0x36, 0x36, 0x36, 0x27, 0x3d, 0x28, + 0xa0, 0x5d, 0x5c, 0x55, 0x50, 0x50, 0x5c, 0x81, + 0x81, 0x50, 0x50, 0x47, 0x50, 0x5c, 0x5c, 0x52, + 0x54, 0x5b, 0x5b, 0x5b, 0x59, 0x52, 0x5b, 0x52, + 0x5b, 0x52, 0x52, 0x54, 0x59, 0x5b, 0x81, 0x81, + 0x9a, 0x33, 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, + 0x35, 0x5a, 0x36, 0x36, 0x36, 0x36, 0x30, 0x4e, + 0x52, 0x54, 0x56, 0x55, 0x55, 0x55, 0x55, 0x5b, + 0x55, 0x55, 0x54, 0x5b, 0x54, 0x55, 0x50, 0x59, + 0x5c, 0x81, 0x59, 0x52, 0x59, 0x59, 0x81, 0x59, + 0x81, 0x81, 0x81, 0x81, 0x5b, 0x5b, 0x81, 0x53, + 0x2a, 0x27, 0xa1, 0x24, 0x25, 0x28, 0x21, 0x36, + 0x36, 0x34, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x25, + 0x5a, 0x4b, 0xa2, 0x8d, 0x8a, 0x5f, 0x21, 0x2d, + 0x55, 0x5c, 0x9a, 0x5c, 0x5c, 0x5c, 0x50, 0x9a, + 0x5c, 0x9a, 0x50, 0x5c, 0x5c, 0x5c, 0x54, 0x5b, + 0x55, 0x5b, 0x54, 0x81, 0x5b, 0x5b, 0x5b, 0x54, + 0x54, 0x5b, 0x5b, 0x5b, 0x5b, 0x54, 0x54, 0x30, + 0x23, 0x36, 0x36, 0x36, 0x21, 0x28, 0x2c, 0x30, + 0x21, 0x41, 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x22, 0x22, 0x28, 0x30, + 0x2d, 0xa3, 0x83, 0xa4, 0xa5, 0xa5, 0x88, 0x22, + 0x3b, 0x47, 0x53, 0x55, 0x55, 0x53, 0x54, 0x5b, + 0x52, 0x53, 0x54, 0x54, 0x54, 0x50, 0x5c, 0x59, + 0x56, 0x59, 0x5b, 0x50, 0x50, 0x59, 0x9a, 0x59, + 0x5c, 0x81, 0x50, 0x59, 0x9a, 0x52, 0x68, 0x69, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x30, 0x32, + 0x25, 0x4b, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x23, 0x24, 0x26, 0x30, 0x33, 0x31, + 0x4b, 0x9b, 0x63, 0xa4, 0xa5, 0xa5, 0xa5, 0x62, + 0x21, 0x2e, 0x44, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x3a, 0x37, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0xa6, 0xa7, 0x71, 0x6e, 0xa8, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x26, 0x25, + 0x8c, 0xa9, 0x2c, 0x25, 0x21, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x28, 0x30, 0x35, 0x2d, 0x2f, 0x3c, 0x3d, + 0x68, 0x8e, 0xaa, 0xab, 0xa5, 0xa5, 0xa5, 0x8b, + 0x8f, 0x36, 0x32, 0x4d, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x3e, 0x38, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0xac, 0xa4, 0xa7, 0xad, 0xa0, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x5f, + 0x7f, 0x6e, 0x34, 0x27, 0x22, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x23, 0x30, 0x31, 0xae, 0x9b, 0x87, 0x99, 0x99, + 0x94, 0x63, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0x65, 0xaf, 0x36, 0x24, 0x50, 0x37, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x37, 0x38, 0xac, 0x6c, 0x64, 0x94, 0xaf, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x7b, 0x65, + 0x8b, 0x64, 0xb0, 0x2a, 0x23, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x24, 0x2b, 0xae, 0x94, 0x63, 0x7e, 0x63, 0x63, + 0x84, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa7, 0x66, 0x22, 0x36, 0x21, 0x3b, 0x38, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x3a, 0x4d, 0xb1, 0x84, 0x84, 0x8e, 0x89, + 0xa1, 0x36, 0x36, 0x36, 0x21, 0xb2, 0x87, 0x84, + 0x6c, 0x6c, 0xb3, 0x35, 0x24, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x31, 0xb4, 0x63, 0x6c, 0xa4, 0xa4, 0xab, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0x6e, 0xb5, 0x36, 0x36, 0x36, 0x2c, 0x3f, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x48, 0x4d, 0xb6, 0x7e, 0x7e, 0x83, 0x6b, + 0xb7, 0xb8, 0x8f, 0xb8, 0xb9, 0x99, 0x63, 0x6c, + 0xa4, 0xa4, 0xba, 0x2d, 0x27, 0x23, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x26, 0x2d, 0x9b, 0x63, 0x6c, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa7, 0x8c, 0xa0, 0x36, 0x36, 0x36, 0x30, + 0x45, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x37, 0x48, 0x4d, 0xbb, 0x63, 0x84, 0xbc, 0x8e, + 0x87, 0x99, 0x6b, 0x99, 0x8e, 0x63, 0xa4, 0xa5, + 0xa5, 0xab, 0x65, 0xb3, 0x5a, 0x26, 0x23, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x2d, 0xbd, 0xbc, 0x6c, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa7, 0x91, 0x36, 0x36, 0x36, 0x36, + 0x2d, 0x37, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x37, 0x40, 0x4d, 0xbe, 0x75, 0x84, 0xaa, 0xbc, + 0x83, 0x94, 0x94, 0x83, 0x63, 0x6c, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa4, 0xbf, 0x3c, 0x35, 0x26, 0x23, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x2e, 0xbd, 0x83, 0x84, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa7, 0x7a, 0x7b, 0x36, 0x36, 0x36, + 0x24, 0x46, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x44, 0x51, 0xb4, 0x83, 0x84, 0x6c, 0x84, + 0x7e, 0x63, 0x63, 0x7e, 0x84, 0xa4, 0xa5, 0xa5, + 0xa5, 0xa5, 0xab, 0x6c, 0xbf, 0x4b, 0x2c, 0x27, + 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x25, 0x31, 0xc0, 0x94, 0x84, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa7, 0x92, 0xa1, 0x36, 0x36, + 0x32, 0x39, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x3f, 0x2f, 0x98, 0x83, 0x84, 0xa4, 0xab, + 0xa4, 0x6c, 0x6c, 0xa4, 0xa4, 0xab, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0x65, 0xc1, 0x2c, + 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x25, 0x31, 0xc0, 0x8e, 0x84, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0x6e, 0x5f, 0x27, 0x4b, + 0x3f, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x38, 0x34, 0xa1, 0xb7, 0x83, 0x84, 0xa4, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xc0, + 0x32, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, + 0x26, 0x2d, 0xc0, 0x8e, 0x84, 0xab, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa7, 0x67, 0x9c, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x45, + 0x35, 0x36, 0xa0, 0xb9, 0x83, 0x84, 0xab, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xab, 0xa4, 0xbe, + 0x2b, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x30, 0x2f, 0xb4, 0x94, 0x84, 0xab, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0x6e, 0x92, 0x40, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x37, 0x5b, 0x25, + 0x36, 0x36, 0x69, 0xb7, 0x75, 0x6c, 0xab, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xab, 0x84, 0xbc, 0xc1, + 0x32, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0xc2, 0x6b, 0xbc, 0xa4, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0x7a, 0x8a, 0xc3, + 0x44, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x4d, 0x2c, 0x36, 0x36, + 0x36, 0x36, 0xc4, 0x98, 0x75, 0x6c, 0xab, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa4, 0xaa, 0x94, 0xae, 0x2c, + 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, + 0x35, 0x9e, 0x7d, 0xaa, 0xa4, 0xab, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xab, 0x84, 0x8a, 0xb7, + 0x7b, 0x53, 0x45, 0x37, 0x58, 0x58, 0x58, 0x37, + 0x38, 0x4c, 0x4e, 0x2c, 0x21, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x7b, 0xb7, 0x83, 0x84, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0x6c, 0x7e, 0x83, 0x9b, 0xb3, 0x31, 0x30, + 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, + 0x5a, 0x9f, 0x7d, 0xbc, 0x84, 0x6c, 0xa4, 0xa4, + 0xab, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0x84, 0x83, 0xc5, + 0xc6, 0x36, 0x21, 0x26, 0x2b, 0x5a, 0x33, 0x30, + 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x21, 0xc6, 0xb9, 0x94, 0x84, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0x6c, 0x6c, + 0x7e, 0x8e, 0xbd, 0xb3, 0x34, 0x2b, 0x27, 0x28, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, + 0x33, 0xc7, 0x6b, 0x87, 0x83, 0x75, 0xbc, 0x63, + 0x7e, 0x84, 0x6c, 0x6c, 0xa4, 0xab, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xab, 0x7e, 0x8e, 0xb7, + 0x82, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x21, 0xc8, 0xb9, 0x7d, 0x7e, 0xa4, 0xa5, + 0xa5, 0xa5, 0xab, 0xa4, 0x6c, 0x7e, 0xbc, 0x94, + 0xb4, 0xb3, 0x2f, 0x35, 0x30, 0x24, 0x22, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x27, 0x31, 0xc9, 0xc7, 0xb9, 0x7c, 0x6b, 0x99, + 0x87, 0x7d, 0x94, 0x75, 0xbc, 0x7e, 0x6c, 0xa4, + 0xab, 0xab, 0xab, 0xab, 0x6c, 0x83, 0x8d, 0xca, + 0x82, 0xa1, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x21, 0xc6, 0xca, 0x6b, 0x75, 0x84, 0x6c, + 0xab, 0xa4, 0x6c, 0x84, 0xbc, 0x7d, 0x6b, 0x9e, + 0x41, 0x5a, 0x2a, 0x24, 0x23, 0x21, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x28, 0x27, 0x35, 0x2d, 0x49, 0xb3, 0xc3, 0x98, + 0xb7, 0xb9, 0xc5, 0x7c, 0x8d, 0x99, 0x8e, 0x75, + 0x63, 0x84, 0x84, 0xaa, 0x75, 0x99, 0xb7, 0xcb, + 0xc8, 0x22, 0x36, 0x36, 0x28, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x25, 0x36, + 0x36, 0x21, 0xb2, 0x89, 0xc5, 0x87, 0x75, 0x7e, + 0xaa, 0x7e, 0x75, 0x8e, 0x6b, 0xb7, 0xb3, 0x34, + 0x33, 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x23, 0x24, 0x27, 0x2a, 0x35, 0x2e, 0x2f, + 0x49, 0xcc, 0xcd, 0x74, 0x89, 0xca, 0xb7, 0x7c, + 0x8d, 0x99, 0x7d, 0x87, 0x7c, 0x98, 0xcb, 0x82, + 0xc4, 0x2b, 0x4a, 0x49, 0x2f, 0x34, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x34, 0x2f, 0x41, + 0x4b, 0x3c, 0xce, 0xcf, 0x98, 0x7c, 0x6b, 0x87, + 0x7d, 0x87, 0x6b, 0xc5, 0x91, 0xc2, 0x31, 0x2a, + 0x24, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x26, 0x30, + 0x33, 0x5a, 0x2e, 0x43, 0x49, 0xb0, 0x74, 0xcf, + 0x89, 0xca, 0xca, 0xca, 0xd0, 0xcf, 0xb5, 0xd1, + 0x49, 0x34, 0x35, 0x32, 0x30, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x30, 0x2a, + 0x2b, 0x34, 0xd2, 0xc8, 0xd3, 0x98, 0xb9, 0xc5, + 0xc5, 0xb9, 0xca, 0x74, 0x49, 0x5a, 0x27, 0x28, + 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, + 0x28, 0x24, 0x26, 0x2a, 0x33, 0x2c, 0x2f, 0x49, + 0xd4, 0xb5, 0x82, 0x82, 0x82, 0xc8, 0xd5, 0x43, + 0x5a, 0x30, 0x24, 0x23, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x24, 0x2a, 0x31, 0xd6, 0xc6, 0x82, 0xcf, 0x89, + 0xd3, 0xb8, 0xd7, 0x2f, 0x35, 0x26, 0x23, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x22, 0x23, 0x28, 0x25, 0x30, 0x2b, + 0x31, 0x2f, 0xd2, 0xd6, 0xd6, 0x2f, 0x2e, 0x33, + 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x21, 0x28, 0x27, 0x35, 0x34, 0xd6, 0xd6, 0xd6, + 0xd8, 0xd2, 0x2e, 0x33, 0x25, 0x23, 0x21, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x23, 0x28, + 0x26, 0x30, 0x32, 0x2b, 0x33, 0x2a, 0x26, 0x28, + 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x23, 0x25, 0x30, 0x33, 0x35, 0x35, + 0x2b, 0x2a, 0x26, 0x28, 0x22, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x21, 0x22, 0x23, 0x28, 0x28, 0x23, 0x22, 0x21, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x21, 0x23, 0x28, 0x24, 0x24, + 0x28, 0x23, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, }; -#endif - -#else +#endif /* !INCLUDE_LINUX_LOGO_DATA */ -#define LINUX_LOGO_COLORS 214 +#endif /* CONFIG_MAC */ -#endif - -#ifdef INCLUDE_LINUX_LOGO_DATA - -#define INCLUDE_LINUX_LOGOBW -#define INCLUDE_LINUX_LOGO16 #include -#else - -/* prototypes only */ -extern unsigned char linux_logo_red[]; -extern unsigned char linux_logo_green[]; -extern unsigned char linux_logo_blue[]; -extern unsigned char linux_logo[]; -extern unsigned char linux_logo_bw[]; -extern unsigned char linux_logo16[]; - -#endif diff --git a/include/asm-ppc/hardirq.h b/include/asm-ppc/hardirq.h index 23b2ae64a9bb..152dc842d2cd 100644 --- a/include/asm-ppc/hardirq.h +++ b/include/asm-ppc/hardirq.h @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.hardirq.h 1.7 05/17/01 18:14:24 cort + * BK Id: SCCS/s.hardirq.h 1.10 06/09/01 22:16:38 paulus */ #ifdef __KERNEL__ #ifndef __ASM_HARDIRQ_H @@ -15,8 +15,7 @@ * for uniformity. */ typedef struct { - unsigned int __softirq_active; - unsigned int __softirq_mask; + unsigned long __softirq_pending; /* set_bit is used on this */ unsigned int __local_irq_count; unsigned int __local_bh_count; unsigned int __syscall_count; diff --git a/include/asm-ppc/softirq.h b/include/asm-ppc/softirq.h index d4ec98ae741a..9280f0baa923 100644 --- a/include/asm-ppc/softirq.h +++ b/include/asm-ppc/softirq.h @@ -1,5 +1,5 @@ /* - * BK Id: SCCS/s.softirq.h 1.8 06/06/01 22:33:09 paulus + * BK Id: SCCS/s.softirq.h 1.10 06/09/01 22:16:38 paulus */ #ifdef __KERNEL__ #ifndef __ASM_SOFTIRQ_H @@ -29,6 +29,9 @@ do { \ } \ } while (0) +#define __cpu_raise_softirq(cpu, nr) set_bit((nr), &softirq_pending(cpu)); +#define raise_softirq(nr) __cpu_raise_softirq(smp_processor_id(), (nr)) + #define in_softirq() (local_bh_count(smp_processor_id()) != 0) #endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-sparc/hardirq.h b/include/asm-sparc/hardirq.h index 00205d6ef017..ac828721c7e0 100644 --- a/include/asm-sparc/hardirq.h +++ b/include/asm-sparc/hardirq.h @@ -14,8 +14,8 @@ /* entry.S is sensitive to the offsets of these fields */ typedef struct { - unsigned int __softirq_active; - unsigned int __softirq_mask; + unsigned int __softirq_pending; + unsigned int __unused_1; #ifndef CONFIG_SMP unsigned int __local_irq_count; #else diff --git a/include/asm-sparc/linux_logo.h b/include/asm-sparc/linux_logo.h index 72c9c07ebd5d..88b42adaab00 100644 --- a/include/asm-sparc/linux_logo.h +++ b/include/asm-sparc/linux_logo.h @@ -1,4 +1,4 @@ -/* $Id: linux_logo.h,v 1.6 1998/08/20 04:44:39 ecd Exp $ +/* $Id: linux_logo.h,v 1.7 2001/06/08 23:01:58 davem Exp $ * include/asm-sparc/linux_logo.h: This is a linux logo * to be displayed on boot. * diff --git a/include/asm-sparc/softirq.h b/include/asm-sparc/softirq.h index 92dde254b189..f63dea3a743c 100644 --- a/include/asm-sparc/softirq.h +++ b/include/asm-sparc/softirq.h @@ -14,8 +14,21 @@ #include #define local_bh_disable() (local_bh_count(smp_processor_id())++) -#define local_bh_enable() (local_bh_count(smp_processor_id())--) - +#define __local_bh_enable() (local_bh_count(smp_processor_id())--) +#define local_bh_enable() \ +do { if (!--local_bh_count(smp_processor_id()) && \ + softirq_pending(smp_processor_id())) { \ + do_softirq(); \ + __sti(); \ + } \ +} while (0) +#define __cpu_raise_softirq(cpu, nr) (softirq_pending(cpu) |= (1< /* for membar() */ #define local_bh_disable() (local_bh_count(smp_processor_id())++) -#define local_bh_enable() (local_bh_count(smp_processor_id())--) - +#define __local_bh_enable() (local_bh_count(smp_processor_id())--) +#define local_bh_enable() \ +do { if (!--local_bh_count(smp_processor_id()) && \ + softirq_pending(smp_processor_id())) { \ + do_softirq(); \ + __sti(); \ + } \ +} while (0) +#define __cpu_raise_softirq(cpu, nr) (softirq_pending(cpu) |= (1< +#include #include #include +#include + +/* + * You can optimize the code size and performance by defining only + * the geometry(ies) available on your hardware. + * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width) + * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2 or 4 bytes) + * + * By default, all (known) geometries are supported. + */ + +#ifndef CONFIG_MTD_CFI_GEOMETRY + +#define CFIDEV_INTERLEAVE_1 (1) +#define CFIDEV_INTERLEAVE_2 (2) +#define CFIDEV_INTERLEAVE_4 (4) + +#define CFIDEV_BUSWIDTH_1 (1) +#define CFIDEV_BUSWIDTH_2 (2) +#define CFIDEV_BUSWIDTH_4 (4) + +#else + +#ifdef CONFIG_MTD_CFI_I1 +#define CFIDEV_INTERLEAVE_1 (1) +#endif +#ifdef CONFIG_MTD_CFI_I2 +#define CFIDEV_INTERLEAVE_2 (2) +#endif +#ifdef CONFIG_MTD_CFI_I4 +#define CFIDEV_INTERLEAVE_4 (4) +#endif + +#ifdef CONFIG_MTD_CFI_B1 +#define CFIDEV_BUSWIDTH_1 (1) +#endif +#ifdef CONFIG_MTD_CFI_B2 +#define CFIDEV_BUSWIDTH_2 (2) +#endif +#ifdef CONFIG_MTD_CFI_B4 +#define CFIDEV_BUSWIDTH_4 (4) +#endif + +#endif + +/* + * The following macros are used to select the code to execute: + * cfi_buswidth_is_*() + * cfi_interleave_is_*() + * [where * is either 1, 2 or 4] + * Those macros should be used with 'if' statements. If only one of few + * geometry arrangements are selected, they expand to constants thus allowing + * the compiler (most of them being 0) to optimize away all the unneeded code, + * while still validating the syntax (which is not possible with embedded + * #if ... #endif constructs). + */ + +#ifdef CFIDEV_INTERLEAVE_1 +# ifdef CFIDEV_INTERLEAVE +# undef CFIDEV_INTERLEAVE +# define CFIDEV_INTERLEAVE (cfi->interleave) +# else +# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1 +# endif +# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1) +#else +# define cfi_interleave_is_1() (0) +#endif + +#ifdef CFIDEV_INTERLEAVE_2 +# ifdef CFIDEV_INTERLEAVE +# undef CFIDEV_INTERLEAVE +# define CFIDEV_INTERLEAVE (cfi->interleave) +# else +# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2 +# endif +# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2) +#else +# define cfi_interleave_is_2() (0) +#endif + +#ifdef CFIDEV_INTERLEAVE_4 +# ifdef CFIDEV_INTERLEAVE +# undef CFIDEV_INTERLEAVE +# define CFIDEV_INTERLEAVE (cfi->interleave) +# else +# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4 +# endif +# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4) +#else +# define cfi_interleave_is_4() (0) +#endif + +#ifndef CFIDEV_INTERLEAVE +#error You must define at least one interleave to support! +#endif + +#ifdef CFIDEV_BUSWIDTH_1 +# ifdef CFIDEV_BUSWIDTH +# undef CFIDEV_BUSWIDTH +# define CFIDEV_BUSWIDTH (map->buswidth) +# else +# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1 +# endif +# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1) +#else +# define cfi_buswidth_is_1() (0) +#endif + +#ifdef CFIDEV_BUSWIDTH_2 +# ifdef CFIDEV_BUSWIDTH +# undef CFIDEV_BUSWIDTH +# define CFIDEV_BUSWIDTH (map->buswidth) +# else +# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2 +# endif +# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2) +#else +# define cfi_buswidth_is_2() (0) +#endif + +#ifdef CFIDEV_BUSWIDTH_4 +# ifdef CFIDEV_BUSWIDTH +# undef CFIDEV_BUSWIDTH +# define CFIDEV_BUSWIDTH (map->buswidth) +# else +# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4 +# endif +# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4) +#else +# define cfi_buswidth_is_4() (0) +#endif + +#ifndef CFIDEV_BUSWIDTH +#error You must define at least one bus width to support! +#endif + +/* NB: these values must represents the number of bytes needed to meet the + * device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes. + * These numbers are used in calculations. + */ +#define CFI_DEVICETYPE_X8 (8 / 8) +#define CFI_DEVICETYPE_X16 (16 / 8) +#define CFI_DEVICETYPE_X32 (32 / 8) /* NB: We keep these structures in memory in HOST byteorder, except * where individually noted. @@ -37,7 +183,7 @@ struct cfi_ident { __u16 InterfaceDesc; __u16 MaxBufWriteSize; __u8 NumEraseRegions; - __u32 EraseRegionInfo[1]; /* Not host ordered */ + __u32 EraseRegionInfo[0]; /* Not host ordered */ } __attribute__((packed)); /* Extended Query Structure for both PRI and ALT */ @@ -82,20 +228,164 @@ struct cfi_bri_query { #define P_ID_RESERVED 65535 +#define CFI_MODE_CFI 0 +#define CFI_MODE_JEDEC 1 + struct cfi_private { __u16 cmdset; void *cmdset_priv; int interleave; + int device_type; + int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */ + int addr_unlock1; + int addr_unlock2; + int fast_prog; struct mtd_info *(*cmdset_setup)(struct map_info *); - struct cfi_ident cfiq; /* For now only one. We insist that all devs + struct cfi_ident *cfiq; /* For now only one. We insist that all devs must be of the same type. */ + __u8 mfr, id; int numchips; unsigned long chipshift; /* Because they're of the same type */ const char *im_name; /* inter_module name for cmdset_setup */ struct flchip chips[0]; /* per-chip data structure for each chip */ - /* do not add extra fields after "chips" */ }; #define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */ +/* + * Returns the command address according to the given geometry. + */ +static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type) +{ + return (cmd_ofs * type) * interleave; +} + +/* + * Transforms the CFI command for the given geometry (bus width & interleave. + */ +static inline __u32 cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) +{ + __u32 val = 0; + + if (cfi_buswidth_is_1()) { + /* 1 x8 device */ + val = cmd; + } else if (cfi_buswidth_is_2()) { + if (cfi_interleave_is_1()) { + /* 1 x16 device in x16 mode */ + val = cpu_to_cfi16(cmd); + } else if (cfi_interleave_is_2()) { + /* 2 (x8, x16 or x32) devices in x8 mode */ + val = cpu_to_cfi16((cmd << 8) | cmd); + } + } else if (cfi_buswidth_is_4()) { + if (cfi_interleave_is_1()) { + /* 1 x32 device in x32 mode */ + val = cpu_to_cfi32(cmd); + } else if (cfi_interleave_is_2()) { + /* 2 x16 device in x16 mode */ + val = cpu_to_cfi32((cmd << 16) | cmd); + } else if (cfi_interleave_is_4()) { + /* 4 (x8, x16 or x32) devices in x8 mode */ + val = (cmd << 16) | cmd; + val = cpu_to_cfi32((val << 8) | val); + } + } + return val; +} +#define CMD(x) cfi_build_cmd((x), map, cfi) + +/* + * Read a value according to the bus width. + */ + +static inline __u32 cfi_read(struct map_info *map, __u32 addr) +{ + if (cfi_buswidth_is_1()) { + return map->read8(map, addr); + } else if (cfi_buswidth_is_2()) { + return map->read16(map, addr); + } else if (cfi_buswidth_is_4()) { + return map->read32(map, addr); + } else { + return 0; + } +} + +/* + * Write a value according to the bus width. + */ + +static inline void cfi_write(struct map_info *map, __u32 val, __u32 addr) +{ + if (cfi_buswidth_is_1()) { + map->write8(map, val, addr); + } else if (cfi_buswidth_is_2()) { + map->write16(map, val, addr); + } else if (cfi_buswidth_is_4()) { + map->write32(map, val, addr); + } +} + +/* + * Sends a CFI command to a bank of flash for the given geometry. + * + * Returns the offset in flash where the command was written. + * If prev_val is non-null, it will be set to the value at the command address, + * before the command was written. + */ +static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base, + struct map_info *map, struct cfi_private *cfi, + int type, __u32 *prev_val) +{ + __u32 val; + __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type); + + val = cfi_build_cmd(cmd, map, cfi); + + if (prev_val) + *prev_val = cfi_read(map, addr); + + cfi_write(map, val, addr); + + return addr - base; +} + +static inline __u8 cfi_read_query(struct map_info *map, __u32 addr) +{ + if (cfi_buswidth_is_1()) { + return map->read8(map, addr); + } else if (cfi_buswidth_is_2()) { + return cfi16_to_cpu(map->read16(map, addr)); + } else if (cfi_buswidth_is_4()) { + return cfi32_to_cpu(map->read32(map, addr)); + } else { + return 0; + } +} + +#ifndef min +#define min(x,y) ( (x)<(y)?(x):(y) ) +#endif + +static inline void cfi_udelay(int us) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (current->need_resched) + schedule(); + else +#endif + udelay(us); +} +static inline void cfi_spin_lock(spinlock_t *mutex) +{ + spin_lock_bh(mutex); +} + +static inline void cfi_spin_unlock(spinlock_t *mutex) +{ + spin_unlock_bh(mutex); +} + + #endif /* __MTD_CFI_H__ */ diff --git a/include/linux/mtd/cfi_endian.h b/include/linux/mtd/cfi_endian.h new file mode 100644 index 000000000000..052139bf210d --- /dev/null +++ b/include/linux/mtd/cfi_endian.h @@ -0,0 +1,141 @@ +/* + * $Id: cfi_endian.h,v 1.9 2001/04/23 21:19:11 nico Exp $ + * + * It seems that some helpful people decided to make life easier + * for software engineers who aren't capable of dealing with the + * concept of byteswapping, and advise engineers to swap the bytes + * by wiring the data lines up to flash chips from BE hosts backwards. + * + * So we have ugly stuff here to disable the byteswapping where necessary. + * I'm not going to try to do this dynamically. + * + * At first I thought these guys were on crack, but then I discovered the + * LART. + * + */ + +#include + +#ifndef CONFIG_MTD_CFI_ADV_OPTIONS + +#define CFI_HOST_ENDIAN + +#else + +#ifdef CONFIG_MTD_CFI_NOSWAP +#define CFI_HOST_ENDIAN +#endif + +#ifdef CONFIG_MTD_CFI_LE_BYTE_SWAP +#define CFI_LITTLE_ENDIAN +#endif + +#ifdef CONFIG_MTD_CFI_BE_BYTE_SWAP +#define CFI_BIG_ENDIAN +#endif + +#ifdef CONFIG_MTD_CFI_LART_BIT_SWAP +#define CFI_LART_ENDIAN +#endif + +#endif + +#if defined(CFI_LITTLE_ENDIAN) +#define cpu_to_cfi8(x) (x) +#define cfi8_to_cpu(x) (x) +#define cpu_to_cfi16(x) cpu_to_le16(x) +#define cpu_to_cfi32(x) cpu_to_le32(x) +#define cfi16_to_cpu(x) le16_to_cpu(x) +#define cfi32_to_cpu(x) le32_to_cpu(x) +#elif defined (CFI_BIG_ENDIAN) +#define cpu_to_cfi8(x) (x) +#define cfi8_to_cpu(x) (x) +#define cpu_to_cfi16(x) cpu_to_be16(x) +#define cpu_to_cfi32(x) cpu_to_be32(x) +#define cfi16_to_cpu(x) be16_to_cpu(x) +#define cfi32_to_cpu(x) be32_to_cpu(x) +#elif defined (CFI_HOST_ENDIAN) +#define cpu_to_cfi8(x) (x) +#define cfi8_to_cpu(x) (x) +#define cpu_to_cfi16(x) (x) +#define cpu_to_cfi32(x) (x) +#define cfi16_to_cpu(x) (x) +#define cfi32_to_cpu(x) (x) +#elif defined (CFI_LART_ENDIAN) +/* + Fuck me backwards. The data line mapping on LART is as follows: + + U2 CPU | U3 CPU + 0 20 | 0 12 + 1 22 | 1 14 + 2 19 | 2 11 + 3 17 | 3 9 + 4 24 | 4 0 + 5 26 | 5 2 + 6 31 | 6 7 + 7 29 | 7 5 + 8 21 | 8 13 + 9 23 | 9 15 + 10 18 | 10 10 + 11 16 | 11 8 + 12 25 | 12 1 + 13 27 | 13 3 + 14 30 | 14 6 + 15 28 | 15 4 + + For historical reference: the reason why the LART has this strange + mapping is that the designer of the board wanted address lines to + be as short as possible. Why? Because in that way you don't need + drivers in the address lines so the memory access time can be held + short. -- Erik Mouw +*/ +/* cpu_to_cfi16() and cfi16_to_cpu() are not needed because the LART + * only has 32 bit wide Flash memory. -- Erik + */ +#define cpu_to_cfi16(x) (x) +#define cfi16_to_cpu(x) (x) +static inline __u32 cfi32_to_cpu(__u32 x) +{ + __u32 ret; + + ret = (x & 0x08009000) >> 11; + ret |= (x & 0x00002000) >> 10; + ret |= (x & 0x04004000) >> 8; + ret |= (x & 0x00000010) >> 4; + ret |= (x & 0x91000820) >> 3; + ret |= (x & 0x22080080) >> 2; + ret |= (x & 0x40000400); + ret |= (x & 0x00040040) << 1; + ret |= (x & 0x00110000) << 4; + ret |= (x & 0x00220100) << 5; + ret |= (x & 0x00800208) << 6; + ret |= (x & 0x00400004) << 9; + ret |= (x & 0x00000001) << 12; + ret |= (x & 0x00000002) << 13; + + return ret; +} +static inline __u32 cpu_to_cfi32(__u32 x) +{ + __u32 ret; + + ret = (x & 0x00010012) << 11; + ret |= (x & 0x00000008) << 10; + ret |= (x & 0x00040040) << 8; + ret |= (x & 0x00000001) << 4; + ret |= (x & 0x12200104) << 3; + ret |= (x & 0x08820020) << 2; + ret |= (x & 0x40000400); + ret |= (x & 0x00080080) >> 1; + ret |= (x & 0x01100000) >> 4; + ret |= (x & 0x04402000) >> 5; + ret |= (x & 0x20008200) >> 6; + ret |= (x & 0x80000800) >> 9; + ret |= (x & 0x00001000) >> 12; + ret |= (x & 0x00004000) >> 13; + + return ret; +} +#else +#error No CFI endianness defined +#endif diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 696938aa1853..c8e143a1f723 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -2,7 +2,7 @@ /* Linux driver for Disk-On-Chip 2000 */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: doc2000.h,v 1.12 2000/11/03 12:43:43 dwmw2 Exp $ */ +/* $Id: doc2000.h,v 1.13 2001/05/29 12:03:45 dwmw2 Exp $ */ #ifndef __MTD_DOC2000_H__ #define __MTD_DOC2000_H__ @@ -43,15 +43,19 @@ * On PPC, it's mmap'd and 16-bit wide. * Others use readb/writeb */ -#if defined(__arm__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+(reg<<2)))) -#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+(reg<<2)) = (__u32)d} while(0) +#if defined(__arm__) +#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2)))) +#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) +#define DOC_IOREMAP_LEN 0x8000 #elif defined(__ppc__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+(reg<<1)))) -#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+(reg<<1)) = (__u16)d} while(0) +#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1)))) +#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) +#define DOC_IOREMAP_LEN 0x4000 #else -#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + reg) -#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + reg) +#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) +#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + (reg)) +#define DOC_IOREMAP_LEN 0x2000 + #endif #if defined(__i386__) diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index 17c7cec2ccd2..4cdccad20abe 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.4 2000/07/03 12:58:41 dwmw2 Exp $ + * $Id: flashchip.h,v 1.7 2001/01/18 03:52:36 nico Exp $ * */ @@ -25,12 +25,17 @@ typedef enum { FL_CFI_QUERY, FL_JEDEC_QUERY, FL_ERASING, + FL_ERASE_SUSPENDING, FL_ERASE_SUSPENDED, FL_WRITING, + FL_WRITING_TO_BUFFER, + FL_WRITE_SUSPENDING, FL_WRITE_SUSPENDED, FL_PM_SUSPENDED, FL_SYNCING, FL_UNLOADING, + FL_LOCKING, + FL_UNLOCKING, FL_UNKNOWN } flstate_t; diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 71fc46b1a7fa..84553406e6b1 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.10.2.2 2001/01/09 00:44:51 dwmw2 Exp $ */ +/* $Id: map.h,v 1.24 2001/06/09 19:53:16 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -53,47 +53,23 @@ struct map_info { unsigned long map_priv_1; unsigned long map_priv_2; void *fldrv_priv; - void (*fldrv_destroy)(struct mtd_info *); - const char *im_name; + struct mtd_chip_driver *fldrv; }; -#ifdef CONFIG_MODULES -/* - * Probe for the contents of a map device and make an MTD structure - * if anything is recognised. Doesn't register it because the calling - * map driver needs to set the 'module' field first. - */ -static inline struct mtd_info *do_map_probe(struct map_info *map, const char *funcname, const char *modname) -{ - struct mtd_info *(*probe_p)(struct map_info *); - struct mtd_info *mtd = NULL; - if ((probe_p = inter_module_get_request(modname, funcname))) - mtd = (*probe_p)(map); /* map->im_name is set by probe */ - - return mtd; -} +struct mtd_chip_driver { + struct mtd_info *(*probe)(struct map_info *map); + void (*destroy)(struct mtd_info *); + struct module *module; + char *name; + struct list_head list; +}; +void register_mtd_chip_driver(struct mtd_chip_driver *); +void unregister_mtd_chip_driver(struct mtd_chip_driver *); -/* - * Commonly-used probe functions for different types of chip. - */ -#define do_cfi_probe(x) do_map_probe(x, "cfi_probe", "cfi_probe") -#define do_jedec_probe(x) do_map_probe(x, "jedec_probe", "jedec_probe") -#define do_ram_probe(x) do_map_probe(x, "map_ram_probe", "map_ram") -#define do_rom_probe(x) do_map_probe(x, "map_rom_probe", "map_rom") -#else - /* without module support, call probe function directly */ -extern struct mtd_info *cfi_probe(struct map_info *); -extern struct mtd_info *jedec_probe(struct map_info *); -extern struct mtd_info *map_ram_probe(struct map_info *); -extern struct mtd_info *map_rom_probe(struct map_info *); +struct mtd_info *do_map_probe(char *name, struct map_info *map); -#define do_cfi_probe(x) cfi_probe(x) -#define do_jedec_probe(x) jedec_probe(x) -#define do_ram_probe(x) map_ram_probe(x) -#define do_rom_probe(x) map_rom_probe(x) -#endif /* * Destroy an MTD device which was created for a map device. @@ -103,8 +79,11 @@ static inline void map_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; - map->fldrv_destroy(mtd); - inter_module_put(map->im_name); + map->fldrv->destroy(mtd); +#ifdef CONFIG_MODULES + if (map->fldrv->module) + __MOD_DEC_USE_COUNT(map->fldrv->module); +#endif kfree(mtd); } diff --git a/include/linux/mtd/mapped.h b/include/linux/mtd/mapped.h deleted file mode 100644 index 81668b71e916..000000000000 --- a/include/linux/mtd/mapped.h +++ /dev/null @@ -1,92 +0,0 @@ -// -*- mode: cpp; mode: fold -*- -// Description /*{{{*/ -// $Id: mapped.h,v 1.2 2000/03/14 17:13:12 dwmw2 Exp $ -/* ###################################################################### - - Memory Mapped MTD Routines - - These routines are support routines for memory mapped chips, with - routines to support common sorts of flash. For devices that are based - on a memory mapped interface these routines provide everything necessary, - only a window changing function is required by the low level implementation. - - The entry point to setup and register a memory mapped MTD device, - mtd_mapped_setup will perform a detection sequence that can determine - the type size and configuration of many sorts of chip setups. - - ROMs and RAMs are detected and passed off to very simple routines, Flash - writing and erasing is handled as well. - - ##################################################################### */ - /*}}}*/ -#ifndef __MTD_FLASH_H__ -#define __MTD_FLASH_H__ - -#include -#include - -// MTD flags for ordinary flash -struct JEDECTable -{ - u_short jedec; - char *name; - u_long size; - u_long sectorsize; - u_long capabilities; -}; - -// JEDEC being 0 is the end of the chip array -struct flash_chip -{ - u_short jedec; - u_long size; - u_long sectorsize; - u_long base; - u_long capabilities; - - // These markers are filled in by the flash_chip_scan function - u_long start; - u_long length; -}; - -struct mapped_mtd_info -{ - struct mtd_info mtd; - u_long pagesize; // Size of the memory window - u_long maxsize; // Maximum MTD size in pages - u_char mfr,id; - char part[100]; // Part Catalogue number if available - int *lock; - // Multiple chip support, only used if this is type MTD_FLASH - u_char interleve; // Address chip interleve (0 = concatination) - struct flash_chip chips[5]; - - // Operations - unsigned long (*page)(struct mapped_mtd_info *map,unsigned long page); - int (*jedec_sense)(struct mapped_mtd_info *map); -}; - -extern struct JEDECTable mtd_JEDEC_table[]; - -// Automatic configurators -extern int mtd_mapped_setup(struct mapped_mtd_info *map); -extern int mtd_mapped_remove(struct mapped_mtd_info *map); - -// Generic functions -extern int flash_jedec(struct mapped_mtd_info *map); -extern int flash_erase(struct mtd_info *map, struct erase_info *instr); -extern int flash_write(struct mtd_info *map, loff_t start, size_t len, - size_t *retlen, const u_char *buf); -extern int rom_read(struct mtd_info *map, loff_t start, size_t len, - size_t *retlen, u_char *buf); -extern int ram_write(struct mtd_info *map, loff_t start, size_t len, - size_t *retlen, const u_char *buf); - -// Helpers -extern int page_jump(struct mapped_mtd_info *map,unsigned long start, - unsigned long len,unsigned long *buffer, - unsigned long *size); -extern void flash_chip_scan(struct mapped_mtd_info *map,unsigned long start, - unsigned long len); - -#endif /* __MTD_FLASH_H__ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a703b853e56a..ae5bfe3e10fb 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,5 +1,5 @@ -/* $Id: mtd.h,v 1.26 2000/10/30 17:18:04 sjhill Exp $ */ +/* $Id: mtd.h,v 1.33 2001/06/09 00:08:59 dwmw2 Exp $ */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ @@ -16,13 +16,13 @@ #endif /* __KERNEL__ */ struct erase_info_user { - unsigned long start; - unsigned long length; + u_int32_t start; + u_int32_t length; }; struct mtd_oob_buf { - loff_t start; - ssize_t length; + u_int32_t start; + u_int32_t length; unsigned char *ptr; }; @@ -68,13 +68,21 @@ struct mtd_oob_buf { struct mtd_info_user { u_char type; - u_long flags; - u_long size; // Total size of the MTD - u_long erasesize; - u_long oobblock; // Size of OOB blocks (e.g. 512) - u_long oobsize; // Amount of OOB data per block (e.g. 16) - u_long ecctype; - u_long eccsize; + u_int32_t flags; + u_int32_t size; // Total size of the MTD + u_int32_t erasesize; + u_int32_t oobblock; // Size of OOB blocks (e.g. 512) + u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) + u_int32_t ecctype; + u_int32_t eccsize; +}; + +struct region_info_user { + u_int32_t offset; /* At which this region starts, + * from the beginning of the MTD */ + u_int32_t erasesize; /* For this region */ + u_int32_t numblocks; /* Number of blocks in this region */ + u_int32_t regionindex; }; #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) @@ -83,11 +91,14 @@ struct mtd_info_user { #define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) #define MEMLOCK _IOW('M', 5, struct erase_info_user) #define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) #ifndef __KERNEL__ typedef struct mtd_info_user mtd_info_t; typedef struct erase_info_user erase_info_t; +typedef struct region_info_user region_info_t; /* User-space ioctl definitions */ @@ -103,8 +114,8 @@ typedef struct erase_info_user erase_info_t; struct erase_info { struct mtd_info *mtd; - u_long addr; - u_long len; + u_int32_t addr; + u_int32_t len; u_long time; u_long retries; u_int dev; @@ -115,22 +126,40 @@ struct erase_info { struct erase_info *next; }; +struct mtd_erase_region_info { + u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ + u_int32_t erasesize; /* For this region */ + u_int32_t numblocks; /* Number of blocks of erasesize in this region */ +}; struct mtd_info { u_char type; - u_long flags; - u_long size; // Total size of the MTD - u_long erasesize; - u_long oobblock; // Size of OOB blocks (e.g. 512) - u_long oobsize; // Amount of OOB data per block (e.g. 16) - u_long ecctype; - u_long eccsize; + u_int32_t flags; + u_int32_t size; // Total size of the MTD + + /* "Major" erase size for the device. Naïve users may take this + * to be the only erase size available, or may use the more detailed + * information below if they desire + */ + u_int32_t erasesize; + + u_int32_t oobblock; // Size of OOB blocks (e.g. 512) + u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) + u_int32_t ecctype; + u_int32_t eccsize; // Kernel-only stuff starts here. char *name; int index; - u_long bank_size; + /* Data for variable erase regions. If numeraseregions is zero, + * it means that the whole device has erasesize as given above. + */ + int numeraseregions; + struct mtd_erase_region_info *eraseregions; + + /* This really shouldn't be here. It can go away in 2.5 */ + u_int32_t bank_size; struct module *module; int (*erase) (struct mtd_info *mtd, struct erase_info *instr); diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h new file mode 100644 index 000000000000..f3f1d7e0f6aa --- /dev/null +++ b/include/linux/mtd/nand_ecc.h @@ -0,0 +1,28 @@ +/* + * drivers/mtd/nand_ecc.h + * + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * $Id: nand_ecc.h,v 1.1 2000/10/12 00:57:15 sjhill 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 + * published by the Free Software Foundation. + * + * This file is the header for the ECC algorithm. + */ + +/* + * Creates non-inverted ECC code from line parity + */ +void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code); + +/* + * Calculate 3 byte ECC code for 256 byte block + */ +void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); + +/* + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index ae00ceb0ba2a..3483a8c4022d 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -2,7 +2,7 @@ /* Defines for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftl.h,v 1.9 2000/11/07 05:48:49 ollie Exp $ */ +/* $Id: nftl.h,v 1.10 2000/12/29 00:25:38 dwmw2 Exp $ */ #ifndef __MTD_NFTL_H__ #define __MTD_NFTL_H__ @@ -115,6 +115,7 @@ int NFTL_formatblock(struct NFTLrecord *s, int block); #define MAX_NFTLS 16 #define MAX_SECTORS_PER_UNIT 32 +#define NFTL_PARTN_BITS 4 #endif /* __KERNEL__ */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index a92023886008..1d3351cc1eca 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: partitions.h,v 1.3 2000/11/10 23:35:12 nico Exp $ + * $Id: partitions.h,v 1.6 2001/03/17 17:10:21 dwmw2 Exp $ */ #ifndef MTD_PARTITIONS_H @@ -22,10 +22,11 @@ * * For each partition, these fields are available: * name: string that will be used to label the partition's MTD device. - * size: the partition size; if 0, the partition will extend to the end of the - * master MTD device. - * offset: absolute starting position within the master MTD device; if 0, - * partition will start where the previous one ended. + * size: the partition size; if defined as MTDPART_SIZ_FULL, the partition + * will extend to the end of the master MTD device. + * offset: absolute starting position within the master MTD device; if + * defined as MTDPART_OFS_APPEND, the partition will start where the + * previous one ended. * mask_flags: contains flags that have to be masked (removed) from the * master MTD flag set for the corresponding MTD partition. * For example, to force a read-only partition, simply adding @@ -37,11 +38,14 @@ struct mtd_partition { char *name; /* identifier string */ - u_long size; /* partition size */ - u_long offset; /* offset within the master MTD space */ - u_long mask_flags; /* master MTD flags to mask out for this partition */ + u_int32_t size; /* partition size */ + u_int32_t offset; /* offset within the master MTD space */ + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ }; +#define MTDPART_OFS_APPEND (-1) +#define MTDPART_SIZ_FULL (0) + int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); diff --git a/include/linux/pci.h b/include/linux/pci.h index d69cc5182cd8..aa3aacf0dd70 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -327,6 +327,9 @@ enum pci_mmap_state { #define pci_for_each_dev_reverse(dev) \ for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev)) +#define pci_for_each_bus(bus) \ +for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses); bus = pci_bus_b(bus->node.next)) + /* * The pci_dev structure is used to describe both PCI and ISAPnP devices. */ @@ -356,6 +359,10 @@ struct pci_dev { this if your device has broken DMA or supports 64-bit transfers. */ + u32 current_state; /* Current operating state. In ACPI-speak, + this is D0-D3, D0 being fully functional, + and D3 being off. */ + /* device is compatible with these IDs */ unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE]; unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE]; @@ -469,10 +476,11 @@ struct pci_driver { struct list_head node; char *name; const struct pci_device_id *id_table; /* NULL if wants all devices */ - int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ - void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ - void (*suspend)(struct pci_dev *dev); /* Device suspended */ - void (*resume)(struct pci_dev *dev); /* Device woken up */ + int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ + void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ + int (*suspend)(struct pci_dev *dev, u32 state); /* Device suspended */ + int (*resume) (struct pci_dev *dev); /* Device woken up */ + int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); /* Enable wake event */ }; @@ -547,14 +555,18 @@ int pci_write_config_byte(struct pci_dev *dev, int where, u8 val); int pci_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(struct pci_dev *dev, int where, u32 val); -#define HAVE_PCI_DISABLE_DEVICE int pci_enable_device(struct pci_dev *dev); void pci_disable_device(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev); int pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask); -int pci_set_power_state(struct pci_dev *dev, int state); int pci_assign_resource(struct pci_dev *dev, int i); +/* Power management related routines */ +int pci_save_state(struct pci_dev *dev, u32 *buffer); +int pci_restore_state(struct pci_dev *dev, u32 *buffer); +int pci_set_power_state(struct pci_dev *dev, int state); +int pci_enable_wake(struct pci_dev *dev, u32 state, int enable); + /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ int pci_claim_resource(struct pci_dev *, int); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5599f65984cb..a5cb21c2f302 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -160,20 +160,37 @@ #define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 #define PCI_VENDOR_ID_ATI 0x1002 +/* Mach64 */ #define PCI_DEVICE_ID_ATI_68800 0x4158 #define PCI_DEVICE_ID_ATI_215CT222 0x4354 #define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215ET222 0x4554 +/* Mach64 / Rage */ #define PCI_DEVICE_ID_ATI_215GB 0x4742 #define PCI_DEVICE_ID_ATI_215GD 0x4744 #define PCI_DEVICE_ID_ATI_215GI 0x4749 #define PCI_DEVICE_ID_ATI_215GP 0x4750 #define PCI_DEVICE_ID_ATI_215GQ 0x4751 +#define PCI_DEVICE_ID_ATI_215XL 0x4752 #define PCI_DEVICE_ID_ATI_215GT 0x4754 #define PCI_DEVICE_ID_ATI_215GTB 0x4755 +#define PCI_DEVICE_ID_ATI_215_IV 0x4756 +#define PCI_DEVICE_ID_ATI_215_IW 0x4757 +#define PCI_DEVICE_ID_ATI_215_IZ 0x475A #define PCI_DEVICE_ID_ATI_210888GX 0x4758 -#define PCI_DEVICE_ID_ATI_215LG 0x4c47 -#define PCI_DEVICE_ID_ATI_264LT 0x4c54 +#define PCI_DEVICE_ID_ATI_215_LB 0x4c42 +#define PCI_DEVICE_ID_ATI_215_LD 0x4c44 +#define PCI_DEVICE_ID_ATI_215_LG 0x4c47 +#define PCI_DEVICE_ID_ATI_215_LI 0x4c49 +#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D +#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E +#define PCI_DEVICE_ID_ATI_215_LR 0x4c52 +#define PCI_DEVICE_ID_ATI_215_LS 0x4c53 +#define PCI_DEVICE_ID_ATI_264_LT 0x4c54 +/* Mach64 VT */ #define PCI_DEVICE_ID_ATI_264VT 0x5654 +#define PCI_DEVICE_ID_ATI_264VU 0x5655 +#define PCI_DEVICE_ID_ATI_264VV 0x5656 /* Rage128 Pro GL */ #define PCI_DEVICE_ID_ATI_Rage128_PA 0x5041 #define PCI_DEVICE_ID_ATI_Rage128_PB 0x5042 @@ -1135,6 +1152,7 @@ #define PCI_DEVICE_ID_AVM_B1 0x0700 #define PCI_DEVICE_ID_AVM_C4 0x0800 #define PCI_DEVICE_ID_AVM_A1 0x0a00 +#define PCI_DEVICE_ID_AVM_C2 0x1100 #define PCI_DEVICE_ID_AVM_T1 0x1200 #define PCI_VENDOR_ID_DIPIX 0x1246 diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 8dea93d1aae0..dff8588a2bfe 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -21,7 +21,6 @@ */ extern void dquot_initialize(struct inode *inode, short type); extern void dquot_drop(struct inode *inode); -extern void invalidate_dquots(kdev_t dev, short type); extern int quota_off(struct super_block *sb, short type); extern int sync_dquots(kdev_t dev, short type); diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index d02476de1485..f46154e9fdac 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -1806,6 +1806,10 @@ void reiserfs_delete_inode (struct inode * inode); extern int reiserfs_notify_change(struct dentry * dentry, struct iattr * attr); void reiserfs_write_inode (struct inode * inode, int) ; +/* nfsd support functions */ +struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent); +int reiserfs_dentry_to_fh(struct dentry *, __u32 *fh, int *lenp, int need_parent); + /* we don't mark inodes dirty, we just log them */ void reiserfs_dirty_inode (struct inode * inode) ; diff --git a/include/linux/swap.h b/include/linux/swap.h index d43f5d981d7c..71ad9d7f3773 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -205,6 +205,16 @@ extern spinlock_t pagemap_lru_lock; page->zone->inactive_dirty_pages++; \ } +/* Like the above, but add us after the bookmark. */ +#define add_page_to_inactive_dirty_list_marker(page) { \ + DEBUG_ADD_PAGE \ + ZERO_PAGE_BUG \ + SetPageInactiveDirty(page); \ + list_add(&(page)->lru, marker_lru); \ + nr_inactive_dirty_pages++; \ + page->zone->inactive_dirty_pages++; \ +} + #define add_page_to_inactive_clean_list(page) { \ DEBUG_ADD_PAGE \ ZERO_PAGE_BUG \ diff --git a/kernel/timer.c b/kernel/timer.c index 579b065f3f46..1a86dfcb5481 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -32,7 +32,7 @@ long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ /* The current time */ -volatile struct timeval xtime __attribute__ ((aligned (16))); +struct timeval xtime __attribute__ ((aligned (16))); /* Don't completely fail for HZ > 500. */ int tickadj = 500/HZ ? : 1; /* microsecs */ diff --git a/mm/filemap.c b/mm/filemap.c index b90b423c330a..86b9354a2424 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -230,17 +230,17 @@ static int truncate_list_pages(struct list_head *head, unsigned long start, unsi unsigned long offset; page = list_entry(curr, struct page, list); - curr = curr->next; offset = page->index; /* Is one of the pages to truncate? */ if ((offset >= start) || (*partial && (offset + 1) == start)) { + list_del(head); + list_add(head, curr); if (TryLockPage(page)) { page_cache_get(page); spin_unlock(&pagecache_lock); wait_on_page(page); - page_cache_release(page); - return 1; + goto out_restart; } page_cache_get(page); spin_unlock(&pagecache_lock); @@ -252,11 +252,15 @@ static int truncate_list_pages(struct list_head *head, unsigned long start, unsi truncate_complete_page(page); UnlockPage(page); - page_cache_release(page); - return 1; + goto out_restart; } + curr = curr->next; } return 0; +out_restart: + page_cache_release(page); + spin_lock(&pagecache_lock); + return 1; } @@ -273,15 +277,19 @@ void truncate_inode_pages(struct address_space * mapping, loff_t lstart) { unsigned long start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); + int complete; -repeat: spin_lock(&pagecache_lock); - if (truncate_list_pages(&mapping->clean_pages, start, &partial)) - goto repeat; - if (truncate_list_pages(&mapping->dirty_pages, start, &partial)) - goto repeat; - if (truncate_list_pages(&mapping->locked_pages, start, &partial)) - goto repeat; + do { + complete = 1; + while (truncate_list_pages(&mapping->clean_pages, start, &partial)) + complete = 0; + while (truncate_list_pages(&mapping->dirty_pages, start, &partial)) + complete = 0; + while (truncate_list_pages(&mapping->locked_pages, start, &partial)) + complete = 0; + } while (!complete); + /* Traversed all three lists without dropping the lock */ spin_unlock(&pagecache_lock); } diff --git a/mm/vmscan.c b/mm/vmscan.c index 2f2b359f9c98..c85c452ecf77 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -265,7 +265,7 @@ out_unlock: return !count; } -#define SWAP_MM_SHIFT 2 +#define SWAP_MM_SHIFT 4 #define SWAP_SHIFT 5 #define SWAP_MIN 8 @@ -407,7 +407,7 @@ out: /** * page_launder - clean dirty inactive pages, move to inactive_clean list * @gfp_mask: what operations we are allowed to do - * @sync: should we wait synchronously for the cleaning of pages + * @sync: are we allowed to do synchronous IO in emergencies ? * * When this function is called, we are most likely low on free + * inactive_clean pages. Since we want to refill those pages as @@ -426,23 +426,61 @@ out: #define MAX_LAUNDER (4 * (1 << page_cluster)) #define CAN_DO_IO (gfp_mask & __GFP_IO) #define CAN_DO_BUFFERS (gfp_mask & __GFP_BUFFER) +#define marker_lru (&marker_page_struct.lru) int page_launder(int gfp_mask, int sync) { + static int cannot_free_pages; int launder_loop, maxscan, cleaned_pages, maxlaunder; struct list_head * page_lru; struct page * page; + /* Our bookmark of where we are in the inactive_dirty list. */ + struct page marker_page_struct = { zone: NULL }; + launder_loop = 0; maxlaunder = 0; cleaned_pages = 0; dirty_page_rescan: spin_lock(&pagemap_lru_lock); - maxscan = nr_inactive_dirty_pages; - while ((page_lru = inactive_dirty_list.prev) != &inactive_dirty_list && - maxscan-- > 0) { + /* + * By not scanning all inactive dirty pages we'll write out + * really old dirty pages before evicting newer clean pages. + * This should cause some LRU behaviour if we have a large + * amount of inactive pages (due to eg. drop behind). + * + * It also makes us accumulate dirty pages until we have enough + * to be worth writing to disk without causing excessive disk + * seeks and eliminates the infinite penalty clean pages incurred + * vs. dirty pages. + */ + maxscan = nr_inactive_dirty_pages / 4; + if (launder_loop) + maxscan *= 2; + list_add_tail(marker_lru, &inactive_dirty_list); + for (;;) { + page_lru = marker_lru->prev; + if (page_lru == &inactive_dirty_list) + break; + if (--maxscan < 0) + break; + if (!free_shortage()) + break; + page = list_entry(page_lru, struct page, lru); + /* Move the bookmark backwards.. */ + list_del(marker_lru); + list_add_tail(marker_lru, page_lru); + + /* Don't waste CPU if chances are we cannot free anything. */ + if (launder_loop && maxlaunder < 0 && cannot_free_pages) + break; + + /* Skip other people's marker pages. */ + if (!page->zone) + continue; + /* Wrong page on list?! (list corruption, should not happen) */ if (!PageInactiveDirty(page)) { printk("VM: page_launder, wrong page on list.\n"); @@ -454,7 +492,6 @@ dirty_page_rescan: /* Page is or was in use? Move it to the active list. */ if (PageReferenced(page) || page->age > 0 || - page->zone->free_pages > page->zone->pages_high || (!page->buffers && page_count(page) > 1) || page_ramdisk(page)) { del_page_from_inactive_dirty_list(page); @@ -464,11 +501,9 @@ dirty_page_rescan: /* * The page is locked. IO in progress? - * Move it to the back of the list. + * Skip the page, we'll take a look when it unlocks. */ if (TryLockPage(page)) { - list_del(page_lru); - list_add(page_lru, &inactive_dirty_list); continue; } @@ -482,10 +517,8 @@ dirty_page_rescan: if (!writepage) goto page_active; - /* First time through? Move it to the back of the list */ + /* First time through? Skip the page. */ if (!launder_loop || !CAN_DO_IO) { - list_del(page_lru); - list_add(page_lru, &inactive_dirty_list); UnlockPage(page); continue; } @@ -498,6 +531,8 @@ dirty_page_rescan: writepage(page); page_cache_release(page); + maxlaunder--; + /* And re-start the thing.. */ spin_lock(&pagemap_lru_lock); continue; @@ -525,9 +560,9 @@ dirty_page_rescan: spin_unlock(&pagemap_lru_lock); /* Will we do (asynchronous) IO? */ - if (launder_loop && maxlaunder == 0 && sync) + if (launder_loop && maxlaunder-- == 0 && sync) wait = 2; /* Synchrounous IO */ - else if (launder_loop && maxlaunder-- > 0) + else if (launder_loop && maxlaunder > 0) wait = 1; /* Async IO */ else wait = 0; /* No IO */ @@ -544,7 +579,7 @@ dirty_page_rescan: /* The buffers were not freed. */ if (!clearedbuf) { - add_page_to_inactive_dirty_list(page); + add_page_to_inactive_dirty_list_marker(page); /* The page was only in the buffer cache. */ } else if (!page->mapping) { @@ -600,6 +635,8 @@ page_active: UnlockPage(page); } } + /* Remove our marker. */ + list_del(marker_lru); spin_unlock(&pagemap_lru_lock); /* @@ -615,16 +652,29 @@ page_active: */ if ((CAN_DO_IO || CAN_DO_BUFFERS) && !launder_loop && free_shortage()) { launder_loop = 1; - /* If we cleaned pages, never do synchronous IO. */ - if (cleaned_pages) + /* + * If we, or the previous process running page_launder(), + * managed to free any pages we never do synchronous IO. + */ + if (cleaned_pages || !cannot_free_pages) sync = 0; + /* Else, do synchronous IO (if we are allowed to). */ + else if (sync) + sync = 1; /* We only do a few "out of order" flushes. */ maxlaunder = MAX_LAUNDER; - /* Kflushd takes care of the rest. */ + /* Let bdflush take care of the rest. */ wakeup_bdflush(0); goto dirty_page_rescan; } + /* + * If we failed to free pages (because all pages are dirty) + * we remember this for the next time. This will prevent us + * from wasting too much CPU here. + */ + cannot_free_pages = !cleaned_pages; + /* Return the number of pages moved to the inactive_clean list. */ return cleaned_pages; } @@ -852,7 +902,7 @@ static int do_try_to_free_pages(unsigned int gfp_mask, int user) * list, so this is a relatively cheap operation. */ if (free_shortage()) { - ret += page_launder(gfp_mask, user); + ret += page_launder(gfp_mask, 1); shrink_dcache_memory(DEF_PRIORITY, gfp_mask); shrink_icache_memory(DEF_PRIORITY, gfp_mask); } diff --git a/net/khttpd/security.h b/net/khttpd/security.h index 346beacbb7a1..0876da27562c 100644 --- a/net/khttpd/security.h +++ b/net/khttpd/security.h @@ -9,4 +9,4 @@ struct DynamicString char value[32-sizeof(void*)]; /* fill 1 cache-line */ }; -#endif \ No newline at end of file +#endif -- 2.39.5