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á
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 <linux-mtd@lists.infradead.org> if
+ you want to help to make it more reliable.
Common Flash Interface (CFI) support
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
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
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
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
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
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
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.
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
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
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
- 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 <devfs_generate_path> terminates string for root entry
+ Thanks to Tim Jansen <tim@tjansen.de>
+
+- Exported <devfs_get_name> to modules
+
+- Make <devfs_mk_symlink> send events to devfsd
+
+- Cleaned up option processing in <devfs_setup>
+
+- Fixed bugs in handling symlinks: could leak or cause Oops
+
+- Cleaned up directory handling by separating fops
+ Thanks to Alexander Viro <viro@math.psu.edu>
+===============================================================================
+Changes for patch v178
+
+- Fixed handling of inverted options in <devfs_setup>
+===============================================================================
+Changes for patch v179
+
+- Adjusted <try_modload> to account for <devfs_generate_path> fix
+===============================================================================
+Changes for patch v180
+
+- Fixed !CONFIG_DEVFS_FS stub declaration of <devfs_get_info>
Linux Devfs (Device File System) FAQ
Richard Gooch
-3-JUL-2000
+26-APR-2001
-----------------------------------------------------------------------------
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.
Other Issues
Kernel Naming Scheme
Devfsd Naming Scheme
+Old Compatibility Names
SCSI Host Probing Issues
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
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
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.
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!
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
/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
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.
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
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.
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
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,
# file classes -- these are regular expressions
-<console>=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9]
-+<console>=tty[0-9][0-9]* [0-9][0-9]* :[0-9]\.[0-9] :[0-9]
++<console>=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9]
# device classes -- these are shell-style globs
<floppy>=/dev/fd[0-1]*
+If the patch does not apply, then change the line:
+
+<console>=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9]
+
+with:
+
+<console>=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
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.
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
+
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
+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
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=<device>" option to the kernel when booting. If you use
+LILO, then you must have this in lilo.conf:
append = "root=<device>"
root = <device>
-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 <device> 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".
-------- -------- -----------
/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
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:
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
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
Making things work
Alternatives to devfs
+What I don't like about devfs
+
+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.
+
-----------------------------------------------------------------------------
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.
+
+
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
----------------------------------
---------
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
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
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:
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
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 6
-EXTRAVERSION =-pre1
+EXTRAVERSION =-pre3
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
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)
#
extern int do_pipe(int *);
-extern asmlinkage int sys_swapon(const char *specialfile, int swap_flags);
extern asmlinkage unsigned long sys_brk(unsigned long);
/*
static inline void
sable_update_irq_hw(unsigned long bit, unsigned long mask)
{
- int port = 0x536;
+ int port = 0x537;
if (bit >= 16) {
port = 0x53d;
} else if (bit >= 8) {
port = 0x53a;
val1 = 0xE0 | (bit - 8);
- val2 = 0xE0 | 2;
+ val2 = 0xE0 | 3;
} else {
port = 0x536;
val1 = 0xE0 | (bit - 0);
#
# 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
#
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?
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
EXCEPTION(EX_INTERNAL | 0x116);
return;
#endif /* PARANOID */
+ break;
}
}
else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
/*
- * 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
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)
/*
- * 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 <linux/config.h>
#include <linux/module.h>
#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);
/*
- * 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.
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");
|| 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;
-/* $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)
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)
#include <linux/threads.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/string.h>
#include <asm/page.h>
#include <asm/oplib.h>
-/* $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)
#ifdef CONFIG_SUN_AUXIO
extern void auxio_probe(void);
#endif
-extern void rs_init(void);
static inline void *ebus_alloc(size_t size)
{
printk("]");
}
-extern void clock_probe(void);
-extern void power_init(void);
void __init ebus_init(void)
{
++num_ebus;
}
- rs_init();
#ifdef CONFIG_SUN_AUXIO
auxio_probe();
#endif
- clock_probe();
- power_init();
}
-/* $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)
}
}
+extern void rs_init(void);
+extern void clock_probe(void);
+extern void power_init(void);
+
void __init pcibios_init(void)
{
pci_controller_probe();
isa_init();
ebus_init();
+ rs_init();
+ clock_probe();
+ power_init();
}
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,
-/* $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)
}
/* 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;
(err / sizeof(pcp->prom_assignments[0]));
}
- fixup_obp_assignments(pcp);
+ fixup_obp_assignments(pdev, pcp);
pdev->sysdata = pcp;
}
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)
{
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);
+ }
}
}
}
-/* $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)
#include <asm/pbm.h>
#include <asm/iommu.h>
#include <asm/irq.h>
+#include <asm/smp.h>
#include "pci_impl.h"
#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
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)
/* 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;
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);
-/* $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)
{
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) {
-/* $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)
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
-/* $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)
#include <asm/fhc.h>
#include <asm/pbm.h>
#include <asm/ebus.h>
+#include <asm/isa.h>
#include <asm/starfire.h>
extern rwlock_t xtime_lock;
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) {
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) {
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");
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 {
case FORE200E_STATE_BLANK:
/* nothing to do for that state */
+ break;
}
}
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;
}
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;
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;
void *newval, size_t newlen, void **context)
{
unsigned char tmp_uuid[16], *uuid;
- int len;
+ unsigned int len;
if (!oldval || !oldlenp)
return 1;
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;
#ifdef __sparc__
#include <asm/ebus.h>
+#ifdef __sparc_v9__
+#include <asm/isa.h>
+#endif
static unsigned long rtc_port;
static int rtc_irq;
#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
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;
}
/*
- * $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.
- *
*
*/
/*
- * $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 <linux/config.h>
/*
- * $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 <linux/module.h>
/*
- * $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 <linux/config.h>
/*
- * $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 <linux/module.h>
/*
- * $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.
- *
*
*/
#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;
/*
- * $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__
/*
* $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 <linux/version.h>
/*
- * $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);
/*
- * $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__
/*
- * $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
/*
- * $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 <linux/module.h>
/*
- * $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 <linux/config.h>
-/* $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
#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;
#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
-/* $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
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]) {
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)
-/* $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
#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++)
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
+ return 0;
}
void
-/* $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
*
#include <linux/pci.h>
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
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:
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");
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;
-/* $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
*
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);
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);
-/* $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
*
*/
-const char *l1_revision = "$Revision: 2.41.6.2 $";
+const char *l1_revision = "$Revision: 2.41.6.3 $";
#define __NO_VERSION__
#include <linux/init.h>
#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)
-/* $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
#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, ...);
{
}
-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
-/* $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
#include "isdnl3.h"
#include <linux/config.h>
-const char *l3_revision = "$Revision: 2.17.6.2 $";
+const char *l3_revision = "$Revision: 2.17.6.3 $";
static struct Fsm l3fsm;
}
}
-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
# 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
# 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-----
-/* $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
#include <linux/init.h>
#include <linux/random.h>
-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
#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
-/* $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
#include "hysdn_defs.h"
#include <linux/kernelcapi.h>
-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");
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");
-/* $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.
*
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 */
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 */
-/* $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
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)) {
hysdn_log_open(struct inode *ino, struct file *filep)
{
hysdn_card *card;
- struct procdata *pd;
+ struct procdata *pd = NULL;
ulong flags;
lock_kernel();
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 */
-/* $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).
*
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];
}
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);
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
# 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
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
--- /dev/null
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer (drivers/sound/aci.c)
+ * Look there for further info...
+ */
+
+/* Revision history:
+ *
+ * 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * 2000-09-05 Robert Siemer <Robert.Siemer@gmx.de>
+ * removed unfinished volume control (maybe adding it later again)
+ * use OSS-mixer; added stereo control
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I cant get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions... Robert
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev.h>
+#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 {
+ unsigned long freq;
+ int muted;
+ int stereo;
+};
+
+
+static int pcm20_mute(struct pcm20_device *dev, unsigned char mute)
+{
+ dev->muted = 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(ACI_SET_TUNERMONO, !stereo);
+}
+
+static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq)
+{
+ unsigned char freql;
+ unsigned char freqh;
+
+ dev->freq=freq;
+
+ freq /= 160;
+ if (!(aci_version==0x07 || aci_version>=0xb0))
+ freq /= 10; /* I don't know exactly which version
+ * needs this hack */
+ freql = freq & 0xff;
+ freqh = freq >> 8;
+
+ aci_rds_cmd(RDS_RESET, 0, 0);
+ pcm20_stereo(dev, 1);
+
+ return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh);
+}
+
+static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal)
+{
+ /* okay, check for signal, stereo and rds here... */
+ int i;
+ unsigned char buf;
+
+ if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0)
+ return i;
+#if DEBUG
+ printk("check_sig: 0x%x\n", i);
+#endif
+ if (i & 0x80) {
+ /* no signal from tuner */
+ *flags=0;
+ *signal=0;
+ return 0;
+ } else
+ *signal=0xffff;
+
+ if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0)
+ return i;
+ if (i & 0x40) {
+ *flags=0;
+ } else {
+ /* stereo */
+ *flags=VIDEO_TUNER_STEREO_ON;
+ /* I cant see stereo, when forced to mono */
+ dev->stereo=1;
+ }
+
+ if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0)
+ return i;
+ if (buf & 1)
+ /* RDS available */
+ *flags|=VIDEO_TUNER_RDS_ON;
+ else
+ return 0;
+
+ if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0)
+ return i;
+#if DEBUG
+ printk("rds-signal: %d\n", buf);
+#endif
+ if (buf > 15) {
+ printk("miropcm20-radio: RX strengths unexpected high...\n");
+ buf=15;
+ }
+ /* refine signal */
+ if ((*signal=SCALE(15, 0xffff, buf))==0)
+ *signal = 1;
+
+ return 0;
+}
+
+static int pcm20_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct pcm20_device *pcm20=dev->priv;
+ int i;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability v;
+ v.type=VID_TYPE_TUNER;
+ strcpy(v.name, "Miro PCM20");
+ v.channels=1;
+ v.audios=1;
+ /* No we don't do pictures */
+ v.maxwidth=0;
+ v.maxheight=0;
+ v.minwidth=0;
+ v.minheight=0;
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg,sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v.rangelow=87*16000;
+ v.rangehigh=108*16000;
+ pcm20_getflags(pcm20, &v.flags, &v.signal);
+ v.flags|=VIDEO_TUNER_LOW;
+ v.mode=VIDEO_MODE_AUTO;
+ strcpy(v.name, "FM");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ if(copy_to_user(arg, &pcm20->freq, sizeof(pcm20->freq)))
+ return -EFAULT;
+ return 0;
+ case VIDIOCSFREQ:
+ if(copy_from_user(&pcm20->freq, arg, sizeof(pcm20->freq)))
+ return -EFAULT;
+ i=pcm20_setfreq(pcm20, pcm20->freq);
+#if DEBUG
+ printk("First view (setfreq): 0x%x\n", i);
+#endif
+ return i;
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio v;
+ memset(&v,0, sizeof(v));
+ v.flags=VIDEO_AUDIO_MUTABLE;
+ if (pcm20->muted)
+ v.flags|=VIDEO_AUDIO_MUTE;
+ v.mode=VIDEO_SOUND_STEREO;
+ if (pcm20->stereo)
+ v.mode|=VIDEO_SOUND_MONO;
+ /* v.step=2048; */
+ strcpy(v.name, "Radio");
+ if(copy_to_user(arg,&v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.audio)
+ return -EINVAL;
+
+ pcm20_mute(pcm20, !!(v.flags&VIDEO_AUDIO_MUTE));
+ if(v.flags&VIDEO_SOUND_MONO)
+ pcm20_stereo(pcm20, 0);
+ if(v.flags&VIDEO_SOUND_STEREO)
+ pcm20_stereo(pcm20, 1);
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+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 = {
+ freq: 87*16000,
+ muted: 1,
+ stereo: 0
+};
+
+static struct video_device pcm20_radio = {
+ owner: THIS_MODULE,
+ name: "Miro PCM 20 radio",
+ type: VID_TYPE_TUNER,
+ hardware: VID_HARDWARE_RTRACK,
+ open: pcm20_open,
+ close: pcm20_close,
+ ioctl: pcm20_ioctl,
+ priv: &pcm20_unit
+};
+
+static int __init pcm20_init(void)
+{
+ if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ goto video_register_device;
+
+ 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");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+
+EXPORT_NO_SYMBOLS;
+
+static void __exit pcm20_cleanup(void)
+{
+ unload_aci_rds();
+ video_unregister_device(&pcm20_radio);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
--- /dev/null
+/*
+ * Many thanks to Fred Seidel <seidel@metabox.de>, the
+ * designer of the RDS decoder hardware. With his help
+ * I was able to code this driver.
+ * Thanks also to Norberto Pellicci, Dominic Mounteney
+ * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
+ * for good hints on finding Fred. It was somewhat hard
+ * to locate him here in Germany... [:
+ *
+ * Revision history:
+ *
+ * 2000-08-09 Robert Siemer <Robert.Siemer@gmx.de>
+ * RDS support for MiroSound PCM20 radio
+ */
+
+#define _NO_VERSION_
+
+/* #include <linux/kernel.h> */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include "../../sound/aci.h"
+#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_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
+
+
+#if DEBUG
+static void print_matrix(char array[], unsigned int length)
+{
+ int i, j;
+
+ for (i=0; i<length; i++) {
+ printk(KERN_DEBUG "aci-rds: ");
+ for (j=7; j>=0; j--) {
+ printk("%d", (array[i] >> j) & 0x1);
+ }
+ if (i%8 == 0)
+ printk(" byte-border\n");
+ else
+ printk("\n");
+ }
+}
+#endif /* DEBUG */
+
+static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
+{
+ int i;
+
+ if (size != 8)
+ return -1;
+ for (i = 7; i >= 0; i--)
+ sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
+ sendbuffer[0] |= RDS_CLOCKMASK;
+
+ return 0;
+}
+
+static int rds_waitread(void)
+{
+ unsigned char byte;
+ int i=2000;
+
+ do {
+ byte=inb(RDS_REGISTER);
+ i--;
+ }
+ while ((byte & RDS_BUSYMASK) && i);
+
+ if (i) {
+ #if DEBUG
+ printk(KERN_DEBUG "rds_waitread()");
+ print_matrix(&byte, 1);
+ #endif
+ return (byte);
+ } else {
+ printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
+ return -1;
+ }
+}
+
+/* dont use any ..._nowait() function if you are not sure what you do... */
+
+static inline void rds_rawwrite_nowait(unsigned char byte)
+{
+ #if DEBUG
+ printk(KERN_DEBUG "rds_rawwrite()");
+ print_matrix(&byte, 1);
+ #endif
+ outb(byte, RDS_REGISTER);
+}
+
+static int rds_rawwrite(unsigned char byte)
+{
+ if (rds_waitread() >= 0) {
+ rds_rawwrite_nowait(byte);
+ return 0;
+ } else
+ return -1;
+}
+
+static int rds_write(unsigned char cmd)
+{
+ unsigned char sendbuffer[8];
+ int i;
+
+ if (byte2trans(cmd, sendbuffer, 8) != 0){
+ return -1;
+ } else {
+ for (i=0; i<8; i++) {
+ rds_rawwrite(sendbuffer[i]);
+ }
+ }
+ return 0;
+}
+
+static int rds_readcycle_nowait(void)
+{
+ rds_rawwrite_nowait(0);
+ return rds_waitread();
+}
+
+static int rds_readcycle(void)
+{
+ if (rds_rawwrite(0) < 0)
+ return -1;
+ return rds_waitread();
+}
+
+static int rds_read(unsigned char databuffer[], int datasize)
+{
+ #define READSIZE (8*datasize)
+
+ int i,j;
+
+ if (datasize < 1) /* nothing to read */
+ return 0;
+
+ /* to be able to use rds_readcycle_nowait()
+ I have to waitread() here */
+ if (rds_waitread() < 0)
+ return -1;
+
+ memset(databuffer, 0, datasize);
+
+ for (i=0; i< READSIZE; i++)
+ if((j=rds_readcycle_nowait()) < 0) {
+ return -1;
+ } else {
+ databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
+ }
+
+ return 0;
+}
+
+static int rds_ack(void)
+{
+ int i=rds_readcycle();
+
+ if (i < 0)
+ return -1;
+ if (i & RDS_DATAMASK) {
+ return 0; /* ACK */
+ } else {
+ printk(KERN_DEBUG "aci-rds: NACK\n");
+ return 1; /* NACK */
+ }
+}
+
+int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
+{
+ int ret;
+
+ if (down_interruptible(&aci_rds_sem))
+ return -EINTR;
+
+ rds_write(cmd);
+
+ /* RDS_RESET doesn't need further processing */
+ if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
+ ret = -1;
+ else
+ ret = 0;
+
+ up(&aci_rds_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL(aci_rds_cmd);
+
+int __init attach_aci_rds(void)
+{
+ init_MUTEX(&aci_rds_sem);
+ return 0;
+}
+
+void __exit unload_aci_rds(void)
+{
+}
--- /dev/null
+#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_ */
--- /dev/null
+/* MiroSOUND PCM20 radio rds interface driver
+ * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
+ * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information.
+ */
+
+/* Revision history:
+ *
+ * 2001-04-18 Robert Siemer <Robert.Siemer@gmx.de>
+ * separate file for user interface driver
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/devfs_fs_kernel.h>
+#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);
+++ /dev/null
-/* Miro PCM20 radio driver for Linux radio support
- * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
- * Thanks to Norberto Pellici for the ACI device interface specification
- * The API part is based on the radiotrack driver by M. Kirkwood
- * This driver relies on the aci mixer (drivers/sound/aci.c)
- * Look there for further info...
- */
-
-/* Revision history:
- *
- * 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
- * 2000-09-05 Robert Siemer <Robert.Siemer@gmx.de>
- * removed unfinished volume control (maybe adding it later again)
- * use OSS-mixer; added stereo control
- */
-
-/* What ever you think about the ACI, version 0x07 is not very well!
- * I cant get frequency, 'tuner status', 'tuner flags' or mute/mono
- * conditions... Robert
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/videodev.h>
-#include <linux/devfs_fs_kernel.h>
-#include <asm/uaccess.h>
-
-char * aci_radio_name;
-
-#include "../../sound/aci.h"
-
-static int users = 0;
-static int radio_nr = -1;
-MODULE_PARM(radio_nr, "i");
-
-struct pcm20_device
-{
- unsigned long freq;
- int muted;
- int stereo;
-};
-
-
-static int pcm20_mute(struct pcm20_device *dev, unsigned char mute)
-{
- dev->muted = mute;
- return aci_write_cmd(0xa3, mute);
-}
-
-static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo)
-{
- dev->stereo = stereo;
- return aci_write_cmd(0xa4, !stereo);
-}
-
-static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq)
-{
- unsigned char freql;
- unsigned char freqh;
-
- dev->freq=freq;
-
- freq /= 160;
- if (!(aci_version==0x07 || aci_version>=0xb0))
- freq /= 10; /* I don't know exactly which version
- * needs this hack */
- freql = freq & 0xff;
- freqh = freq >> 8;
-
- aci_rds_cmd(RDS_RESET, 0, 0);
- pcm20_stereo(dev, 1);
-
- return aci_rw_cmd(0xa7, freql, freqh); /* Tune to frequency */
-}
-
-static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal)
-{
- /* okay, check for signal, stereo and rds here... */
- int i;
- unsigned char buf;
-
- if ((i=aci_rw_cmd(0xa9, -1, -1))<0)
- return i;
-#if DEBUG
- printk("check_sig: 0x%x\n", i);
-#endif
- if (i & 0x80) {
- /* no signal from tuner */
- *flags=0;
- *signal=0;
- return 0;
- } else
- *signal=0xffff;
-
- if ((i=aci_rw_cmd(0xa8, -1, -1))<0)
- return i;
- if (i & 0x40) {
- *flags=0;
- } else {
- /* stereo */
- *flags=VIDEO_TUNER_STEREO_ON;
- /* I cant see stereo, when forced to mono */
- dev->stereo=1;
- }
-
- if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0)
- return i;
- if (buf & 1)
- /* RDS available */
- *flags|=VIDEO_TUNER_RDS_ON;
- else
- return 0;
-
- if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0)
- return i;
-#if DEBUG
- printk("rds-signal: %d\n", buf);
-#endif
- if (buf > 15) {
- printk("rds-miropcm20: RX strengths unexpected high...\n");
- buf=15;
- }
- /* refine signal */
- if ((*signal=SCALE(15, 0xffff, buf))==0)
- *signal = 1;
-
- return 0;
-}
-
-static int pcm20_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
- struct pcm20_device *pcm20=dev->priv;
- int i;
-
- switch(cmd)
- {
- case VIDIOCGCAP:
- {
- struct video_capability v;
- v.type=VID_TYPE_TUNER;
- strcpy(v.name, "Miro PCM20");
- v.channels=1;
- v.audios=1;
- /* No we don't do pictures */
- v.maxwidth=0;
- v.maxheight=0;
- v.minwidth=0;
- v.minheight=0;
- if(copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCGTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg,sizeof(v))!=0)
- return -EFAULT;
- if(v.tuner) /* Only 1 tuner */
- return -EINVAL;
- v.rangelow=87*16000;
- v.rangehigh=108*16000;
- pcm20_getflags(pcm20, &v.flags, &v.signal);
- v.flags|=VIDEO_TUNER_LOW;
- v.mode=VIDEO_MODE_AUTO;
- strcpy(v.name, "FM");
- if(copy_to_user(arg,&v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.tuner!=0)
- return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
- return 0;
- }
- case VIDIOCGFREQ:
- if(copy_to_user(arg, &pcm20->freq, sizeof(pcm20->freq)))
- return -EFAULT;
- return 0;
- case VIDIOCSFREQ:
- if(copy_from_user(&pcm20->freq, arg, sizeof(pcm20->freq)))
- return -EFAULT;
- i=pcm20_setfreq(pcm20, pcm20->freq);
-#if DEBUG
- printk("First view (setfreq): 0x%x\n", i);
-#endif
- return i;
- case VIDIOCGAUDIO:
- {
- struct video_audio v;
- memset(&v,0, sizeof(v));
- v.flags=VIDEO_AUDIO_MUTABLE;
- if (pcm20->muted)
- v.flags|=VIDEO_AUDIO_MUTE;
- v.mode=VIDEO_SOUND_STEREO;
- if (pcm20->stereo)
- v.mode|=VIDEO_SOUND_MONO;
- /* v.step=2048; */
- strcpy(v.name, "Radio");
- if(copy_to_user(arg,&v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.audio)
- return -EINVAL;
-
- pcm20_mute(pcm20, !!(v.flags&VIDEO_AUDIO_MUTE));
- if(v.flags&VIDEO_SOUND_MONO)
- pcm20_stereo(pcm20, 0);
- if(v.flags&VIDEO_SOUND_STEREO)
- pcm20_stereo(pcm20, 1);
-
- return 0;
- }
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-static int pcm20_open(struct video_device *dev, int flags)
-{
- if(users)
- return -EBUSY;
- users++;
- return 0;
-}
-
-static void pcm20_close(struct video_device *dev)
-{
- users--;
-}
-
-static struct pcm20_device pcm20_unit=
-{
- freq: 87*16000,
- muted: 1,
- stereo: 0
-};
-
-static struct video_device pcm20_radio=
-{
- owner: THIS_MODULE,
- name: "Miro PCM 20 radio",
- type: VID_TYPE_TUNER,
- hardware: VID_HARDWARE_RTRACK,
- open: pcm20_open,
- close: pcm20_close,
- ioctl: pcm20_ioctl,
- priv: &pcm20_unit
-};
-
-static int __init pcm20_init(void)
-{
- if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1)
- return -EINVAL;
-
- 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
- printk(KERN_INFO "Miro PCM20 radio card driver.\n");
-
- return 0;
-}
-
-MODULE_AUTHOR("Ruurd Reitsma");
-MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
-
-EXPORT_NO_SYMBOLS;
-
-static void __exit pcm20_cleanup(void)
-{
- unload_aci_rds();
- video_unregister_device(&pcm20_radio);
-}
-
-module_init(pcm20_init);
-module_exit(pcm20_cleanup);
+++ /dev/null
-/*
- * Many thanks to Fred Seidel <seidel@metabox.de>, the
- * designer of the RDS decoder hardware. With his help
- * I was able to code this driver.
- * Thanks also to Norberto Pellicci, Dominic Mounteney
- * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
- * for good hints on finding Fred. It was somewhat hard
- * to locate him here in Germany... [:
- *
- * Revision history:
- *
- * 2000-08-09 Robert Siemer <Robert.Siemer@gmx.de>
- * RDS support for MiroSound PCM20 radio
- */
-
-#define _NO_VERSION_
-
-/* #include <linux/kernel.h> */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <asm/semaphore.h>
-#include <asm/io.h>
-#include "../../sound/aci.h"
-
-#define WATCHMASK 0352 /* 11101010 */
-
-#define DEBUG 0
-
-static struct semaphore aci_rds_sem;
-
-
-#define RDS_BUSYMASK 0x10 /* Bit 4 */
-#define RDS_CLOCKMASK 0x08 /* Bit 3 */
-#define RDS_DATAMASK 0x04 /* Bit 2 */
-
-
-static void print_matrix(char array[], unsigned int length)
-{
- int i, j;
-
- for (i=0; i<length; i++) {
- printk("aci-rds: ");
- for (j=7; j>=0; j--) {
- printk("%d", (array[i] >> j) & 0x1);
- }
- if (i%8 == 0)
- printk(" byte-border\n");
- else
- printk("\n");
- }
-}
-
-static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
-{
- int i;
-
- if (size != 8)
- return -1;
- for (i = 7; i >= 0; i--)
- sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
- sendbuffer[0] |= RDS_CLOCKMASK;
-
- 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;
- int i=2000;
-
- 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()");
- print_matrix(&byte, 1);
-#endif
- return (byte);
- } else {
- printk("aci-rds: rds_waitread() timeout...\n");
- return -1;
- }
-}
-
-/* dont use any ..._nowait() function if you are not sure what you do... */
-
-static inline void rds_rawwrite_nowait(unsigned char byte)
-{
-#if DEBUG
- printk("rds_rawwrite()");
- print_matrix(&byte, 1);
-#endif
- outb(byte, RDS_REGISTER);
-}
-
-static int rds_rawwrite(unsigned char byte)
-{
- if (rds_waitread() >= 0) {
- rds_rawwrite_nowait(byte);
- return 0;
- } else
- return -1;
-}
-
-static int rds_write(unsigned char cmd)
-{
- unsigned char sendbuffer[8];
- int i;
-
- if (byte2trans(cmd, sendbuffer, 8) != 0){
- return -1;
- } else {
- for (i=0; i<8; i++) {
- rds_rawwrite(sendbuffer[i]);
- }
- }
- return 0;
-}
-
-static int rds_readcycle_nowait(void)
-{
- rds_rawwrite_nowait(0);
- return rds_waitread();
-}
-
-static int rds_readcycle(void)
-{
- if (rds_rawwrite(0) < 0)
- return -1;
- return rds_waitread();
-}
-
-static int rds_read(unsigned char databuffer[], int datasize)
-{
-
-#define READSIZE (8*datasize)
-
- int i,j;
- unsigned char* readbuffer;
-
- if (!datasize) /* nothing to read */
- return 0;
-
- /* to be able to use rds_readcycle_nowait()
- I have to readwait() 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;
- }
- }
-
- 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);
- return 0;
-}
-
-static int rds_ack(void)
-{
- int i=rds_readcycle();
-
- if (i < 0)
- return -1;
- if (i & RDS_DATAMASK) {
- return 0; /* ACK */
- } else {
- return 1; /* NACK */
- }
-}
-
-int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
-{
- int ret;
-
- if (down_interruptible(&aci_rds_sem))
- return -EINTR;
-
- if (rds_write(cmd))
- ret = -2;
-
- /* RDS_RESET doesn't need further processing */
- if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
- ret = -1;
- else
- ret = 0;
-
- up(&aci_rds_sem);
-
- return ret;
-}
-
-int __init attach_aci_rds(void)
-{
- init_MUTEX(&aci_rds_sem);
- return 0;
-}
-
-void __exit unload_aci_rds(void)
-{
-}
#endif
default:
/* nothing */
+ break;
}
return 0;
-# $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)'
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
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
# 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)
+
--- /dev/null
+/*
+ * 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 <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#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);
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-
-#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 <kaos@ocs.com.au> 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; i<sizeof(*extp); i++) {
- ((unsigned char *)extp)[i] =
- map->read8(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<<i))
- printk(" - Unknown Bit %X: supported\n", i);
- }
-
- printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport);
- printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported");
- for (i=1; i<8; i++) {
- if (extp->SuspendCmdSupport & (1<<i))
- printk(" - Unknown Bit %X: supported\n", i);
- }
-
- printk(" Block Status Register Mask: %4.4X\n", extp->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<<i))
- printk(" - Unknown Bit %X Active: yes\n",i);
- }
-
- printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
- extp->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<<cfi->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 && i<cfi->numchips; 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 && i<cfi->numchips; 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; i<cfi->numchips; 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);
+++ /dev/null
-/*
- * Common Flash Interface support:
- * AMD & Fujitsu Extended Vendor Command Set (ID 0x0002)
- *
- * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
- *
- * This code is GPL
- *
- * $Id: cfi_cmdset_0002.c,v 1.1 2000/07/11 12:32:09 dwmw2 Exp $
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-
-#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<<cfi->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 && i<cfi->numchips; 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 && i<cfi->numchips; 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; i<cfi->numchips; 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);
+++ /dev/null
-/*
- 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 <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-
-#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-
-
-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 <kaos@ocs.com.au> 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; i<sizeof(struct cfi_ident); i++) {
- ((unsigned char *)&cfi.cfiq)[i] = map->read8(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; i<cfi.cfiq.NumEraseRegions; i++) {
- __u16 EraseRegionInfoNum = (map->read8(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<<cfi.chipshift); base < map->size; base += (1<<cfi.chipshift))
- cfi_probe_new_chip(map, base, &chip[0], &cfi);
-
- retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
-
- if (!retcfi)
- 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);
- }
- 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<<cfip->WordWriteTimeoutTyp);
- printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
-
- if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
- printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
- printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
- }
- else
- printk("Full buffer write not supported\n");
-
- printk("Typical block erase timeout: %d µs\n", 1<<cfip->BlockEraseTimeoutTyp);
- printk("Maximum block erase timeout: %d µs\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp));
- if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) {
- printk("Typical chip erase timeout: %d µs\n", 1<<cfip->ChipEraseTimeoutTyp);
- printk("Maximum chip erase timeout: %d µs\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->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);
--- /dev/null
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * MTD map driver for AMD compatible flash chips (non-CFI)
+ *
+ * Author: Jonas Holmberg <jonas.holmberg@axis.com>
+ *
+ * $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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/flashchip.h>
+
+/* 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);
--- /dev/null
+/*
+ * 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 <nico@cam.org>
+ * - 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/compatmac.h>
+
+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 <kaos@ocs.com.au> 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; i<sizeof(*extp); i++) {
+ ((unsigned char *)extp)[i] =
+ cfi_read_query(map, (base+((adr+i)*cfi->interleave*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<<i))
+ printk(" - Unknown Bit %X: supported\n", i);
+ }
+
+ printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport);
+ printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported");
+ for (i=1; i<8; i++) {
+ if (extp->SuspendCmdSupport & (1<<i))
+ printk(" - Unknown Bit %X: supported\n", i);
+ }
+
+ printk(" Block Status Register Mask: %4.4X\n", extp->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<<i))
+ printk(" - Unknown Bit %X Active: yes\n",i);
+ }
+
+ printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
+ extp->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<<cfi->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; i<cfi->cfiq->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; j<cfi->numchips; 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; i<mtd->numeraseregions;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<<cfi->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<CFIDEV_INTERLEAVE; i++) {
+ chipstatus |= status >> (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 (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 >> 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 && i<cfi->numchips; 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 && i<cfi->numchips; 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; i<cfi->numchips; 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);
--- /dev/null
+/*
+ * Common Flash Interface support:
+ * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002)
+ *
+ * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
+ *
+ * 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+
+#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; i<cfi->cfiq->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<<cfi->cfiq->WordWriteTimeoutTyp;
+ cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
+ cfi->chips[i].erase_time = 1<<cfi->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<<cfi->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; i<cfi->cfiq->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; j<cfi->numchips; 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; i<mtd->numeraseregions;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<<cfi->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 (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 >> 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 && i<cfi->numchips; 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 && i<cfi->numchips; 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; i<cfi->numchips; 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);
+
--- /dev/null
+/* $Id: cfi_jedec.c,v 1.5 2001/06/02 14:52:23 dwmw2 Exp $ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+
+/* 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; i<num_erase_regions; i++){
+ p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
+ }
+ return 0; /* ok */
+}
+
--- /dev/null
+/*
+ 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+
+/* #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; i<cfi->numchips; 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; i<cfi.cfiq->NumEraseRegions; 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<<cfi.chipshift); base < map->size; base += (1<<cfi.chipshift))
+ cfi_probe_chip_1(map, base, &chip[0], &cfi);
+
+ retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
+
+ if (!retcfi) {
+ printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->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<<cfip->WordWriteTimeoutTyp);
+ printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
+
+ if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
+ printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
+ printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
+ }
+ else
+ printk("Full buffer write not supported\n");
+
+ printk("Typical block erase timeout: %d µs\n", 1<<cfip->BlockEraseTimeoutTyp);
+ printk("Maximum block erase timeout: %d µs\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp));
+ if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) {
+ printk("Typical chip erase timeout: %d µs\n", 1<<cfip->ChipEraseTimeoutTyp);
+ printk("Maximum chip erase timeout: %d µs\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->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);
--- /dev/null
+/*
+ * $Id: chipreg.c,v 1.8 2001/06/09 19:58:19 dwmw2 Exp $
+ *
+ * Registration for chip drivers
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/map.h>
+
+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);
--- /dev/null
+
+/* JEDEC Flash Interface.
+ * This is an older type of interface for self programming flash. It is
+ * commonly use in older AMD chips and is obsolete compared with CFI.
+ * It is called JEDEC because the JEDEC association distributes the ID codes
+ * for the chips.
+ *
+ * See the AMD flash databook for information on how to operate the interface.
+ *
+ * This code does not support anything wider than 8 bit flash chips, I am
+ * not going to guess how to send commands to them, plus I expect they will
+ * all speak CFI..
+ *
+ * $Id: jedec.c,v 1.8 2001/06/09 23:56:57 dwmw2 Exp $
+ */
+
+#include <linux/mtd/jedec.h>
+
+struct mtd_info *jedec_probe(struct map_info *);
+int jedec_probe8(struct map_info *map,unsigned long base,
+ struct jedec_private *priv);
+int jedec_probe16(struct map_info *map,unsigned long base,
+ struct jedec_private *priv);
+int jedec_probe32(struct map_info *map,unsigned long base,
+ struct jedec_private *priv);
+static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
+ unsigned long len);
+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);
+
+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[] =
+ {{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) {};
+static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+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 mtd_info *jedec_probe(struct map_info *map)
+{
+ struct mtd_info *MTD = &__MTD;
+ unsigned long Base;
+ unsigned long SectorSize;
+ unsigned count;
+ unsigned I,Uniq;
+ char Part[200];
+ memset(&priv,0,sizeof(priv));
+
+ my_bank_size = map->size;
+
+ 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 += my_bank_size)
+ {
+ // Perhaps zero could designate all tests?
+ if (map->buswidth == 0)
+ map->buswidth = 1;
+
+ 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->buswidth == 4)
+ jedec_probe32(map,Base,&priv);
+ }
+
+ // Get the biggest sector size
+ 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;
+ }
+
+ // Quickly ensure that the other sector sizes are factors of the largest
+ for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ {
+ if ((SectorSize/priv.chips[I].sectorsize)*priv.chips[I].sectorsize != SectorSize)
+ {
+ printk("mtd: Failed. Device has incompatible mixed sector sizes\n");
+ return 0;
+ }
+ }
+
+ /* Generate a part name that includes the number of different chips and
+ other configuration information */
+ count = 1;
+ strncpy(Part,map->name,sizeof(Part)-10);
+ Part[sizeof(Part)-11] = 0;
+ strcat(Part," ");
+ Uniq = 0;
+ for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ {
+ const struct JEDECTable *JEDEC;
+
+ if (priv.chips[I+1].jedec == priv.chips[I].jedec)
+ {
+ count++;
+ continue;
+ }
+
+ // Locate the chip in the jedec table
+ JEDEC = jedec_idtoinf(priv.chips[I].jedec >> 8,priv.chips[I].jedec);
+ if (JEDEC == 0)
+ {
+ printk("mtd: Internal Error, JEDEC not set\n");
+ return 0;
+ }
+
+ if (Uniq != 0)
+ strcat(Part,",");
+ Uniq++;
+
+ if (count != 1)
+ sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name);
+ else
+ sprintf(Part+strlen(Part),"%s",JEDEC->name);
+ if (strlen(Part) > sizeof(Part)*2/3)
+ break;
+ count = 1;
+ }
+
+ /* Determine if the chips are organized in a linear fashion, or if there
+ 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 < my_bank_size)
+ my_bank_size = priv.size;
+ priv.is_banked = 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");
+
+ // printk("Part: '%s'\n",Part);
+
+ memset(MTD,0,sizeof(*MTD));
+ // 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->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)
+ MTD->read = jedec_read_banked;
+ else
+ MTD->read = jedec_read;
+ MTD->write = flash_write;
+ MTD->sync = jedec_sync;
+ MTD->priv = map;
+ map->fldrv_priv = &priv;
+ map->fldrv = &jedec_chipdrv;
+ MOD_INC_USE_COUNT;
+ return MTD;
+}
+
+/* Helper for the JEDEC function, JEDEC numbers all have odd parity */
+static int checkparity(u_char C)
+{
+ u_char parity = 0;
+ while (C != 0)
+ {
+ parity ^= C & 1;
+ C >>= 1;
+ }
+
+ return parity == 1;
+}
+
+
+/* Take an array of JEDEC numbers that represent interleved flash chips
+ and process them. Check to make sure they are good JEDEC numbers, look
+ them up and then add them to the chip list */
+int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count,
+ unsigned long base,struct jedec_private *priv)
+{
+ unsigned I,J;
+ unsigned long Size;
+ unsigned long SectorSize;
+ const struct JEDECTable *JEDEC;
+
+ // Test #2 JEDEC numbers exhibit odd parity
+ for (I = 0; I != Count; I++)
+ {
+ if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0)
+ return 0;
+ }
+
+ // Finally, just make sure all the chip sizes are the same
+ JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
+
+ if (JEDEC == 0)
+ {
+ printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
+ return 0;
+ }
+
+ Size = JEDEC->size;
+ SectorSize = JEDEC->sectorsize;
+ for (I = 0; I != Count; I++)
+ {
+ JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
+ if (JEDEC == 0)
+ {
+ printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
+ return 0;
+ }
+
+ if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize)
+ {
+ printk("mtd: Failed. Interleved flash does not have matching characteristics\n");
+ return 0;
+ }
+ }
+
+ // Load the Chips
+ for (I = 0; I != MAX_JEDEC_CHIPS; I++)
+ {
+ if (priv->chips[I].jedec == 0)
+ break;
+ }
+
+ if (I + Count > MAX_JEDEC_CHIPS)
+ {
+ printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n");
+ return 0;
+ }
+
+ // Add them to the table
+ for (J = 0; J != Count; J++)
+ {
+ unsigned long Bank;
+
+ JEDEC = jedec_idtoinf(Mfg[J],Id[J]);
+ priv->chips[I].jedec = (Mfg[J] << 8) | Id[J];
+ priv->chips[I].size = JEDEC->size;
+ priv->chips[I].sectorsize = JEDEC->sectorsize;
+ priv->chips[I].base = base + J;
+ priv->chips[I].datashift = J*8;
+ priv->chips[I].capabilities = JEDEC->capabilities;
+ priv->chips[I].offset = priv->size + J;
+
+ // log2 n :|
+ priv->chips[I].addrshift = 0;
+ for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++);
+
+ // Determine how filled this bank is.
+ 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/my_bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank;
+ I++;
+ }
+
+ priv->size += priv->chips[I-1].size*Count;
+
+ return priv->chips[I-1].size;
+}
+
+/* Lookup the chip information from the JEDEC ID table. */
+const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id)
+{
+ __u16 Id = (mfr << 8) | id;
+ unsigned long I = 0;
+ for (I = 0; JEDEC_table[I].jedec != 0; I++)
+ if (JEDEC_table[I].jedec == Id)
+ return JEDEC_table + I;
+ return 0;
+}
+
+// Look for flash using an 8 bit bus interface
+int jedec_probe8(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+{
+ #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)
+int jedec_probe16(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+{
+ return 0;
+}
+
+// Look for flash using a 32 bit bus interface (ie 4 8-bit chips)
+int jedec_probe32(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+{
+ #define flread(x) map->read32(map,base+((x)<<2))
+ #define flwrite(v,x) map->write32(map,v,base+((x)<<2))
+
+ const unsigned long AutoSel1 = 0xAAAAAAAA;
+ const unsigned long AutoSel2 = 0x55555555;
+ const unsigned long AutoSel3 = 0x90909090;
+ const unsigned long Reset = 0xF0F0F0F0;
+ __u32 OldVal;
+ __u8 Mfg[4];
+ __u8 Id[4];
+ 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);
+
+ // Test #1, JEDEC numbers are readable from 0x??00/0x??01
+ if (flread(0) != flread(0x100) ||
+ flread(1) != flread(0x101))
+ {
+ flwrite(Reset,0x555);
+ return 0;
+ }
+
+ // Split up the JEDEC numbers
+ OldVal = flread(0);
+ for (I = 0; I != 4; I++)
+ Mfg[I] = (OldVal >> (I*8));
+ OldVal = flread(1);
+ for (I = 0; I != 4; I++)
+ Id[I] = (OldVal >> (I*8));
+
+ Size = handle_jedecs(map,Mfg,Id,4,base,priv);
+ if (Size == 0)
+ {
+ flwrite(Reset,0x555);
+ return 0;
+ }
+
+ /* Check if there is address wrap around within a single bank, if this
+ returns JEDEC numbers then we assume that it is wrap around. Notice
+ we call this routine with the JEDEC return still enabled, if two or
+ more flashes have a truncated address space the probe test will still
+ work */
+ if (base + Size+0x555 < map->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))
+ {
+ jedec_probe32(map,base+Size,priv);
+ }
+ }
+
+ // Reset.
+ flwrite(0xF0F0F0F0,0x555);
+
+ return 1;
+
+ #undef flread
+ #undef flwrite
+}
+
+/* Linear read. */
+static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+ map->copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+}
+
+/* Banked read. Take special care to jump past the holes in the bank
+ mapping. This version assumes symetry in the holes.. */
+static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct map_info *map = (struct map_info *)mtd->priv;
+ struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
+
+ *retlen = 0;
+ while (len > 0)
+ {
+ // Determine what bank and offset into that bank the first byte is
+ unsigned long bank = from & (~(priv->bank_fill[0]-1));
+ unsigned long offset = from & (priv->bank_fill[0]-1);
+ unsigned long get = len;
+ if (priv->bank_fill[0] - offset < len)
+ get = priv->bank_fill[0] - offset;
+
+ bank /= priv->bank_fill[0];
+ map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
+
+ len -= get;
+ *retlen += get;
+ from += get;
+ }
+ return 0;
+}
+
+/* Pass the flags value that the flash return before it re-entered read
+ mode. */
+static void jedec_flash_failed(unsigned char code)
+{
+ /* Bit 5 being high indicates that there was an internal device
+ failure, erasure time limits exceeded or something */
+ if ((code & (1 << 5)) != 0)
+ {
+ printk("mtd: Internal Flash failure\n");
+ return;
+ }
+ printk("mtd: Programming didn't take\n");
+}
+
+/* This uses the erasure function described in the AMD Flash Handbook,
+ it will work for flashes with a fixed sector size only. Flashes with
+ a selection of sector sizes (ie the AMD Am29F800B) will need a different
+ routine. This routine tries to parallize erasing multiple chips/sectors
+ where possible */
+static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ // Does IO to the currently selected chip
+ #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift))
+ #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
+
+ unsigned long Time = 0;
+ unsigned long NoTime = 0;
+ unsigned long start = instr->addr, len = instr->len;
+ unsigned int I;
+ struct map_info *map = (struct map_info *)mtd->priv;
+ struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
+
+ // Verify the arguments..
+ if (start + len > mtd->size ||
+ (start % mtd->erasesize) != 0 ||
+ (len % mtd->erasesize) != 0 ||
+ (len/mtd->erasesize) == 0)
+ return -EINVAL;
+
+ jedec_flash_chip_scan(priv,start,len);
+
+ // Start the erase sequence on each chip
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ {
+ unsigned long off;
+ struct jedec_flash_chip *chip = priv->chips + I;
+
+ if (chip->length == 0)
+ continue;
+
+ if (chip->start + chip->length > chip->size)
+ {
+ printk("DIE\n");
+ return -EIO;
+ }
+
+ flwrite(0xF0,chip->start + 0x555);
+ flwrite(0xAA,chip->start + 0x555);
+ flwrite(0x55,chip->start + 0x2AA);
+ flwrite(0x80,chip->start + 0x555);
+ flwrite(0xAA,chip->start + 0x555);
+ flwrite(0x55,chip->start + 0x2AA);
+
+ /* 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 */
+ for (off = 0; off < len; off += chip->sectorsize)
+ {
+ // Check to make sure we didn't timeout
+ flwrite(0x30,chip->start + off);
+ if (off == 0)
+ continue;
+ if ((flread(chip->start + off) & (1 << 3)) != 0)
+ {
+ printk("mtd: Ack! We timed out the erase timer!\n");
+ return -EIO;
+ }
+ }
+ }
+
+ /* We could split this into a timer routine and return early, performing
+ background erasure.. Maybe later if the need warrents */
+
+ /* Poll the flash for erasure completion, specs say this can take as long
+ as 480 seconds to do all the sectors (for a 2 meg flash).
+ Erasure time is dependant on chip age, temp and wear.. */
+
+ /* This being a generic routine assumes a 32 bit bus. It does read32s
+ and bundles interleved chips into the same grouping. This will work
+ for all bus widths */
+ Time = 0;
+ NoTime = 0;
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ {
+ struct jedec_flash_chip *chip = priv->chips + I;
+ unsigned long off = 0;
+ unsigned todo[4] = {0,0,0,0};
+ unsigned todo_left = 0;
+ unsigned J;
+
+ if (chip->length == 0)
+ continue;
+
+ /* Find all chips in this data line, realistically this is all
+ or nothing up to the interleve count */
+ for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
+ {
+ if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
+ (chip->base & (~((1<<chip->addrshift)-1))))
+ {
+ todo_left++;
+ todo[priv->chips[J].base & ((1<<chip->addrshift)-1)] = 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];
+ unsigned long Count = 0;
+
+ /* 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 ;> */
+ 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)
+ {
+ for (J = 0; J != 4; J++)
+ {
+ __u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF;
+ __u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF;
+ __u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF;
+ if (todo[J] == 0)
+ continue;
+
+ if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2)
+ {
+// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2);
+ continue;
+ }
+
+ if (Byte1 == Byte2)
+ {
+ jedec_flash_failed(Byte3);
+ return -EIO;
+ }
+
+ todo[J] = 0;
+ todo_left--;
+ }
+
+/* if (NoTime == 0)
+ Time += HZ/10 - schedule_timeout(HZ/10);*/
+ NoTime = 0;
+
+ 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++;
+
+/* // Count time, max of 15s per sector (according to AMD)
+ if (Time > 15*len/mtd->erasesize*HZ)
+ {
+ printk("mtd: Flash Erase Timed out\n");
+ return -EIO;
+ } */
+ }
+
+ // Skip to the next chip if we used chip erase
+ if (chip->length == chip->size)
+ off = chip->size;
+ else
+ off += chip->sectorsize;
+
+ if (off >= chip->length)
+ break;
+ NoTime = 1;
+ }
+
+ for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
+ {
+ if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
+ (chip->base & (~((1<<chip->addrshift)-1))))
+ priv->chips[J].length = 0;
+ }
+ }
+
+ //printk("done\n");
+ if (instr->callback)
+ instr->callback(instr);
+ return 0;
+
+ #undef flread
+ #undef flwrite
+}
+
+/* This is the simple flash writing function. It writes to every byte, in
+ sequence. It takes care of how to properly address the flash if
+ the flash is interleved. It can only be used if all the chips in the
+ array are identical!*/
+static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ /* Does IO to the currently selected chip. It takes the bank addressing
+ base (which is divisable by the chip size) adds the necesary lower bits
+ of addrshift (interleve index) and then adds the control register index. */
+ #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+ #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+
+ struct map_info *map = (struct map_info *)mtd->priv;
+ 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;
+
+ //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;
+ unsigned long bank;
+ unsigned long boffset;
+
+ // Compute the base of the flash.
+ 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])*my_bank_size;
+ base = bank + boffset;
+
+ // 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++)
+ {
+ unsigned char oldbyte = map->read8(map,base+off);
+ unsigned char Last[4];
+ unsigned long Count = 0;
+
+ 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");
+
+ // Write
+ flwrite(0xAA,0x555);
+ flwrite(0x55,0x2AA);
+ flwrite(0xA0,0x555);
+ map->write8(map,*buf,base + off);
+ Last[0] = map->read8(map,base + off);
+ Last[1] = map->read8(map,base + off);
+ Last[2] = map->read8(map,base + off);
+
+ /* Wait for the flash to finish the operation. We store the last 4
+ status bytes that have been retrieved so we can determine why
+ it failed. The toggle bits keep toggling when there is a
+ failure */
+ for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
+ Count < 10000; Count++)
+ Last[Count % 4] = map->read8(map,base + off);
+ if (Last[(Count - 1) % 4] != *buf)
+ {
+ jedec_flash_failed(Last[(Count - 3) % 4]);
+ return -EIO;
+ }
+ }
+ }
+ *retlen = save_len;
+ return 0;
+}
+
+/* This is used to enhance the speed of the erase routine,
+ when things are being done to multiple chips it is possible to
+ parallize the operations, particularly full memory erases of multi
+ chip memories benifit */
+static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
+ unsigned long len)
+{
+ unsigned int I;
+
+ // Zero the records
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ priv->chips[I].start = priv->chips[I].length = 0;
+
+ // Intersect the region with each chip
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+ {
+ struct jedec_flash_chip *chip = priv->chips + I;
+ unsigned long ByteStart;
+ unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift);
+
+ // End is before this chip or the start is after it
+ if (start+len < chip->offset ||
+ ChipEndByte - (1 << chip->addrshift) < start)
+ continue;
+
+ if (start < chip->offset)
+ {
+ ByteStart = chip->offset;
+ chip->start = 0;
+ }
+ else
+ {
+ chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift;
+ ByteStart = start;
+ }
+
+ if (start + len >= ChipEndByte)
+ chip->length = (ChipEndByte - ByteStart) >> chip->addrshift;
+ else
+ chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift;
+ }
+}
+ /*}}}*/
+#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);
--- /dev/null
+/*
+ * Common code to handle map devices which are simple RAM
+ * (C) 2000 Red Hat. GPL'd.
+ * $Id: map_ram.c,v 1.11 2001/06/08 15:34:04 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/map.h>
+
+
+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 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;
+
+ map->write8(map, 0xAA, 0);
+ if (map->read8(map, 0) != 0xAA)
+ return NULL;
+
+ /* Check the last byte is RAM */
+ map->write8(map, 0x55, map->size-1);
+ if (map->read8(map, map->size-1) != 0x55)
+ return NULL;
+
+ 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);
+ if (!mtd)
+ return NULL;
+
+ memset(mtd, 0, sizeof(*mtd));
+
+ map->fldrv = &mapram_chipdrv;
+ mtd->priv = map;
+ mtd->name = map->name;
+ mtd->type = MTD_RAM;
+ mtd->erasesize = 0x10000;
+ mtd->size = map->size;
+ mtd->erase = mapram_erase;
+ mtd->read = mapram_read;
+ mtd->write = mapram_write;
+ mtd->sync = mapram_nop;
+ mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
+ mtd->erasesize = PAGE_SIZE;
+
+ MOD_INC_USE_COUNT;
+ return mtd;
+}
+
+
+static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+ map->copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+}
+
+static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+{
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+ map->copy_to(map, to, buf, len);
+ *retlen = len;
+ return 0;
+}
+
+static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ /* Yeah, it's inefficient. Who cares? It's faster than a _real_
+ flash erase. */
+ struct map_info *map = (struct map_info *)mtd->priv;
+ unsigned long i;
+
+ for (i=0; i<instr->len; i++)
+ map->write8(map, 0xFF, instr->addr + i);
+
+ if (instr->callback)
+ instr->callback(instr);
+
+ return 0;
+}
+
+static void mapram_nop(struct mtd_info *mtd)
+{
+ /* Nothing to see here */
+}
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_ram_init init_module
+#define map_ram_exit cleanup_module
+#endif
+
+static int __init map_ram_init(void)
+{
+ register_mtd_chip_driver(&mapram_chipdrv);
+ return 0;
+}
+
+static void __exit map_ram_exit(void)
+{
+ unregister_mtd_chip_driver(&mapram_chipdrv);
+}
+
+module_init(map_ram_init);
+module_exit(map_ram_exit);
--- /dev/null
+/*
+ * Common code to handle map devices which are simple ROM
+ * (C) 2000 Red Hat. GPL'd.
+ * $Id: map_rom.c,v 1.14 2001/06/02 14:30:43 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/map.h>
+
+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 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)
+{
+ struct mtd_info *mtd;
+
+ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+ if (!mtd)
+ return NULL;
+
+ memset(mtd, 0, sizeof(*mtd));
+
+ map->fldrv = &maprom_chipdrv;
+ mtd->priv = map;
+ mtd->name = map->name;
+ mtd->type = MTD_ROM;
+ mtd->size = map->size;
+ mtd->read = maprom_read;
+ mtd->write = maprom_write;
+ mtd->sync = maprom_nop;
+ mtd->flags = MTD_CAP_ROM;
+ mtd->erasesize = 131072;
+
+ MOD_INC_USE_COUNT;
+ return mtd;
+}
+
+
+static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+ map->copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+}
+
+static void maprom_nop(struct mtd_info *mtd)
+{
+ /* Nothing to see here */
+}
+
+static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+{
+ printk(KERN_NOTICE "maprom_write called\n");
+ return -EIO;
+}
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_rom_init init_module
+#define map_rom_exit cleanup_module
+#endif
+
+mod_init_t map_rom_init(void)
+{
+ register_mtd_chip_driver(&maprom_chipdrv);
+ return 0;
+}
+
+mod_exit_t map_rom_exit(void)
+{
+ unregister_mtd_chip_driver(&maprom_chipdrv);
+}
+
+module_init(map_rom_init);
+module_exit(map_rom_exit);
--- /dev/null
+/*
+ * MTD chip driver for pre-CFI Sharp flash chips
+ *
+ * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/delay.h>
+
+#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<<sharp->chipshift))
+ thislen = (1<<sharp->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<<sharp->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<<sharp->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(jiffies<timeo){
+ map->write32(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 <ds@lineo.com>\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);
+
--- /dev/null
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*======================================================================
+
+ $Id: doc1000.c,v 1.11 2000/11/24 13:43:16 dwmw2 Exp $
+
+======================================================================*/
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/iflash.h>
+
+/* Parameters that can be set with 'insmod' */
+
+static u_long base = 0xe0000;
+static int erase_timeout = 10*HZ; /* in ticks */
+static int retry_limit = 4; /* write retries */
+static u_long max_tries = 4096; /* status polling */
+
+MODULE_PARM(base,"l");
+MODULE_PARM(erase_timeout, "i");
+MODULE_PARM(retry_limit, "i");
+MODULE_PARM(max_tries, "i");
+
+#define WINDOW_SIZE 0x2000
+#define WINDOW_MASK (WINDOW_SIZE - 1)
+#define PAGEREG_LO (WINDOW_SIZE)
+#define PAGEREG_HI (WINDOW_SIZE + 2)
+
+static struct mtd_info *mymtd;
+static struct timer_list flashcard_timer;
+
+#define MAX_CELLS 32
+#define MAX_FLASH_DEVICES 8
+
+/* A flash region is composed of one or more "cells", where we allow
+ simultaneous erases if they are in different cells */
+
+
+
+struct mypriv {
+ u_char *baseaddr;
+ u_short curpage;
+ u_char locked;
+ u_short numdevices;
+ u_char interleave;
+ struct erase_info *cur_erases;
+ wait_queue_head_t wq;
+ u_char devstat[MAX_FLASH_DEVICES];
+ u_long devshift;
+};
+
+
+static void flashcard_periodic(u_long data);
+static int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr);
+static int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+static int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+static void flashcard_sync (struct mtd_info *mtd);
+
+static inline void resume_erase(volatile u_char *addr);
+static inline int suspend_erase(volatile u_char *addr);
+static inline int byte_write (volatile u_char *addr, u_char byte);
+static inline int word_write (volatile u_char *addr, __u16 word);
+static inline int check_write(volatile u_char *addr);
+static inline void block_erase (volatile u_char *addr);
+static inline int check_erase(volatile u_char *addr);
+
+#ifdef CONFIG_SMP
+#warning This is definitely not SMP safe. Lock the paging mechanism.
+#endif
+
+static u_char *pagein(struct mtd_info *mtd, u_long addr)
+{
+ struct mypriv *priv=mtd->priv;
+ u_short page = addr >> 13;
+
+ priv->baseaddr[PAGEREG_LO] = page & 0xff;
+ priv->baseaddr[PAGEREG_HI] = page >> 8;
+ priv->curpage = page;
+
+ return &priv->baseaddr[addr & WINDOW_MASK];
+}
+
+
+void flashcard_sync (struct mtd_info *mtd)
+{
+ struct mypriv *priv=mtd->priv;
+
+ flashcard_periodic((u_long) mtd);
+ printk("sync...");
+ if (priv->cur_erases)
+ interruptible_sleep_on(&priv->wq);
+ printk("Done.\n");
+}
+
+int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ u_char *pageaddr;
+ struct mypriv *priv=mtd->priv;
+ struct erase_info **tmp=&priv->cur_erases;
+
+ if (instr->len != mtd->erasesize)
+ return -EINVAL;
+ if (instr->addr + instr->len > mtd->size)
+ return -EINVAL;
+
+ pageaddr=pagein(mtd,instr->addr);
+ instr->mtd = mtd;
+ instr->dev = instr->addr >> priv->devshift;
+ instr->cell = (instr->addr - (instr->dev << priv->devshift)) / mtd->erasesize;
+ instr->next = NULL;
+ instr->state = MTD_ERASE_PENDING;
+
+ while (*tmp)
+ {
+ tmp = &((*tmp) -> next);
+ }
+
+ *tmp = instr;
+ flashcard_periodic((u_long)mtd);
+ return 0;
+}
+
+
+int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+ u_char *pageaddr=pagein(mtd,from);
+ struct mypriv *priv=mtd->priv;
+ u_char device = from >> priv->devshift;
+ u_char cell = (int) (from - (device << priv->devshift)) / mtd->erasesize;
+ int ret = 0, timeron = 0;
+
+ if ((from & WINDOW_MASK) + len <= WINDOW_SIZE)
+ *retlen = len;
+ else
+ *retlen = WINDOW_SIZE - (from & WINDOW_MASK);
+
+ if (priv->devstat[device])
+ {
+
+ /* There is an erase in progress or pending for this device. Stop it */
+ timeron = del_timer(&flashcard_timer);
+
+ if (priv->cur_erases && priv->cur_erases->cell == cell)
+
+ {
+ /* The erase is on the current cell. Just return all 0xff */
+ add_timer(&flashcard_timer);
+
+
+ printk("Cell %d currently erasing. Setting to all 0xff\n",cell);
+ memset(buf, 0xff, *retlen);
+ return 0;
+ }
+ if (priv->devstat[device] == MTD_ERASING)
+ {
+ ret = suspend_erase(pageaddr);
+ priv->devstat[device] = MTD_ERASE_SUSPEND;
+
+ if (ret)
+ {
+ printk("flashcard: failed to suspend erase\n");
+ add_timer (&flashcard_timer);
+ return ret;
+ }
+ }
+
+ }
+
+ writew(IF_READ_ARRAY, (u_long)pageaddr & ~1);
+
+ ret = 0;
+ memcpy (buf, pageaddr, *retlen);
+
+ writew(IF_READ_CSR, (u_long)pageaddr & ~1);
+
+
+ if (priv->devstat[device] & MTD_ERASE_SUSPEND)
+ {
+ resume_erase(pageaddr);
+ priv->devstat[device]=MTD_ERASING;
+ }
+
+
+ if (timeron) add_timer (&flashcard_timer);
+
+ return ret;
+}
+
+
+int flashcard_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;
+ u_char *endaddr, *startaddr;
+ register u_char *pageaddr;
+ u_char device = to >> priv->devshift;
+/* jiffies_t oldj=jiffies;*/
+ int ret;
+
+ while (priv->devstat[device])
+ {
+ flashcard_sync(mtd);
+ }
+
+ if ((to & WINDOW_MASK) + len <= WINDOW_SIZE)
+ *retlen = len;
+ else
+ *retlen = WINDOW_SIZE - (to & WINDOW_MASK);
+
+ pageaddr = pagein(mtd, to);
+ startaddr = (u_char *)((u_long) pageaddr & ~1);
+ endaddr = pageaddr+(*retlen);
+
+
+
+ /* Set up to read */
+ writew(IF_READ_CSR, startaddr);
+
+ /* Make sure it's aligned by reading the first byte if necessary */
+ if (to & 1)
+ {
+ /* Unaligned access */
+
+ u_char cbuf;
+
+ cbuf = *buf;
+
+ if (!((u_long)pageaddr & 0xf))
+ schedule();
+
+ ret = byte_write(pageaddr, cbuf);
+ if (ret) return ret;
+
+ pageaddr++; buf++;
+ }
+
+
+ for ( ; pageaddr + 1 < endaddr; buf += 2, pageaddr += 2)
+ {
+ /* if ((u_long)pageaddr & 0xf) schedule();*/
+
+ ret = word_write(pageaddr, *(__u16 *)buf);
+ if (ret)
+ return ret;
+ }
+
+ if (pageaddr != endaddr)
+ {
+ /* One more byte to write at the end. */
+ u_char cbuf;
+
+ cbuf = *buf;
+
+ ret = byte_write(pageaddr, cbuf);
+
+ if (ret) return ret;
+ }
+
+ return check_write(startaddr);
+/* printk("Time taken in flashcard_write: %lx jiffies\n",jiffies - oldj);*/
+}
+
+
+
+
+/*====================================================================*/
+
+static inline int byte_write (volatile u_char *addr, u_char byte)
+{
+ register u_char status;
+ register u_short i = 0;
+
+ do {
+ status = readb(addr);
+ if (status & CSR_WR_READY)
+ {
+ writeb(IF_WRITE & 0xff, addr);
+ writeb(byte, addr);
+ return 0;
+ }
+ i++;
+ } while(i < max_tries);
+
+
+ printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status);
+ return -EIO;
+}
+
+static inline int word_write (volatile u_char *addr, __u16 word)
+{
+ register u_short status;
+ register u_short i = 0;
+
+ do {
+ status = readw(addr);
+ if ((status & CSR_WR_READY) == CSR_WR_READY)
+ {
+ writew(IF_WRITE, addr);
+ writew(word, addr);
+ return 0;
+ }
+ i++;
+ } while(i < max_tries);
+
+ printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status);
+ return -EIO;
+}
+
+static inline void block_erase (volatile u_char *addr)
+{
+ writew(IF_BLOCK_ERASE, addr);
+ writew(IF_CONFIRM, addr);
+}
+
+
+static inline int check_erase(volatile u_char *addr)
+{
+ __u16 status;
+
+/* writew(IF_READ_CSR, addr);*/
+ status = readw(addr);
+
+
+ if ((status & CSR_WR_READY) != CSR_WR_READY)
+ return -EBUSY;
+
+ if (status & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR))
+ {
+ printk(KERN_NOTICE "flashcard: erase failed, status 0x%x\n",
+ status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline int suspend_erase(volatile u_char *addr)
+{
+ __u16 status;
+ u_long i = 0;
+
+ writew(IF_ERASE_SUSPEND, addr);
+ writew(IF_READ_CSR, addr);
+
+ do {
+ status = readw(addr);
+ if ((status & CSR_WR_READY) == CSR_WR_READY)
+ return 0;
+ i++;
+ } while(i < max_tries);
+
+ printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status);
+ return -EIO;
+
+}
+
+static inline void resume_erase(volatile u_char *addr)
+{
+ __u16 status;
+
+ writew(IF_READ_CSR, addr);
+ status = readw(addr);
+
+ /* Only give resume signal if the erase is really suspended */
+ if (status & CSR_ERA_SUSPEND)
+ writew(IF_CONFIRM, addr);
+}
+
+static inline void reset_block(volatile u_char *addr)
+{
+ u_short i;
+ __u16 status;
+
+ writew(IF_CLEAR_CSR, addr);
+
+ for (i = 0; i < 100; i++) {
+ writew(IF_READ_CSR, addr);
+ status = readw(addr);
+ if (status != 0xffff) break;
+ udelay(1000);
+ }
+
+ writew(IF_READ_CSR, addr);
+}
+
+static inline int check_write(volatile u_char *addr)
+{
+ u_short status, i = 0;
+
+ writew(IF_READ_CSR, addr);
+
+ do {
+ status = readw(addr);
+ if (status & (CSR_WR_ERR | CSR_VPP_LOW))
+ {
+ printk(KERN_NOTICE "flashcard: write failure at %p, status 0x%x\n", addr, status);
+ reset_block(addr);
+ return -EIO;
+ }
+ if ((status & CSR_WR_READY) == CSR_WR_READY)
+ return 0;
+ i++;
+ } while (i < max_tries);
+
+ printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status);
+ return -EIO;
+}
+
+
+/*====================================================================*/
+
+
+
+static void flashcard_periodic(unsigned long data)
+{
+ register struct mtd_info *mtd = (struct mtd_info *)data;
+ register struct mypriv *priv = mtd->priv;
+ struct erase_info *erase = priv->cur_erases;
+ u_char *pageaddr;
+
+ del_timer (&flashcard_timer);
+
+ if (!erase)
+ return;
+
+ pageaddr = pagein(mtd, erase->addr);
+
+ if (erase->state == MTD_ERASE_PENDING)
+ {
+ block_erase(pageaddr);
+ priv->devstat[erase->dev] = erase->state = MTD_ERASING;
+ erase->time = jiffies;
+ erase->retries = 0;
+ }
+ else if (erase->state == MTD_ERASING)
+ {
+ /* It's trying to erase. Check whether it's finished */
+
+ int ret = check_erase(pageaddr);
+
+ if (!ret)
+ {
+ /* It's finished OK */
+ priv->devstat[erase->dev] = 0;
+ priv->cur_erases = erase->next;
+ erase->state = MTD_ERASE_DONE;
+ if (erase->callback)
+ (*(erase->callback))(erase);
+ else
+ kfree(erase);
+ }
+ else if (ret == -EIO)
+ {
+ if (++erase->retries > retry_limit)
+ {
+ printk("Failed too many times. Giving up\n");
+ priv->cur_erases = erase->next;
+ priv->devstat[erase->dev] = 0;
+ erase->state = MTD_ERASE_FAILED;
+ if (erase->callback)
+ (*(erase->callback))(erase);
+ else
+ kfree(erase);
+ }
+ else
+ priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING;
+ }
+ else if (erase->time + erase_timeout < jiffies)
+ {
+ printk("Flash erase timed out. The world is broken.\n");
+
+ /* Just ignore and hope it goes away. For a while, read ops will give the CSR
+ and writes won't work. */
+
+ priv->cur_erases = erase->next;
+ priv->devstat[erase->dev] = 0;
+ erase->state = MTD_ERASE_FAILED;
+ if (erase->callback)
+ (*(erase->callback))(erase);
+ else
+ kfree(erase);
+ }
+ }
+
+ if (priv->cur_erases)
+ {
+ flashcard_timer.expires = jiffies + HZ;
+ add_timer (&flashcard_timer);
+ }
+ else
+ wake_up_interruptible(&priv->wq);
+
+}
+
+#if defined (MODULE) && LINUX_VERSION_CODE < 0x20211
+#define init_doc1000 init_module
+#define cleanup_doc1000 cleanup_module
+#endif
+
+int __init init_doc1000(void)
+{
+ struct mypriv *priv;
+
+ if (!base)
+ {
+ printk(KERN_NOTICE "flashcard: No start address for memory device.\n");
+ return -EINVAL;
+ }
+
+ mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+
+ if (!mymtd)
+ {
+ printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device.\n");
+ return -ENOMEM;
+ }
+
+ memset(mymtd,0,sizeof(struct mtd_info));
+
+ mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL);
+ if (!mymtd->priv)
+ {
+ kfree(mymtd);
+ printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device's private data.\n");
+ return -ENOMEM;
+ }
+
+
+
+
+ priv=mymtd->priv;
+ init_waitqueue_head(&priv->wq);
+
+ memset (priv,0,sizeof(struct mypriv));
+
+ priv->baseaddr = phys_to_virt(base);
+ priv->numdevices = 4;
+
+ mymtd->name = "M-Systems DiskOnChip 1000";
+
+ mymtd->size = 0x100000;
+ mymtd->flags = MTD_CLEAR_BITS | MTD_ERASEABLE;
+ mymtd->erase = flashcard_erase;
+ mymtd->point = NULL;
+ mymtd->unpoint = NULL;
+ mymtd->read = flashcard_read;
+ mymtd->write = flashcard_write;
+
+ mymtd->sync = flashcard_sync;
+ mymtd->erasesize = 0x10000;
+ // mymtd->interleave = 2;
+ priv->devshift = 24;
+ mymtd->type = MTD_NORFLASH;
+
+ if (add_mtd_device(mymtd))
+ {
+ printk(KERN_NOTICE "MTD device registration failed!\n");
+ kfree(mymtd->priv);
+ kfree(mymtd);
+ return -EAGAIN;
+ }
+
+ init_timer(&flashcard_timer);
+ flashcard_timer.function = flashcard_periodic;
+ flashcard_timer.data = (u_long)mymtd;
+ return 0;
+}
+
+static void __init cleanup_doc1000(void)
+{
+ kfree (mymtd->priv);
+ del_mtd_device(mymtd);
+ kfree(mymtd);
+}
+
+#if LINUX_VERSION_CODE >= 0x20211
+module_init(init_doc1000);
+module_exit(cleanup_doc1000);
+#endif
--- /dev/null
+
+/*
+ * Linux driver for Disk-On-Chip 2000 and Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2000.c,v 1.43 2001/06/02 14:30:43 dwmw2 Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
+#include <linux/mtd/doc2000.h>
+
+#define DOC_SUPPORT_2000
+#define DOC_SUPPORT_MILLENNIUM
+
+#ifdef DOC_SUPPORT_2000
+#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k)
+#else
+#define DoC_is_2000(doc) (0)
+#endif
+
+#ifdef DOC_SUPPORT_MILLENNIUM
+#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
+#else
+#define DoC_is_Millennium(doc) (0)
+#endif
+
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcpy_from|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
+*/
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+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);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
+
+static struct mtd_info *doc2klist = NULL;
+
+/* Perform the required delay cycles by reading from the appropriate register */
+static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles)
+{
+ volatile char dummy;
+ int i;
+
+ for (i = 0; i < cycles; i++) {
+ if (DoC_is_Millennium(doc))
+ dummy = ReadDOC(doc->virtadr, NOP);
+ else
+ dummy = ReadDOC(doc->virtadr, DOCStatus);
+ }
+
+}
+
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct DiskOnChip *doc)
+{
+ unsigned long docptr = doc->virtadr;
+ unsigned short c = 0xffff;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
+
+ /* Out-of-line routine to wait for chip response */
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
+ ;
+
+ if (c == 0)
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
+ return (c == 0);
+}
+
+static inline int DoC_WaitReady(struct DiskOnChip *doc)
+{
+ unsigned long docptr = doc->virtadr;
+ /* This is inline, to optimise the common case, where it's ready instantly */
+ int ret = 0;
+
+ /* 4 read form NOP register should be issued in prior to the read from CDSNControl
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 4);
+
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+
+ /* issue 2 read from NOP register after reading from CDSNControl register
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 2);
+
+ 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
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
+ unsigned char xtraflags)
+{
+ unsigned long docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags |= CDSN_CTRL_FLASH_IO;
+
+ /* Assert the CLE (Command Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ if (DoC_is_Millennium(doc))
+ WriteDOC(command, docptr, CDSNSlowIO);
+
+ /* Send the command */
+ WriteDOC_(command, docptr, doc->ioreg);
+
+ /* Lower the CLE line */
+ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */
+ return DoC_WaitReady(doc);
+}
+
+/* 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
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
+ unsigned char xtraflags1, unsigned char xtraflags2)
+{
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags1 |= CDSN_CTRL_FLASH_IO;
+
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Send the address */
+ /* Devices with 256-byte page are addressed as:
+ Column (bits 0-7), Page (bits 8-15, 16-23, 24-31)
+ * there is no device on the market with page256
+ and more than 24 bits.
+ Devices with 512-byte page are addressed as:
+ Column (bits 0-7), Page (bits 9-16, 17-24, 25-31)
+ * 25-31 is sent only if the chip support it.
+ * bit 8 changes the read command to be sent
+ (NAND_CMD_READ0 or NAND_CMD_READ1).
+ */
+
+ if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
+ }
+
+ if (doc->page256) {
+ ofs = ofs >> 8;
+ } else {
+ ofs = ofs >> 9;
+ }
+
+ if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) {
+ for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
+ }
+ }
+
+ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
+
+ /* FIXME: The SlowIO's for millennium could be replaced by
+ a single WritePipeTerm here. mf. */
+
+ /* Lower the ALE line */
+ WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Wait for the chip to respond - Software requirement 11.4.1 */
+ return DoC_WaitReady(doc);
+}
+
+/* Read a buffer from DoC, taking care of Millennium odditys */
+static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len)
+{
+ int dummy;
+ int modulus = 0xffff;
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ if (DoC_is_Millennium(doc)) {
+ /* Read the data via the internal pipeline through CDSN IO register,
+ see Pipelined Read Operations 11.3 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+
+ /* Millennium should use the LastDataRead register - Pipeline Reads */
+ len--;
+
+ /* This is needed for correctly ECC calculation */
+ modulus = 0xff;
+ }
+
+ for (i = 0; i < len; i++)
+ buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus));
+
+ if (DoC_is_Millennium(doc)) {
+ buf[i] = ReadDOC(docptr, LastDataRead);
+ }
+}
+
+/* Write a buffer to DoC, taking care of Millennium odditys */
+static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len)
+{
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++)
+ WriteDOC_(buf[i], docptr, doc->ioreg + i);
+
+ if (DoC_is_Millennium(doc)) {
+ WriteDOC(0x00, docptr, WritePipeTerm);
+ }
+}
+
+
+/* DoC_SelectChip: Select a given flash chip within the current floor */
+
+static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip)
+{
+ unsigned long docptr = doc->virtadr;
+
+ /* Software requirement 11.4.4 before writing DeviceSelect */
+ /* Deassert the CE line to eliminate glitches on the FCE# outputs */
+ WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Select the individual flash chip requested */
+ WriteDOC(chip, docptr, CDSNDeviceSelect);
+ DoC_Delay(doc, 4);
+
+ /* Reassert the CE line */
+ WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr,
+ CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Wait for it to be ready */
+ return DoC_WaitReady(doc);
+}
+
+/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
+
+static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor)
+{
+ unsigned long docptr = doc->virtadr;
+
+ /* Select the floor (bank) of chips required */
+ WriteDOC(floor, docptr, FloorSelect);
+
+ /* Wait for the chip to be ready */
+ return DoC_WaitReady(doc);
+}
+
+/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
+
+static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
+{
+ int mfr, id, i;
+ volatile char dummy;
+
+ /* Page in the required floor/chip */
+ DoC_SelectFloor(doc, floor);
+ DoC_SelectChip(doc, chip);
+
+ /* Reset the chip */
+ if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (reset) for %d,%d returned true\n",
+ floor, chip);
+ return 0;
+ }
+
+
+ /* Read the NAND chip ID: 1. Send ReadID command */
+ if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (ReadID) for %d,%d returned true\n",
+ floor, chip);
+ return 0;
+ }
+
+ /* Read the NAND chip ID: 2. Send address byte zero */
+ DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0);
+
+ /* Read the manufacturer and device id codes from the device */
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ id = ReadDOC_(doc->virtadr, doc->ioreg);
+
+ /* No response - return failure */
+ if (mfr == 0xff || mfr == 0)
+ return 0;
+
+ /* Check it's the same as the first chip we identified.
+ * M-Systems say that any given DiskOnChip device should only
+ * contain _one_ type of flash part, although that's not a
+ * hardware restriction. */
+ if (doc->mfr) {
+ if (doc->mfr == mfr && doc->id == id)
+ return 1; /* This is another the same the first */
+ else
+ printk(KERN_WARNING
+ "Flash chip at floor %d, chip %d is different:\n",
+ floor, chip);
+ }
+
+ /* Print and store the manufacturer and ID codes. */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (mfr == nand_flash_ids[i].manufacture_id &&
+ id == nand_flash_ids[i].model_id) {
+ printk(KERN_INFO
+ "Flash chip found: Manufacturer ID: %2.2X, "
+ "Chip ID: %2.2X (%s)\n", mfr, id,
+ nand_flash_ids[i].name);
+ if (!doc->mfr) {
+ doc->mfr = mfr;
+ doc->id = id;
+ doc->chipshift =
+ nand_flash_ids[i].chipshift;
+ doc->page256 = nand_flash_ids[i].page256;
+ doc->pageadrlen =
+ nand_flash_ids[i].pageadrlen;
+ doc->erasesize =
+ nand_flash_ids[i].erasesize;
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+
+ /* We haven't fully identified the chip. Print as much as we know. */
+ printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n",
+ id, mfr);
+
+ printk(KERN_WARNING "Please report to dwmw2@infradead.org\n");
+ return 0;
+}
+
+/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
+
+static void DoC_ScanChips(struct DiskOnChip *this)
+{
+ int floor, chip;
+ int numchips[MAX_FLOORS];
+ int maxchips = MAX_CHIPS;
+ int ret = 1;
+
+ this->numchips = 0;
+ this->mfr = 0;
+ this->id = 0;
+
+ if (DoC_is_Millennium(this))
+ maxchips = MAX_CHIPS_MIL;
+
+ /* For each floor, find the number of valid chips it contains */
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
+ ret = 1;
+ numchips[floor] = 0;
+ for (chip = 0; chip < maxchips && ret != 0; chip++) {
+
+ ret = DoC_IdentChip(this, floor, chip);
+ if (ret) {
+ numchips[floor]++;
+ this->numchips++;
+ }
+ }
+ }
+
+ /* If there are none at all that we recognise, bail */
+ if (!this->numchips) {
+ printk("No flash chips recognised.\n");
+ return;
+ }
+
+ /* Allocate an array to hold the information for each chip */
+ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
+ if (!this->chips) {
+ printk("No memory for allocating chip info structures\n");
+ return;
+ }
+
+ ret = 0;
+
+ /* Fill out the chip array with {floor, chipno} for each
+ * detected chip in the device. */
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
+ for (chip = 0; chip < numchips[floor]; chip++) {
+ this->chips[ret].floor = floor;
+ this->chips[ret].chip = chip;
+ this->chips[ret].curadr = 0;
+ this->chips[ret].curmode = 0x50;
+ ret++;
+ }
+ }
+
+ /* 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 MiB\n",
+ this->numchips, this->totlen >> 20);
+}
+
+static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
+{
+ int tmp1, tmp2, retval;
+ if (doc1->physadr == doc2->physadr)
+ return 1;
+
+ /* Use the alias resolution register which was set aside for this
+ * purpose. If it's value is the same on both chips, they might
+ * be the same chip, and we write to one and check for a change in
+ * the other. It's unclear if this register is usuable in the
+ * DoC 2000 (it's in the Millennium docs), but it seems to work. */
+ tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
+ tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
+ if (tmp1 != tmp2)
+ return 0;
+
+ WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution);
+ tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
+ if (tmp2 == (tmp1 + 1) % 0xff)
+ retval = 1;
+ else
+ retval = 0;
+
+ /* Restore register contents. May not be necessary, but do it just to
+ * be safe. */
+ WriteDOC(tmp1, doc1->virtadr, AliasResolution);
+
+ return retval;
+}
+
+static const char im_name[] = "DoC2k_init";
+
+/* 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 <kaos@ocs.com.au> 29 Oct 2000.
+ */
+static void DoC2k_init(struct mtd_info *mtd)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ struct DiskOnChip *old = NULL;
+
+ /* We must avoid being called twice for the same device. */
+
+ if (doc2klist)
+ old = (struct DiskOnChip *) doc2klist->priv;
+
+ while (old) {
+ if (DoC2k_is_alias(old, this)) {
+ printk(KERN_NOTICE
+ "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
+ this->physadr);
+ iounmap((void *) this->virtadr);
+ kfree(mtd);
+ return;
+ }
+ if (old->nextdoc)
+ old = (struct DiskOnChip *) old->nextdoc->priv;
+ else
+ old = NULL;
+ }
+
+
+ switch (this->ChipID) {
+ case DOC_ChipID_Doc2k:
+ mtd->name = "DiskOnChip 2000";
+ this->ioreg = DoC_2k_CDSN_IO;
+ break;
+ case DOC_ChipID_DocMil:
+ mtd->name = "DiskOnChip Millennium";
+ this->ioreg = DoC_Mil_CDSN_IO;
+ break;
+ }
+
+ printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
+ this->physadr);
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->size = 0;
+ mtd->erasesize = 0;
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+ mtd->module = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = doc_read;
+ mtd->write = doc_write;
+ mtd->read_ecc = doc_read_ecc;
+ mtd->write_ecc = doc_write_ecc;
+ mtd->read_oob = doc_read_oob;
+ mtd->write_oob = doc_write_oob;
+ mtd->sync = NULL;
+
+ this->totlen = 0;
+ this->numchips = 0;
+
+ this->curfloor = -1;
+ this->curchip = -1;
+
+ /* Ident all the chips present. */
+ DoC_ScanChips(this);
+
+ if (!this->totlen) {
+ kfree(mtd);
+ iounmap((void *) this->virtadr);
+ } else {
+ this->nextdoc = doc2klist;
+ doc2klist = mtd;
+ mtd->size = this->totlen;
+ mtd->erasesize = this->erasesize;
+ add_mtd_device(mtd);
+ return;
+ }
+}
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf)
+{
+ /* Just a special case of doc_read_ecc */
+ return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
+}
+
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf, u_char * eccbuf)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ unsigned long docptr;
+ struct Nand *mychip;
+ unsigned char syndrome[6];
+ volatile char dummy;
+ int i, len256 = 0, ret=0;
+
+ docptr = this->virtadr;
+
+ /* Don't allow read past end of device */
+ if (from >= this->totlen)
+ return -EINVAL;
+
+ /* Don't allow a single read to cross a 512-byte block boundary */
+ if (from + len > ((from | 0x1ff) + 1))
+ len = ((from | 0x1ff) + 1) - from;
+
+ /* The ECC will not be calculated correctly if less than 512 is read */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector read (adr: %lx size %lx)\n",
+ (long) from, (long) len);
+
+ /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
+
+
+ /* Find the chip which is to be used and select it */
+ mychip = &this->chips[from >> (this->chipshift)];
+
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
+
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ DoC_Command(this,
+ (!this->page256
+ && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
+ CDSN_CTRL_ECC_IO);
+
+ if (eccbuf) {
+ /* Prime the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ }
+
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && from + len > (from | 0xff) + 1) {
+ len256 = (from | 0xff) + 1 - from;
+ DoC_ReadBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
+ CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
+ }
+
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
+ /* Let the caller know we completed it */
+ *retlen = len;
+
+ if (eccbuf) {
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ /* Note: this will work even with 2M x 8bit devices as */
+ /* they have 8 bytes of OOB per 256 page. mf. */
+ DoC_ReadBuf(this, eccbuf, 6);
+
+ /* Flush the pipeline */
+ if (DoC_is_Millennium(this)) {
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+ i = ReadDOC(docptr, ECCConf);
+ } else {
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ i = ReadDOC(docptr, 2k_ECCStatus);
+ }
+
+ /* Check the ECC Status */
+ if (i & 0x80) {
+ int nb_errors;
+ /* There was an ECC error */
+#ifdef ECC_DEBUG
+ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] =
+ ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
+
+#ifdef ECC_DEBUG
+ printk("Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
+ }
+
+#ifdef PSYCHO_DEBUG
+ printk("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (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);
+ }
+
+ /* 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;
+}
+
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf)
+{
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
+}
+
+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)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
+ unsigned long docptr;
+ volatile char dummy;
+ int len256 = 0;
+ struct Nand *mychip;
+
+ docptr = this->virtadr;
+
+ /* Don't allow write past end of device */
+ if (to >= this->totlen)
+ return -EINVAL;
+
+ /* Don't allow a single write to cross a 512-byte block boundary */
+ if (to + len > ((to | 0x1ff) + 1))
+ len = ((to | 0x1ff) + 1) - to;
+
+ /* The ECC will not be calculated correctly if less than 512 is written */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector write (adr: %lx size %lx)\n",
+ (long) to, (long) len);
+
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
+
+ /* Find the chip which is to be used and select it */
+ mychip = &this->chips[to >> (this->chipshift)];
+
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
+
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* Set device to main plane of flash */
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(this,
+ (!this->page256
+ && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
+
+ if (eccbuf) {
+ /* Prime the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ }
+
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && to + len > (to | 0xff) + 1) {
+ len256 = (to | 0xff) + 1 - to;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+ return -EIO;
+ }
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
+ CDSN_CTRL_ECC_IO);
+ }
+
+ DoC_WriteBuf(this, &buf[len256], len - len256);
+
+ if (eccbuf) {
+ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ if (DoC_is_Millennium(this)) {
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ } else {
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ for (di = 0; di < 6; di++) {
+ eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
+ }
+
+ /* Reset the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+
+#ifdef PSYCHO_DEBUG
+ printk
+ ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
+#endif
+ }
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+ return -EIO;
+ }
+
+ /* Let the caller know we completed it */
+ *retlen = len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+ size_t dummy;
+
+ /* Write the ECC data to flash */
+ for (di=0; di<6; di++)
+ x[di] = eccbuf[di];
+
+ x[6]=0x55;
+ x[7]=0x55;
+
+ return doc_write_oob(mtd, to, 8, &dummy, x);
+ }
+
+ return 0;
+}
+
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, u_char * buf)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
+ unsigned long docptr;
+ struct Nand *mychip;
+
+ docptr = this->virtadr;
+
+ mychip = &this->chips[ofs >> this->chipshift];
+
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_ReadBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff),
+ CDSN_CTRL_WP, 0);
+ }
+
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
+ *retlen = len;
+ /* 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);
+
+}
+
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, const u_char * buf)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+ int dummy;
+
+ // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
+ // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+ return -EIO;
+ }
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0);
+ }
+
+ DoC_WriteBuf(this, &buf[len256], len - len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+ return -EIO;
+ }
+
+ *retlen = len;
+ return 0;
+
+}
+
+int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ __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 (%x != %x)n",
+ len, mtd->erasesize);
+
+ docptr = this->virtadr;
+
+ mychip = &this->chips[ofs >> this->chipshift];
+
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ instr->state = MTD_ERASE_PENDING;
+
+ DoC_Command(this, NAND_CMD_ERASE1, 0);
+ DoC_Address(this, ADDR_PAGE, ofs, 0, 0);
+ DoC_Command(this, NAND_CMD_ERASE2, 0);
+
+ instr->state = MTD_ERASING;
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error writing\n");
+ /* There was an error */
+ instr->state = MTD_ERASE_FAILED;
+ } else
+ instr->state = MTD_ERASE_DONE;
+
+ if (instr->callback)
+ instr->callback(instr);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ *
+ * Module stuff
+ *
+ ****************************************************************************/
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define cleanup_doc2000 cleanup_module
+#define init_doc2000 init_module
+#endif
+
+int __init init_doc2000(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
+ return 0;
+}
+
+static void __exit cleanup_doc2000(void)
+{
+ struct mtd_info *mtd;
+ struct DiskOnChip *this;
+
+ while ((mtd = doc2klist)) {
+ this = (struct DiskOnChip *) mtd->priv;
+ doc2klist = this->nextdoc;
+
+ del_mtd_device(mtd);
+
+ iounmap((void *) this->virtadr);
+ kfree(this->chips);
+ kfree(mtd);
+ }
+ inter_module_unregister(im_name);
+}
+
+module_exit(cleanup_doc2000);
+module_init(init_doc2000);
--- /dev/null
+
+/*
+ * Linux driver for Disk-On-Chip Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2001.c,v 1.34 2001/06/02 14:30:43 dwmw2 Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
+#include <linux/mtd/doc2000.h>
+
+/* #define ECC_DEBUG */
+
+/* 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
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+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);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
+
+static struct mtd_info *docmillist = NULL;
+
+/* Perform the required delay cycles by reading from the NOP register */
+static void DoC_Delay(unsigned long docptr, unsigned short cycles)
+{
+ volatile char dummy;
+ int i;
+
+ for (i = 0; i < cycles; i++)
+ dummy = ReadDOC(docptr, NOP);
+}
+
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(unsigned long docptr)
+{
+ unsigned short c = 0xffff;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
+
+ /* Out-of-line routine to wait for chip response */
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
+ ;
+
+ if (c == 0)
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
+ return (c == 0);
+}
+
+static inline int DoC_WaitReady(unsigned long docptr)
+{
+ /* This is inline, to optimise the common case, where it's ready instantly */
+ int ret = 0;
+
+ /* 4 read form NOP register should be issued in prior to the read from CDSNControl
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(docptr, 4);
+
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(docptr);
+
+ /* issue 2 read from NOP register after reading from CDSNControl register
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(docptr, 2);
+
+ return ret;
+}
+
+/* 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,
+ unsigned char xtraflags)
+{
+ /* Assert the CLE (Command Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(docptr, 4);
+
+ /* Send the command */
+ 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 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,
+ unsigned char xtraflags1, unsigned char xtraflags2)
+{
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(docptr, 4);
+
+ /* Send the address */
+ switch (numbytes)
+ {
+ case 1:
+ /* 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, 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, 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;
+ }
+
+ /* Lower the ALE line */
+ WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(docptr, 4);
+}
+
+/* DoC_SelectChip: Select a given flash chip within the current floor */
+static int DoC_SelectChip(unsigned long docptr, int chip)
+{
+ /* Select the individual flash chip requested */
+ WriteDOC(chip, docptr, CDSNDeviceSelect);
+ DoC_Delay(docptr, 4);
+
+ /* Wait for it to be ready */
+ return DoC_WaitReady(docptr);
+}
+
+/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
+static int DoC_SelectFloor(unsigned long docptr, int floor)
+{
+ /* Select the floor (bank) of chips required */
+ WriteDOC(floor, docptr, FloorSelect);
+
+ /* Wait for the chip to be ready */
+ return DoC_WaitReady(docptr);
+}
+
+/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
+static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
+{
+ int mfr, id, i;
+ volatile char dummy;
+
+ /* Page in the required floor/chip
+ FIXME: is this supported by Millennium ?? */
+ DoC_SelectFloor(doc->virtadr, floor);
+ DoC_SelectChip(doc->virtadr, chip);
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_WaitReady(doc->virtadr);
+
+ /* Read the NAND chip ID: 1. Send ReadID command */
+ DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP);
+
+ /* Read the NAND chip ID: 2. Send address byte zero */
+ DoC_Address(doc->virtadr, 1, 0x00, CDSN_CTRL_WP, 0x00);
+
+ /* Read the manufacturer and device id codes of the flash device through
+ 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);
+
+ 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)
+ return 0;
+
+ /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (mfr == nand_flash_ids[i].manufacture_id &&
+ id == nand_flash_ids[i].model_id) {
+ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
+ "Chip ID: %2.2X (%s)\n",
+ mfr, id, nand_flash_ids[i].name);
+ doc->mfr = mfr;
+ doc->id = id;
+ doc->chipshift = nand_flash_ids[i].chipshift;
+ break;
+ }
+ }
+
+ if (nand_flash_ids[i].name == NULL)
+ return 0;
+ else
+ return 1;
+}
+
+/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
+static void DoC_ScanChips(struct DiskOnChip *this)
+{
+ int floor, chip;
+ int numchips[MAX_FLOORS_MIL];
+ int ret;
+
+ this->numchips = 0;
+ this->mfr = 0;
+ this->id = 0;
+
+ /* For each floor, find the number of valid chips it contains */
+ for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) {
+ numchips[floor] = 0;
+ for (chip = 0; chip < MAX_CHIPS_MIL && ret != 0; chip++) {
+ ret = DoC_IdentChip(this, floor, chip);
+ if (ret) {
+ numchips[floor]++;
+ this->numchips++;
+ }
+ }
+ }
+ /* If there are none at all that we recognise, bail */
+ if (!this->numchips) {
+ printk("No flash chips recognised.\n");
+ return;
+ }
+
+ /* Allocate an array to hold the information for each chip */
+ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
+ if (!this->chips){
+ printk("No memory for allocating chip info structures\n");
+ return;
+ }
+
+ /* Fill out the chip array with {floor, chipno} for each
+ * detected chip in the device. */
+ for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) {
+ for (chip = 0 ; chip < numchips[floor] ; chip++) {
+ this->chips[ret].floor = floor;
+ this->chips[ret].chip = chip;
+ this->chips[ret].curadr = 0;
+ this->chips[ret].curmode = 0x50;
+ ret++;
+ }
+ }
+
+ /* 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 MiB\n",
+ this->numchips ,this->totlen >> 20);
+}
+
+static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
+{
+ int tmp1, tmp2, retval;
+
+ if (doc1->physadr == doc2->physadr)
+ return 1;
+
+ /* Use the alias resolution register which was set aside for this
+ * purpose. If it's value is the same on both chips, they might
+ * be the same chip, and we write to one and check for a change in
+ * the other. It's unclear if this register is usuable in the
+ * DoC 2000 (it's in the Millenium docs), but it seems to work. */
+ tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
+ tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
+ if (tmp1 != tmp2)
+ return 0;
+
+ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution);
+ tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
+ if (tmp2 == (tmp1+1) % 0xff)
+ retval = 1;
+ else
+ retval = 0;
+
+ /* Restore register contents. May not be necessary, but do it just to
+ * be safe. */
+ WriteDOC(tmp1, doc1->virtadr, AliasResolution);
+
+ return retval;
+}
+
+static const char im_name[] = "DoCMil_init";
+
+/* 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 <kaos@ocs.com.au> 29 Oct 2000.
+ */
+static void DoCMil_init(struct mtd_info *mtd)
+{
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ struct DiskOnChip *old = NULL;
+
+ /* We must avoid being called twice for the same device. */
+ if (docmillist)
+ old = (struct DiskOnChip *)docmillist->priv;
+
+ while (old) {
+ if (DoCMil_is_alias(this, old)) {
+ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at "
+ "0x%lX - already configured\n", this->physadr);
+ iounmap((void *)this->virtadr);
+ kfree(mtd);
+ return;
+ }
+ if (old->nextdoc)
+ old = (struct DiskOnChip *)old->nextdoc->priv;
+ else
+ old = NULL;
+ }
+
+ mtd->name = "DiskOnChip Millennium";
+ printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",
+ this->physadr);
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->size = 0;
+
+ /* FIXME: erase size is not always 8kB */
+ mtd->erasesize = 0x2000;
+
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+ mtd->module = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = doc_read;
+ mtd->write = doc_write;
+ mtd->read_ecc = doc_read_ecc;
+ mtd->write_ecc = doc_write_ecc;
+ mtd->read_oob = doc_read_oob;
+ mtd->write_oob = doc_write_oob;
+ mtd->sync = NULL;
+
+ this->totlen = 0;
+ this->numchips = 0;
+ this->curfloor = -1;
+ this->curchip = -1;
+
+ /* Ident all the chips present. */
+ DoC_ScanChips(this);
+
+ if (!this->totlen) {
+ kfree(mtd);
+ iounmap((void *)this->virtadr);
+ } else {
+ this->nextdoc = docmillist;
+ docmillist = mtd;
+ mtd->size = this->totlen;
+ add_mtd_device(mtd);
+ return;
+ }
+}
+
+static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ /* Just a special case of doc_read_ecc */
+ return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
+}
+
+static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf)
+{
+ int i, ret;
+ volatile char dummy;
+ unsigned char syndrome[6];
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[from >> (this->chipshift)];
+
+ /* Don't allow read past end of device */
+ if (from >= this->totlen)
+ return -EINVAL;
+
+ /* Don't allow a single read to cross a 512-byte block boundary */
+ if (from + len > ((from | 0x1ff) + 1))
+ len = ((from | 0x1ff) + 1) - from;
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(docptr, mychip->floor);
+ DoC_SelectChip(docptr, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(docptr, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* issue the Read0 or Read1 command depend on which half of the page
+ we are accessing. Polling the Flash Ready bit after issue 3 bytes
+ address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
+ DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
+ DoC_WaitReady(docptr);
+
+ if (eccbuf) {
+ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_EN, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+ }
+
+ /* Read the data via the internal pipeline through CDSN IO register,
+ see Pipelined Read Operations 11.3 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
+ for (i = 0; i < len-1; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
+ }
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
+
+ /* Let the caller know we completed it */
+ *retlen = len;
+ ret = 0;
+
+ if (eccbuf) {
+ /* Read the ECC data from Spare Data Area,
+ see Reed-Solomon EDC/ECC 11.1 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
+ for (i = 0; i < 5; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5);
+#endif
+ eccbuf[5] = ReadDOC(docptr, LastDataRead);
+
+ /* Flush the pipeline */
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+
+ /* Check the ECC Status */
+ if (ReadDOC(docptr, ECCConf) & 0x80) {
+ int nb_errors;
+ /* There was an ECC error */
+#ifdef ECC_DEBUG
+ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
+#ifdef ECC_DEBUG
+ printk("ECC Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
+ }
+
+#ifdef PSYCHO_DEBUG
+ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (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);
+ }
+
+ return ret;
+}
+
+static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
+}
+
+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,ret = 0;
+ volatile char dummy;
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[to >> (this->chipshift)];
+
+ /* Don't allow write past end of device */
+ if (to >= this->totlen)
+ return -EINVAL;
+
+#if 0
+ /* Don't allow a single write to cross a 512-byte block boundary */
+ if (to + len > ( (to | 0x1ff) + 1))
+ len = ((to | 0x1ff) + 1) - to;
+#else
+ /* Don't allow writes which aren't exactly one block */
+ if (to & 0x1ff || len != 0x200)
+ return -EINVAL;
+#endif
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(docptr, mychip->floor);
+ DoC_SelectChip(docptr, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(docptr, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(docptr, NAND_CMD_RESET, 0x00);
+ DoC_WaitReady(docptr);
+ /* Set device to main plane of flash */
+ DoC_Command(docptr, NAND_CMD_READ0, 0x00);
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+ DoC_Address(docptr, 3, to, 0x00, 0x00);
+ DoC_WaitReady(docptr);
+
+ if (eccbuf) {
+ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+ }
+
+ /* Write the data via the internal pipeline through CDSN IO register,
+ see Pipelined Write Operations 11.2 */
+#ifndef USE_MEMCPY
+ for (i = 0; i < len; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
+ WriteDOC(0x00, docptr, WritePipeTerm);
+
+ if (eccbuf) {
+ /* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic
+ see Reed-Solomon EDC/ECC 11.1 */
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ for (i = 0; i < 6; i++) {
+ eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+
+ /* ignore the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+
+#ifndef USE_MEMCPY
+ /* Write the ECC data to flash */
+ for (i = 0; i < 6; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6);
+#endif
+
+ /* write the block status BLOCK_USED (0x5555) at the end of ECC data
+ FIXME: this is only a hack for programming the IPL area for LinuxBIOS
+ and should be replace with proper codes in user space utilities */
+ WriteDOC(0x55, docptr, Mil_CDSN_IO);
+ WriteDOC(0x55, docptr, Mil_CDSN_IO + 1);
+
+ WriteDOC(0x00, docptr, WritePipeTerm);
+
+#ifdef PSYCHO_DEBUG
+ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
+#endif
+ }
+
+ /* Commit the Page Program command and wait for ready
+ see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
+ DoC_WaitReady(docptr);
+
+ /* 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, 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;
+ ret = -EIO;
+ }
+ dummy = ReadDOC(docptr, LastDataRead);
+
+ /* Let the caller know we completed it */
+ *retlen = len;
+
+ return ret;
+}
+
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf)
+{
+#ifndef USE_MEMCPY
+ int i;
+#endif
+ volatile char dummy;
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(docptr, mychip->floor);
+ DoC_SelectChip(docptr, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(docptr, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+
+ /* issue the Read2 command to set the pointer to the Spare Data Area.
+ Polling the Flash Ready bit after issue 3 bytes address in
+ Sequence Read Mode, see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0x00);
+ DoC_WaitReady(docptr);
+
+ /* Read the data out via the internal pipeline through CDSN IO register,
+ see Pipelined Read Operations 11.3 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
+ for (i = 0; i < len-1; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
+
+ *retlen = len;
+
+ return 0;
+}
+
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+#ifndef USE_MEMCPY
+ 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];
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(docptr, mychip->floor);
+ DoC_SelectChip(docptr, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(docptr, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_WaitReady(docptr);
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
+ DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+ DoC_Address(docptr, 3, ofs, 0x00, 0x00);
+
+ /* Write the data via the internal pipeline through CDSN IO register,
+ see Pipelined Write Operations 11.2 */
+#ifndef USE_MEMCPY
+ for (i = 0; i < len; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
+ WriteDOC(0x00, docptr, WritePipeTerm);
+
+ /* Commit the Page Program command and wait for ready
+ see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
+ DoC_WaitReady(docptr);
+
+ /* 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, 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;
+ ret = -EIO;
+ }
+ dummy = ReadDOC(docptr, LastDataRead);
+
+ *retlen = len;
+
+ return ret;
+}
+
+int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ volatile char dummy;
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ __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 (%x != %x)n",
+ len, mtd->erasesize);
+
+ /* Find the chip which is to be used and select it */
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(docptr, mychip->floor);
+ DoC_SelectChip(docptr, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(docptr, mychip->chip);
+ }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
+
+ instr->state = MTD_ERASE_PENDING;
+
+ /* issue the Erase Setup command */
+ DoC_Command(docptr, NAND_CMD_ERASE1, 0x00);
+ DoC_Address(docptr, 2, ofs, 0x00, 0x00);
+
+ /* Commit the Erase Start command and wait for ready
+ see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, NAND_CMD_ERASE2, 0x00);
+ DoC_WaitReady(docptr);
+
+ instr->state = MTD_ERASING;
+
+ /* 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, ReadPipeInit);
+ DoC_Delay(docptr, 2);
+ if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
+ 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);
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * Module stuff
+ *
+ ****************************************************************************/
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define cleanup_doc2001 cleanup_module
+#define init_doc2001 init_module
+#endif
+
+int __init init_doc2001(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
+ return 0;
+}
+
+static void __exit cleanup_doc2001(void)
+{
+ struct mtd_info *mtd;
+ struct DiskOnChip *this;
+
+ while ((mtd=docmillist)) {
+ this = (struct DiskOnChip *)mtd->priv;
+ docmillist = this->nextdoc;
+
+ del_mtd_device(mtd);
+
+ iounmap((void *)this->virtadr);
+ kfree(this->chips);
+ kfree(mtd);
+ }
+ inter_module_unregister(im_name);
+}
+
+module_exit(cleanup_doc2001);
+module_init(init_doc2001);
+
+
--- /dev/null
+/*
+ * ECC algorithm for M-systems disk on chip. We use the excellent Reed
+ * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
+ * GNU GPL License. The rest is simply to convert the disk on chip
+ * syndrom into a standard syndom.
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: docecc.c,v 1.1 2000/11/03 12:43:43 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/doc2000.h>
+
+/* need to undef it (from asm/termbits.h) */
+#undef B0
+
+#define MM 10 /* Symbol size in bits */
+#define KK (1023-4) /* Number of data symbols per block */
+#define B0 510 /* First root of generator polynomial, alpha form */
+#define PRIM 1 /* power of alpha used to generate roots of generator poly */
+#define NN ((1 << MM) - 1)
+
+typedef unsigned short dtype;
+
+/* 1+x^3+x^10 */
+static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
+
+/* This defines the type used to store an element of the Galois Field
+ * used by the code. Make sure this is something larger than a char if
+ * if anything larger than GF(256) is used.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium.
+ */
+typedef int gf;
+
+/* No legal value in index form represents zero, so
+ * we need a special value for this purpose
+ */
+#define A0 (NN)
+
+/* Compute x % NN, where NN is 2**MM - 1,
+ * without a slow divide
+ */
+static inline gf
+modnn(int x)
+{
+ while (x >= NN) {
+ x -= NN;
+ x = (x >> MM) + (x & NN);
+ }
+ return x;
+}
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define CLEAR(a,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = 0;\
+}
+
+#define COPY(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define COPYDOWN(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define Ldec 1
+
+/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
+ lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
+ polynomial form -> index form index_of[j=alpha**i] = i
+ alpha=2 is the primitive element of GF(2**m)
+ HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
+ Let @ represent the primitive element commonly called "alpha" that
+ is the root of the primitive polynomial p(x). Then in GF(2^m), for any
+ 0 <= i <= 2^m-2,
+ @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
+ of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
+ example the polynomial representation of @^5 would be given by the binary
+ representation of the integer "alpha_to[5]".
+ Similarily, index_of[] can be used as follows:
+ As above, let @ represent the primitive element of GF(2^m) that is
+ the root of the primitive polynomial p(x). In order to find the power
+ of @ (alpha) that has the polynomial representation
+ a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ we consider the integer "i" whose binary representation with a(0) being LSB
+ and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
+ "index_of[i]". Now, @^index_of[i] is that element whose polynomial
+ representation is (a(0),a(1),a(2),...,a(m-1)).
+ NOTE:
+ The element alpha_to[2^m-1] = 0 always signifying that the
+ representation of "@^infinity" = 0 is (0,0,0,...,0).
+ Similarily, the element index_of[0] = A0 always signifying
+ that the power of alpha which has the polynomial representation
+ (0,0,...,0) is "infinity".
+
+*/
+
+static void
+generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
+{
+ register int i, mask;
+
+ mask = 1;
+ Alpha_to[MM] = 0;
+ for (i = 0; i < MM; i++) {
+ Alpha_to[i] = mask;
+ Index_of[Alpha_to[i]] = i;
+ /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
+ if (Pp[i] != 0)
+ Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
+ mask <<= 1; /* single left-shift */
+ }
+ Index_of[Alpha_to[MM]] = MM;
+ /*
+ * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
+ * poly-repr of @^i shifted left one-bit and accounting for any @^MM
+ * term that may occur when poly-repr of @^i is shifted.
+ */
+ mask >>= 1;
+ for (i = MM + 1; i < NN; i++) {
+ if (Alpha_to[i - 1] >= mask)
+ Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
+ else
+ Alpha_to[i] = Alpha_to[i - 1] << 1;
+ Index_of[Alpha_to[i]] = i;
+ }
+ Index_of[0] = A0;
+ Alpha_to[NN] = 0;
+}
+
+/*
+ * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
+ * of the feedback shift register after having processed the data and
+ * the ECC.
+ *
+ * Return number of symbols corrected, or -1 if codeword is illegal
+ * or uncorrectable. If eras_pos is non-null, the detected error locations
+ * are written back. NOTE! This array must be at least NN-KK elements long.
+ * The corrected data are written in eras_val[]. They must be xor with the data
+ * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
+ *
+ * First "no_eras" erasures are declared by the calling program. Then, the
+ * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
+ * If the number of channel errors is not greater than "t_after_eras" the
+ * transmitted codeword will be recovered. Details of algorithm can be found
+ * in R. Blahut's "Theory ... of Error-Correcting Codes".
+
+ * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
+ * will result. The decoder *could* check for this condition, but it would involve
+ * extra time on every decoding operation.
+ * */
+static int
+eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
+ gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
+ int no_eras)
+{
+ int deg_lambda, el, deg_omega;
+ int i, j, r,k;
+ gf u,q,tmp,num1,num2,den,discr_r;
+ gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
+ * and syndrome poly */
+ gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
+ gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
+ int syn_error, count;
+
+ syn_error = 0;
+ for(i=0;i<NN-KK;i++)
+ syn_error |= bb[i];
+
+ if (!syn_error) {
+ /* if remainder is zero, data[] is a codeword and there are no
+ * errors to correct. So return data[] unmodified
+ */
+ count = 0;
+ goto finish;
+ }
+
+ for(i=1;i<=NN-KK;i++){
+ s[i] = bb[0];
+ }
+ for(j=1;j<NN-KK;j++){
+ if(bb[j] == 0)
+ continue;
+ tmp = Index_of[bb[j]];
+
+ for(i=1;i<=NN-KK;i++)
+ s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
+ }
+
+ /* undo the feedback register implicit multiplication and convert
+ syndromes to index form */
+
+ for(i=1;i<=NN-KK;i++) {
+ tmp = Index_of[s[i]];
+ if (tmp != A0)
+ tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
+ s[i] = tmp;
+ }
+
+ CLEAR(&lambda[1],NN-KK);
+ lambda[0] = 1;
+
+ if (no_eras > 0) {
+ /* Init lambda to be the erasure locator polynomial */
+ lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
+ for (i = 1; i < no_eras; i++) {
+ u = modnn(PRIM*eras_pos[i]);
+ for (j = i+1; j > 0; j--) {
+ tmp = Index_of[lambda[j - 1]];
+ if(tmp != A0)
+ lambda[j] ^= Alpha_to[modnn(u + tmp)];
+ }
+ }
+#if DEBUG >= 1
+ /* Test code that verifies the erasure locator polynomial just constructed
+ Needed only for decoder debugging. */
+
+ /* find roots of the erasure location polynomial */
+ for(i=1;i<=no_eras;i++)
+ reg[i] = Index_of[lambda[i]];
+ count = 0;
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = 1; j <= no_eras; j++)
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ if (q != 0)
+ continue;
+ /* store root and error location number indices */
+ root[count] = i;
+ loc[count] = k;
+ count++;
+ }
+ if (count != no_eras) {
+ printf("\n lambda(x) is WRONG\n");
+ count = -1;
+ goto finish;
+ }
+#if DEBUG >= 2
+ printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
+ for (i = 0; i < count; i++)
+ printf("%d ", loc[i]);
+ printf("\n");
+#endif
+#endif
+ }
+ for(i=0;i<NN-KK+1;i++)
+ b[i] = Index_of[lambda[i]];
+
+ /*
+ * Begin Berlekamp-Massey algorithm to determine error+erasure
+ * locator polynomial
+ */
+ r = no_eras;
+ el = no_eras;
+ while (++r <= NN-KK) { /* r is the step number */
+ /* Compute discrepancy at the r-th step in poly-form */
+ discr_r = 0;
+ for (i = 0; i < r; i++){
+ if ((lambda[i] != 0) && (s[r - i] != A0)) {
+ discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
+ }
+ }
+ discr_r = Index_of[discr_r]; /* Index form */
+ if (discr_r == A0) {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ } else {
+ /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+ t[0] = lambda[0];
+ for (i = 0 ; i < NN-KK; i++) {
+ if(b[i] != A0)
+ t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
+ else
+ t[i+1] = lambda[i+1];
+ }
+ if (2 * el <= r + no_eras - 1) {
+ el = r + no_eras - el;
+ /*
+ * 2 lines below: B(x) <-- inv(discr_r) *
+ * lambda(x)
+ */
+ for (i = 0; i <= NN-KK; i++)
+ b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
+ } else {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ }
+ COPY(lambda,t,NN-KK+1);
+ }
+ }
+
+ /* Convert lambda to index form and compute deg(lambda(x)) */
+ deg_lambda = 0;
+ for(i=0;i<NN-KK+1;i++){
+ lambda[i] = Index_of[lambda[i]];
+ if(lambda[i] != A0)
+ deg_lambda = i;
+ }
+ /*
+ * Find roots of the error+erasure locator polynomial by Chien
+ * Search
+ */
+ COPY(®[1],&lambda[1],NN-KK);
+ count = 0; /* Number of roots of lambda(x) */
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = deg_lambda; j > 0; j--){
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ }
+ if (q != 0)
+ continue;
+ /* store root (index-form) and error location number */
+ root[count] = i;
+ loc[count] = k;
+ /* If we've already found max possible roots,
+ * abort the search to save time
+ */
+ if(++count == deg_lambda)
+ break;
+ }
+ if (deg_lambda != count) {
+ /*
+ * deg(lambda) unequal to number of roots => uncorrectable
+ * error detected
+ */
+ count = -1;
+ goto finish;
+ }
+ /*
+ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+ * x**(NN-KK)). in index form. Also find deg(omega).
+ */
+ deg_omega = 0;
+ for (i = 0; i < NN-KK;i++){
+ tmp = 0;
+ j = (deg_lambda < i) ? deg_lambda : i;
+ for(;j >= 0; j--){
+ if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
+ tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
+ }
+ if(tmp != 0)
+ deg_omega = i;
+ omega[i] = Index_of[tmp];
+ }
+ omega[NN-KK] = A0;
+
+ /*
+ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+ * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
+ */
+ for (j = count-1; j >=0; j--) {
+ num1 = 0;
+ for (i = deg_omega; i >= 0; i--) {
+ if (omega[i] != A0)
+ num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
+ }
+ num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
+ den = 0;
+
+ /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+ for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
+ if(lambda[i+1] != A0)
+ den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
+ }
+ if (den == 0) {
+#if DEBUG >= 1
+ printf("\n ERROR: denominator = 0\n");
+#endif
+ /* Convert to dual- basis */
+ count = -1;
+ goto finish;
+ }
+ /* Apply error to data */
+ if (num1 != 0) {
+ eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
+ } else {
+ eras_val[j] = 0;
+ }
+ }
+ finish:
+ for(i=0;i<count;i++)
+ eras_pos[i] = loc[i];
+ return count;
+}
+
+/***************************************************************************/
+/* The DOC specific code begins here */
+
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA MM bits words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
+
+/*
+ * Correct the errors in 'sector[]' by using 'ecc1[]' which is the
+ * content of the feedback shift register applyied to the sector and
+ * the ECC. Return the number of errors corrected (and correct them in
+ * sector), or -1 if error
+ */
+int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
+{
+ int parity, i, nb_errors;
+ gf bb[NN - KK + 1];
+ gf error_val[NN-KK];
+ int error_pos[NN-KK], pos, bitpos, index, val;
+ dtype *Alpha_to, *Index_of;
+
+ /* init log and exp tables here to save memory. However, it is slower */
+ Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Alpha_to)
+ return -1;
+
+ Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Index_of) {
+ kfree(Alpha_to);
+ return -1;
+ }
+
+ generate_gf(Alpha_to, Index_of);
+
+ parity = ecc1[1];
+
+ bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
+ bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
+ bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
+ bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
+
+ nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
+ error_val, error_pos, 0);
+ if (nb_errors <= 0)
+ goto the_end;
+
+ /* correct the errors */
+ for(i=0;i<nb_errors;i++) {
+ pos = error_pos[i];
+ if (pos >= NB_DATA && pos < KK) {
+ nb_errors = -1;
+ goto the_end;
+ }
+ if (pos < NB_DATA) {
+ /* extract bit position (MSB first) */
+ pos = 10 * (NB_DATA - 1 - pos) - 6;
+ /* now correct the following 10 bits. At most two bytes
+ can be modified since pos is even */
+ index = (pos >> 3) ^ 1;
+ bitpos = pos & 7;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] >> (2 + bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ index = ((pos >> 3) + 1) ^ 1;
+ bitpos = (bitpos + 10) & 7;
+ if (bitpos == 0)
+ bitpos = 8;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] << (8 - bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ }
+ }
+
+ /* use parity to test extra errors */
+ if ((parity & 0xff) != 0)
+ nb_errors = -1;
+
+ the_end:
+ kfree(Alpha_to);
+ kfree(Index_of);
+ return nb_errors;
+}
+
--- /dev/null
+
+/* Linux driver for Disk-On-Chip devices */
+/* Probe routines common to all DoC devices */
+/* (c) 1999 Machine Vision Holdings, Inc. */
+/* Author: David Woodhouse <dwmw2@infradead.org> */
+/* $Id: docprobe.c,v 1.27 2001/06/03 19:06:09 dwmw2 Exp $ */
+
+
+
+/* DOC_PASSIVE_PROBE:
+ In order to ensure that the BIOS checksum is correct at boot time, and
+ hence that the onboard BIOS extension gets executed, the DiskOnChip
+ goes into reset mode when it is read sequentially: all registers
+ return 0xff until the chip is woken up again by writing to the
+ DOCControl register.
+
+ Unfortunately, this means that the probe for the DiskOnChip is unsafe,
+ because one of the first things it does is write to where it thinks
+ the DOCControl register should be - which may well be shared memory
+ for another device. I've had machines which lock up when this is
+ attempted. Hence the possibility to do a passive probe, which will fail
+ to detect a chip in reset mode, but is at least guaranteed not to lock
+ the machine.
+
+ If you have this problem, uncomment the following line:
+#define DOC_PASSIVE_PROBE
+*/
+
+
+/* DOC_SINGLE_DRIVER:
+ Millennium driver has been merged into DOC2000 driver.
+
+ The newly-merged driver doesn't appear to work for writing. It's the
+ same with the DiskOnChip 2000 and the Millennium. If you have a
+ Millennium and you want write support to work, remove the definition
+ of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
+
+ Otherwise, it's left on in the hope that it'll annoy someone with
+ a Millennium enough that they go through and work out what the
+ difference is :)
+*/
+#define DOC_SINGLE_DRIVER
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/doc2000.h>
+
+/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#endif
+
+
+static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+
+
+static unsigned long __initdata doc_locations[] = {
+#if defined (__alpha__) || defined(__i386__)
+#ifdef CONFIG_MTD_DOCPROBE_HIGH
+ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /* CONFIG_MTD_DOCPROBE_HIGH */
+ 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000,
+ 0xd8000, 0xda000, 0xdc000, 0xde000,
+ 0xe0000, 0xe2000, 0xe4000, 0xe6000,
+ 0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /* CONFIG_MTD_DOCPROBE_HIGH */
+#elif defined(__ppc__)
+ 0xe4000000,
+#elif defined(CONFIG_MOMENCO_OCELOT)
+ 0x2f000000,
+#else
+#warning Unknown architecture for DiskOnChip. No default probe locations defined
+#endif
+ 0 };
+
+/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
+
+static inline int __init doccheck(unsigned long potential, unsigned long physadr)
+{
+ unsigned long window=potential;
+ unsigned char tmp, ChipID;
+#ifndef DOC_PASSIVE_PROBE
+ unsigned char tmp2;
+#endif
+
+ /* Routine copied from the Linux DOC driver */
+
+#ifdef CONFIG_MTD_DOCPROBE_55AA
+ /* Check for 0x55 0xAA signature at beginning of window,
+ this is no longer true once we remove the IPL (for Millennium */
+ if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
+ return 0;
+#endif /* CONFIG_MTD_DOCPROBE_55AA */
+
+#ifndef DOC_PASSIVE_PROBE
+ /* It's not possible to cleanly detect the DiskOnChip - the
+ * bootup procedure will put the device into reset mode, and
+ * it's not possible to talk to it without actually writing
+ * to the DOCControl register. So we store the current contents
+ * of the DOCControl register's location, in case we later decide
+ * that it's not a DiskOnChip, and want to put it back how we
+ * found it.
+ */
+ tmp2 = ReadDOC(window, DOCControl);
+
+ /* Reset the DiskOnChip ASIC */
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
+ window, DOCControl);
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
+ window, DOCControl);
+
+ /* Enable the DiskOnChip ASIC */
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
+ window, DOCControl);
+ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
+ window, DOCControl);
+#endif /* !DOC_PASSIVE_PROBE */
+
+ ChipID = ReadDOC(window, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_Doc2k:
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+ if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp)
+ return ChipID;
+ break;
+
+ case DOC_ChipID_DocMil:
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+ if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp)
+ return ChipID;
+ break;
+
+ default:
+#ifndef CONFIG_MTD_DOCPROBE_55AA
+ printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+ ChipID, physadr);
+#endif
+#ifndef DOC_PASSIVE_PROBE
+ /* Put back the contents of the DOCControl register, in case it's not
+ * actually a DiskOnChip.
+ */
+ WriteDOC(tmp2, window, DOCControl);
+#endif
+ return 0;
+ }
+
+ printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n");
+
+#ifndef DOC_PASSIVE_PROBE
+ /* Put back the contents of the DOCControl register: it's not a DiskOnChip */
+ WriteDOC(tmp2, window, DOCControl);
+#endif
+ return 0;
+}
+
+
+static void __init DoC_Probe(unsigned long physadr)
+{
+ unsigned long docptr;
+ struct DiskOnChip *this;
+ struct mtd_info *mtd;
+ int ChipID;
+ char namebuf[15];
+ char *name = namebuf;
+ char *im_funcname = NULL;
+ char *im_modname = NULL;
+ void (*initroutine)(struct mtd_info *) = NULL;
+
+ docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
+
+ if (!docptr)
+ return;
+
+ if ((ChipID = doccheck(docptr, physadr))) {
+
+ mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
+
+ if (!mtd) {
+ printk("Cannot allocate memory for data structures. Dropping.\n");
+ iounmap((void *)docptr);
+ return;
+ }
+
+ this = (struct DiskOnChip *)(&mtd[1]);
+
+ memset((char *)mtd,0, sizeof(struct mtd_info));
+ memset((char *)this, 0, sizeof(struct DiskOnChip));
+
+ mtd->priv = this;
+ this->virtadr = docptr;
+ this->physadr = physadr;
+ this->ChipID = ChipID;
+ sprintf(namebuf, "with ChipID %2.2X", ChipID);
+
+ switch(ChipID) {
+ case DOC_ChipID_Doc2k:
+ name="2000";
+ im_funcname = "DoC2k_init";
+ im_modname = "doc2000";
+ break;
+
+ case DOC_ChipID_DocMil:
+ name="Millennium";
+#ifdef DOC_SINGLE_DRIVER
+ im_funcname = "DoC2k_init";
+ im_modname = "doc2000";
+#else
+ im_funcname = "DoCMil_init";
+ im_modname = "doc2001";
+#endif /* DOC_SINGLE_DRIVER */
+ break;
+ }
+
+ if (im_funcname)
+ initroutine = inter_module_get_request(im_funcname, im_modname);
+
+ if (initroutine) {
+ (*initroutine)(mtd);
+ inter_module_put(im_funcname);
+ return;
+ }
+ printk("Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
+ }
+ iounmap((void *)docptr);
+}
+
+
+/****************************************************************************
+ *
+ * Module stuff
+ *
+ ****************************************************************************/
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_doc init_module
+#endif
+
+int __init init_doc(void)
+{
+ int i;
+
+ printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n");
+#ifdef PRERELEASE
+ 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);
+ DoC_Probe(doc_config_location);
+ } else {
+ for (i=0; doc_locations[i]; i++) {
+ DoC_Probe(doc_locations[i]);
+ }
+ }
+ /* So it looks like we've been used and we get unloaded */
+ MOD_INC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
+ return 0;
+
+}
+
+module_init(init_doc);
+
--- /dev/null
+/*
+ * mtdram - a test mtd device
+ * $Id: mtdram.c,v 1.24 2001/06/09 23:09:23 dwmw2 Exp $
+ * Author: Alexander Larsson <alex@cendio.se>
+ *
+ * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
+ *
+ * This code is GPL
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/mtd.h>
+
+#ifndef CONFIG_MTDRAM_ABS_POS
+ #define CONFIG_MTDRAM_ABS_POS 0
+#endif
+
+#if CONFIG_MTDRAM_ABS_POS > 0
+ #include <asm/io.h>
+#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..
+static struct mtd_info *mtd_info;
+
+
+static int
+ram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ 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)
+ (*(instr->callback))(instr);
+ return 0;
+}
+
+static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
+{
+ if (from + len > mtd->size)
+ return -EINVAL;
+
+ *mtdbuf = mtd->priv + from;
+ *retlen = len;
+ return 0;
+}
+
+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)
+{
+ 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);
+
+ *retlen=len;
+ return 0;
+}
+
+static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ 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 < 0x20212 && defined(MODULE)
+#define init_mtdram init_module
+#define cleanup_mtdram cleanup_module
+#endif
+
+//static void __exit cleanup_mtdram(void)
+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);
+ }
+}
+
+mod_init_t init_mtdram(void)
+{
+ // Allocate some memory
+ mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd_info)
+ return 0;
+
+ memset(mtd_info, 0, sizeof(*mtd_info));
+
+ // Setup the MTD structure
+ mtd_info->name = "mtdram test device";
+ mtd_info->type = MTD_RAM;
+ 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);
+#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;
+ mtd_info->read = ram_read;
+ 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;
+ }
+
+ return 0;
+}
+
+module_init(init_mtdram);
+module_exit(cleanup_mtdram);
--- /dev/null
+/*
+ * $Id: pmc551.c,v 1.17 2001/05/22 13:56:46 dwmw2 Exp $
+ *
+ * PMC551 PCI Mezzanine Ram Device
+ *
+ * Author:
+ * Mark Ferrell <mferrell@mvista.com>
+ * Copyright 1999,2000 Nortel Networks
+ *
+ * License:
+ * 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 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 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 <saeed@ramix.com> 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 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 <minyard@nortelnetworks.com>
+ * * 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 being the only chance at
+ * recover.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include <linux/pci.h>
+
+#ifndef CONFIG_PCI
+#error Enable PCI in your kernel config
+#endif
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/pmc551.h>
+#include <linux/mtd/compatmac.h>
+
+#if LINUX_VERSION_CODE > 0x20300
+#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
+#else
+#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
+#endif
+
+static struct mtd_info *pmc551list;
+
+static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct mypriv *priv = mtd->priv;
+ u32 start_addr_highbits;
+ u32 end_addr_highbits;
+ u32 start_addr_lowbits;
+ u32 end_addr_lowbits;
+ unsigned long end;
+
+ end = instr->addr + instr->len;
+
+ /* Is it too much memory? The second check find if we wrap around
+ past the end of a u32. */
+ if ((end > mtd->size) || (end < instr->addr)) {
+ return -EINVAL;
+ }
+
+ start_addr_highbits = instr->addr & PMC551_ADDR_HIGH_MASK;
+ end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
+ start_addr_lowbits = instr->addr & PMC551_ADDR_LOW_MASK;
+ end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
+
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ (priv->mem_map0_base_val
+ | start_addr_highbits));
+ if (start_addr_highbits == end_addr_highbits) {
+ /* The whole thing fits within one access, so just one shot
+ will do it. */
+ memset(priv->start + start_addr_lowbits,
+ 0xff,
+ instr->len);
+ } else {
+ /* We have to do multiple writes to get all the data
+ written. */
+ memset(priv->start + start_addr_lowbits,
+ 0xff,
+ priv->aperture_size - start_addr_lowbits);
+ start_addr_highbits += priv->aperture_size;
+ while (start_addr_highbits != end_addr_highbits) {
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ (priv->mem_map0_base_val
+ | start_addr_highbits));
+ memset(priv->start,
+ 0xff,
+ priv->aperture_size);
+ start_addr_highbits += priv->aperture_size;
+ }
+ priv->curr_mem_map0_val = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+ memset(priv->start,
+ 0xff,
+ end_addr_lowbits);
+ }
+
+ instr->state = MTD_ERASE_DONE;
+
+ if (instr->callback) {
+ (*(instr->callback))(instr);
+ }
+
+ return 0;
+}
+
+
+static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr)
+{}
+
+
+static int pmc551_read (struct mtd_info *mtd,
+ loff_t from,
+ size_t len,
+ size_t *retlen,
+ u_char *buf)
+{
+ struct mypriv *priv = (struct mypriv *)mtd->priv;
+ u32 start_addr_highbits;
+ u32 end_addr_highbits;
+ u32 start_addr_lowbits;
+ u32 end_addr_lowbits;
+ unsigned long end;
+ u_char *copyto = buf;
+
+
+ /* Is it past the end? */
+ if (from > mtd->size) {
+ return -EINVAL;
+ }
+
+ end = from + len;
+ start_addr_highbits = from & PMC551_ADDR_HIGH_MASK;
+ end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
+ start_addr_lowbits = from & PMC551_ADDR_LOW_MASK;
+ end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
+
+
+ /* Only rewrite the first value if it doesn't match our current
+ values. Most operations are on the same page as the previous
+ value, so this is a pretty good optimization. */
+ if (priv->curr_mem_map0_val !=
+ (priv->mem_map0_base_val | start_addr_highbits)) {
+ priv->curr_mem_map0_val = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+ }
+
+ if (start_addr_highbits == end_addr_highbits) {
+ /* The whole thing fits within one access, so just one shot
+ will do it. */
+ memcpy(copyto,
+ priv->start + start_addr_lowbits,
+ len);
+ copyto += len;
+ } else {
+ /* We have to do multiple writes to get all the data
+ written. */
+ memcpy(copyto,
+ priv->start + start_addr_lowbits,
+ priv->aperture_size - start_addr_lowbits);
+ copyto += priv->aperture_size - start_addr_lowbits;
+ start_addr_highbits += priv->aperture_size;
+ while (start_addr_highbits != end_addr_highbits) {
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ (priv->mem_map0_base_val
+ | start_addr_highbits));
+ memcpy(copyto,
+ priv->start,
+ priv->aperture_size);
+ copyto += priv->aperture_size;
+ start_addr_highbits += priv->aperture_size;
+ if (start_addr_highbits >= mtd->size) {
+ /* Make sure we have the right value here. */
+ priv->curr_mem_map0_val
+ = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ goto out;
+ }
+ }
+ priv->curr_mem_map0_val = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+ memcpy(copyto,
+ priv->start,
+ end_addr_lowbits);
+ copyto += end_addr_lowbits;
+ }
+
+out:
+ *retlen = copyto - buf;
+ return 0;
+}
+
+static int pmc551_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;
+ u32 start_addr_highbits;
+ u32 end_addr_highbits;
+ u32 start_addr_lowbits;
+ u32 end_addr_lowbits;
+ unsigned long end;
+ const u_char *copyfrom = buf;
+
+
+ /* Is it past the end? */
+ if (to > mtd->size) {
+ return -EINVAL;
+ }
+
+ end = to + len;
+ start_addr_highbits = to & PMC551_ADDR_HIGH_MASK;
+ end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
+ start_addr_lowbits = to & PMC551_ADDR_LOW_MASK;
+ end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
+
+
+ /* Only rewrite the first value if it doesn't match our current
+ values. Most operations are on the same page as the previous
+ value, so this is a pretty good optimization. */
+ if (priv->curr_mem_map0_val !=
+ (priv->mem_map0_base_val | start_addr_highbits)) {
+ priv->curr_mem_map0_val = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+ }
+
+ if (start_addr_highbits == end_addr_highbits) {
+ /* The whole thing fits within one access, so just one shot
+ will do it. */
+ memcpy(priv->start + start_addr_lowbits,
+ copyfrom,
+ len);
+ copyfrom += len;
+ } else {
+ /* We have to do multiple writes to get all the data
+ written. */
+ memcpy(priv->start + start_addr_lowbits,
+ copyfrom,
+ priv->aperture_size - start_addr_lowbits);
+ copyfrom += priv->aperture_size - start_addr_lowbits;
+ start_addr_highbits += priv->aperture_size;
+ while (start_addr_highbits != end_addr_highbits) {
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ (priv->mem_map0_base_val
+ | start_addr_highbits));
+ memcpy(priv->start,
+ copyfrom,
+ priv->aperture_size);
+ copyfrom += priv->aperture_size;
+ start_addr_highbits += priv->aperture_size;
+ if (start_addr_highbits >= mtd->size) {
+ /* Make sure we have the right value here. */
+ priv->curr_mem_map0_val
+ = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ goto out;
+ }
+ }
+ priv->curr_mem_map0_val = (priv->mem_map0_base_val
+ | start_addr_highbits);
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+ memcpy(priv->start,
+ copyfrom,
+ end_addr_lowbits);
+ copyfrom += end_addr_lowbits;
+ }
+
+out:
+ *retlen = copyfrom - buf;
+ return 0;
+}
+
+/*
+ * Fixup routines for the V370PDC
+ * PCI device ID 0x020011b0
+ *
+ * This function basicly kick starts the DRAM oboard the card and gets it
+ * ready to be used. Before this is done the device reads VERY erratic, so
+ * much that it can crash the Linux 2.2.x series kernels when a user cat's
+ * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL
+ * register. FIXME: stop spinning on registers .. must implement a timeout
+ * mechanism
+ * returns the size of the memory region found.
+ */
+static u32 fixup_pmc551 (struct pci_dev *dev)
+{
+#ifdef CONFIG_MTD_PMC551_BUGFIX
+ u32 dram_data;
+#endif
+ u32 size, dcmd, cfg, dtmp;
+ u16 cmd, tmp, i;
+ u8 bcmd, counter;
+
+ /* Sanity Check */
+ if(!dev) {
+ return -ENODEV;
+ }
+
+ /*
+ * Attempt to reset the card
+ * FIXME: Stop Spinning registers
+ */
+ counter=0;
+ /* unlock registers */
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5 );
+ /* read in old data */
+ pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
+ /* bang the reset line up and down for a few */
+ for(i=0;i<10;i++) {
+ counter=0;
+ bcmd &= ~0x80;
+ while(counter++ < 100) {
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+ }
+ counter=0;
+ bcmd |= 0x80;
+ while(counter++ < 100) {
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+ }
+ }
+ bcmd |= (0x40|0x20);
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+
+ /*
+ * Take care and turn off the memory on the device while we
+ * tweak the configurations
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ tmp = cmd & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY);
+ pci_write_config_word(dev, PCI_COMMAND, tmp);
+
+ /*
+ * Disable existing aperture before probing memory size
+ */
+ pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd);
+ dtmp=(dcmd|PMC551_PCI_MEM_MAP_ENABLE|PMC551_PCI_MEM_MAP_REG_EN);
+ pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp);
+ /*
+ * Grab old BAR0 config so that we can figure out memory size
+ * This is another bit of kludge going on. The reason for the
+ * redundancy is I am hoping to retain the original configuration
+ * previously assigned to the card by the BIOS or some previous
+ * fixup routine in the kernel. So we read the old config into cfg,
+ * then write all 1's to the memory space, read back the result into
+ * "size", and then write back all the old config.
+ */
+ pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &cfg );
+#ifndef CONFIG_MTD_PMC551_BUGFIX
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, ~0 );
+ pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &size );
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
+ size=~(size&PCI_BASE_ADDRESS_MEM_MASK)+1;
+#else
+ /*
+ * Get the size of the memory by reading all the DRAM size values
+ * and adding them up.
+ *
+ * KLUDGE ALERT: the boards we are using have invalid column and
+ * row mux values. We fix them here, but this will break other
+ * memory configurations.
+ */
+ pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data);
+ size = PMC551_DRAM_BLK_GET_SIZE(dram_data);
+ dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
+ dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
+ pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data);
+
+ pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data);
+ size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
+ dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
+ dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
+ pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data);
+
+ pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data);
+ size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
+ dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
+ dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
+ pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data);
+
+ pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data);
+ size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
+ dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
+ dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
+ pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data);
+
+ /*
+ * Oops .. something went wrong
+ */
+ if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) {
+ return -ENODEV;
+ }
+#endif /* CONFIG_MTD_PMC551_BUGFIX */
+
+ if ((cfg&PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ return -ENODEV;
+ }
+
+ /*
+ * Precharge Dram
+ */
+ pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 );
+ pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf );
+
+ /*
+ * Wait until command has gone through
+ * FIXME: register spinning issue
+ */
+ do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd );
+ if(counter++ > 100)break;
+ } while ( (PCI_COMMAND_IO) & cmd );
+
+ /*
+ * Turn on auto refresh
+ * The loop is taken directly from Ramix's example code. I assume that
+ * this must be held high for some duration of time, but I can find no
+ * documentation refrencing the reasons why.
+ *
+ */
+ for ( i = 1; i<=8 ; i++) {
+ pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df);
+
+ /*
+ * Make certain command has gone through
+ * FIXME: register spinning issue
+ */
+ counter=0;
+ do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
+ if(counter++ > 100)break;
+ } while ( (PCI_COMMAND_IO) & cmd );
+ }
+
+ pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020);
+ pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff);
+
+ /*
+ * Wait until command completes
+ * FIXME: register spinning issue
+ */
+ counter=0;
+ do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd);
+ if(counter++ > 100)break;
+ } while ( (PCI_COMMAND_IO) & cmd );
+
+ pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd);
+ dcmd |= 0x02000000;
+ pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd);
+
+ /*
+ * Check to make certain fast back-to-back, if not
+ * then set it so
+ */
+ pci_read_config_word( dev, PCI_STATUS, &cmd);
+ if((cmd&PCI_COMMAND_FAST_BACK) == 0) {
+ cmd |= PCI_COMMAND_FAST_BACK;
+ pci_write_config_word( dev, PCI_STATUS, cmd);
+ }
+
+ /*
+ * Check to make certain the DEVSEL is set correctly, this device
+ * has a tendancy to assert DEVSEL and TRDY when a write is performed
+ * to the memory when memory is read-only
+ */
+ if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) {
+ cmd &= ~PCI_STATUS_DEVSEL_MASK;
+ pci_write_config_word( dev, PCI_STATUS, cmd );
+ }
+ /*
+ * Set to be prefetchable and put everything back based on old cfg.
+ * it's possible that the reset of the V370PDC nuked the original
+ * setup
+ */
+ cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
+
+ /*
+ * Turn PCI memory and I/O bus access back on
+ */
+ pci_write_config_word( dev, PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO );
+#ifdef CONFIG_MTD_PMC551_DEBUG
+ /*
+ * Some screen fun
+ */
+ printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%lx\n",
+ (size<1024)?size:(size<1048576)?size/1024:size/1024/1024,
+ (size<1024)?'B':(size<1048576)?'K':'M',
+ size, ((dcmd&(0x1<<3)) == 0)?"non-":"",
+ PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
+
+ /*
+ * Check to see the state of the memory
+ */
+ pci_read_config_dword( dev, PMC551_DRAM_BLK0, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK0 Size: %d at %d\n"
+ "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK1, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK1 Size: %d at %d\n"
+ "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK2, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK2 Size: %d at %d\n"
+ "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK3, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK3 Size: %d at %d\n"
+ "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_word( dev, PCI_COMMAND, &cmd );
+ printk( KERN_DEBUG "pmc551: Memory Access %s\n",
+ (((0x1<<1)&cmd) == 0)?"off":"on" );
+ printk( KERN_DEBUG "pmc551: I/O Access %s\n",
+ (((0x1<<0)&cmd) == 0)?"off":"on" );
+
+ pci_read_config_word( dev, PCI_STATUS, &cmd );
+ printk( KERN_DEBUG "pmc551: Devsel %s\n",
+ ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast":
+ ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium":
+ ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" );
+
+ printk( KERN_DEBUG "pmc551: %sFast Back-to-Back\n",
+ ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" );
+
+ pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
+ printk( KERN_DEBUG "pmc551: EEPROM is under %s control\n"
+ "pmc551: System Control Register is %slocked to PCI access\n"
+ "pmc551: System Control Register is %slocked to EEPROM access\n",
+ (bcmd&0x1)?"software":"hardware",
+ (bcmd&0x20)?"":"un", (bcmd&0x40)?"":"un");
+#endif
+ return size;
+}
+
+/*
+ * Kernel version specific module stuffages
+ */
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_pmc551 init_module
+#define cleanup_pmc551 cleanup_module
+#endif
+
+#if defined(MODULE)
+MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>");
+MODULE_DESCRIPTION(PMC551_VERSION);
+MODULE_PARM(msize, "i");
+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
+/*
+ * Stuff these outside the ifdef so as to not bust compiled in driver support
+ */
+static int msize=0;
+#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
+static int asize=CONFIG_MTD_PMC551_APERTURE_SIZE
+#else
+static int asize=0;
+#endif
+
+/*
+ * PMC551 Card Initialization
+ */
+int __init init_pmc551(void)
+{
+ struct pci_dev *PCI_Device = NULL;
+ struct mypriv *priv;
+ int count, found=0;
+ struct mtd_info *mtd;
+ u32 length = 0;
+
+ if(msize) {
+ if (msize < 6 || msize > 11 ) {
+ printk(KERN_NOTICE "pmc551: Invalid memory size\n");
+ return -ENODEV;
+ }
+ msize = (512*1024)<<msize;
+ }
+
+ if(asize) {
+ if (asize < 1 || asize > 11 ) {
+ printk(KERN_NOTICE "pmc551: Invalid aperture size\n");
+ return -ENODEV;
+ }
+ asize = (512*1024)<<asize;
+ }
+
+ printk(KERN_INFO PMC551_VERSION);
+
+ if(!pci_present()) {
+ printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
+ return -ENODEV;
+ }
+
+ /*
+ * PCU-bus chipset probe.
+ */
+ for( count = 0; count < MAX_MTD_DEVICES; count++ ) {
+
+ if ( (PCI_Device = pci_find_device( PCI_VENDOR_ID_V3_SEMI,
+ PCI_DEVICE_ID_V3_SEMI_V370PDC, PCI_Device ) ) == NULL) {
+ break;
+ }
+
+ printk(KERN_NOTICE "pmc551: Found PCI V370PDC IRQ:%d\n",
+ PCI_Device->irq);
+
+ /*
+ * The PMC551 device acts VERY weird if you don't init it
+ * first. i.e. it will not correctly report devsel. If for
+ * some reason the sdram is in a wrote-protected state the
+ * device will DEVSEL when it is written to causing problems
+ * with the oldproc.c driver in
+ * some kernels (2.2.*)
+ */
+ if((length = fixup_pmc551(PCI_Device)) <= 0) {
+ printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n");
+ break;
+ }
+ if(msize) {
+ length = msize;
+ printk(KERN_NOTICE "pmc551: Using specified memory size 0x%x\n", length);
+ }
+
+ mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd) {
+ printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
+ break;
+ }
+
+ memset(mtd, 0, sizeof(struct mtd_info));
+
+ priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL);
+ if (!priv) {
+ printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
+ kfree(mtd);
+ break;
+ }
+ memset(priv, 0, sizeof(*priv));
+ mtd->priv = priv;
+
+ priv->dev = PCI_Device;
+ if(asize) {
+ if(asize > length) {
+ asize=length;
+ printk(KERN_NOTICE "pmc551: reducing aperture size to fit memory [0x%x]\n",asize);
+ } else {
+ printk(KERN_NOTICE "pmc551: Using specified aperture size 0x%x\n", asize);
+ }
+ priv->aperture_size = asize;
+ } else {
+ priv->aperture_size = length;
+ }
+ priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device)
+ & 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
+ * aperture size. If you know of an easier way to do this then
+ * PLEASE help yourself.
+ *
+ * Not with bloody floating point, you don't. Consider yourself
+ * duly LARTed. dwmw2.
+ */
+ {
+ u32 size;
+ u16 bits;
+ size = priv->aperture_size>>20;
+ for(bits=0;!(size&0x01)&&size>0;bits++,size=size>>1);
+ //size=((u32)((log10(priv->aperture_size)/.30103)-19)<<4);
+ priv->mem_map0_base_val = (PMC551_PCI_MEM_MAP_REG_EN
+ | PMC551_PCI_MEM_MAP_ENABLE
+ | size);
+#ifdef CONFIG_MTD_PMC551_DEBUG
+ printk(KERN_NOTICE "pmc551: aperture set to %d[%d]\n",
+ size, size>>4);
+#endif
+ }
+ priv->curr_mem_map0_val = priv->mem_map0_base_val;
+
+ pci_write_config_dword ( priv->dev,
+ PMC551_PCI_MEM_MAP0,
+ priv->curr_mem_map0_val);
+
+ mtd->size = length;
+ mtd->flags = (MTD_CLEAR_BITS
+ | MTD_SET_BITS
+ | MTD_WRITEB_WRITEABLE
+ | MTD_VOLATILE);
+ mtd->erase = pmc551_erase;
+ mtd->point = NULL;
+ mtd->unpoint = pmc551_unpoint;
+ mtd->read = pmc551_read;
+ mtd->write = pmc551_write;
+ mtd->module = THIS_MODULE;
+ mtd->type = MTD_RAM;
+ mtd->name = "PMC551 RAM board";
+ mtd->erasesize = 0x10000;
+
+ if (add_mtd_device(mtd)) {
+ printk(KERN_NOTICE "pmc551: Failed to register new device\n");
+ iounmap(priv->start);
+ kfree(mtd->priv);
+ kfree(mtd);
+ break;
+ }
+ printk(KERN_NOTICE "Registered pmc551 memory device.\n");
+ printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
+ priv->aperture_size/1024/1024,
+ priv->start,
+ priv->start + priv->aperture_size);
+ printk(KERN_NOTICE "Total memory is %d%c\n",
+ (length<1024)?length:
+ (length<1048576)?length/1024:length/1024/1024,
+ (length<1024)?'B':(length<1048576)?'K':'M');
+ priv->nextpmc551 = pmc551list;
+ pmc551list = mtd;
+ found++;
+ }
+
+ if( !pmc551list ) {
+ printk(KERN_NOTICE "pmc551: not detected,\n");
+ return -ENODEV;
+ } else {
+ printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found);
+ return 0;
+ }
+}
+
+/*
+ * PMC551 Card Cleanup
+ */
+static void __exit cleanup_pmc551(void)
+{
+ int found=0;
+ struct mtd_info *mtd;
+ struct mypriv *priv;
+
+ while((mtd=pmc551list)) {
+ priv = (struct mypriv *)mtd->priv;
+ pmc551list = priv->nextpmc551;
+
+ if(priv->start)
+ iounmap(priv->start);
+
+ kfree (mtd->priv);
+ del_mtd_device(mtd);
+ kfree(mtd);
+ found++;
+ }
+
+ printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found);
+}
+
+module_init(init_pmc551);
+module_exit(cleanup_pmc551);
--- /dev/null
+/*======================================================================
+
+ $Id: slram.c,v 1.19 2001/06/02 20:33:20 dwmw2 Exp $
+
+======================================================================*/
+
+
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+
+#include <linux/mtd/mtd.h>
+
+#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=<name>, <start>, <length / end>\"");
+#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);
+++ /dev/null
-/*======================================================================
-
- $Id: doc1000.c,v 1.11 2000/11/24 13:43:16 dwmw2 Exp $
-
-======================================================================*/
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/major.h>
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <stdarg.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/iflash.h>
-
-/* Parameters that can be set with 'insmod' */
-
-static u_long base = 0xe0000;
-static int erase_timeout = 10*HZ; /* in ticks */
-static int retry_limit = 4; /* write retries */
-static u_long max_tries = 4096; /* status polling */
-
-MODULE_PARM(base,"l");
-MODULE_PARM(erase_timeout, "i");
-MODULE_PARM(retry_limit, "i");
-MODULE_PARM(max_tries, "i");
-
-#define WINDOW_SIZE 0x2000
-#define WINDOW_MASK (WINDOW_SIZE - 1)
-#define PAGEREG_LO (WINDOW_SIZE)
-#define PAGEREG_HI (WINDOW_SIZE + 2)
-
-static struct mtd_info *mymtd;
-static struct timer_list flashcard_timer;
-
-#define MAX_CELLS 32
-#define MAX_FLASH_DEVICES 8
-
-/* A flash region is composed of one or more "cells", where we allow
- simultaneous erases if they are in different cells */
-
-
-
-struct mypriv {
- u_char *baseaddr;
- u_short curpage;
- u_char locked;
- u_short numdevices;
- u_char interleave;
- struct erase_info *cur_erases;
- wait_queue_head_t wq;
- u_char devstat[MAX_FLASH_DEVICES];
- u_long devshift;
-};
-
-
-static void flashcard_periodic(u_long data);
-static int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr);
-static int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-static int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-static void flashcard_sync (struct mtd_info *mtd);
-
-static inline void resume_erase(volatile u_char *addr);
-static inline int suspend_erase(volatile u_char *addr);
-static inline int byte_write (volatile u_char *addr, u_char byte);
-static inline int word_write (volatile u_char *addr, __u16 word);
-static inline int check_write(volatile u_char *addr);
-static inline void block_erase (volatile u_char *addr);
-static inline int check_erase(volatile u_char *addr);
-
-#ifdef CONFIG_SMP
-#warning This is definitely not SMP safe. Lock the paging mechanism.
-#endif
-
-static u_char *pagein(struct mtd_info *mtd, u_long addr)
-{
- struct mypriv *priv=mtd->priv;
- u_short page = addr >> 13;
-
- priv->baseaddr[PAGEREG_LO] = page & 0xff;
- priv->baseaddr[PAGEREG_HI] = page >> 8;
- priv->curpage = page;
-
- return &priv->baseaddr[addr & WINDOW_MASK];
-}
-
-
-void flashcard_sync (struct mtd_info *mtd)
-{
- struct mypriv *priv=mtd->priv;
-
- flashcard_periodic((u_long) mtd);
- printk("sync...");
- if (priv->cur_erases)
- interruptible_sleep_on(&priv->wq);
- printk("Done.\n");
-}
-
-int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- u_char *pageaddr;
- struct mypriv *priv=mtd->priv;
- struct erase_info **tmp=&priv->cur_erases;
-
- if (instr->len != mtd->erasesize)
- return -EINVAL;
- if (instr->addr + instr->len > mtd->size)
- return -EINVAL;
-
- pageaddr=pagein(mtd,instr->addr);
- instr->mtd = mtd;
- instr->dev = instr->addr >> priv->devshift;
- instr->cell = (instr->addr - (instr->dev << priv->devshift)) / mtd->erasesize;
- instr->next = NULL;
- instr->state = MTD_ERASE_PENDING;
-
- while (*tmp)
- {
- tmp = &((*tmp) -> next);
- }
-
- *tmp = instr;
- flashcard_periodic((u_long)mtd);
- return 0;
-}
-
-
-int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- u_char *pageaddr=pagein(mtd,from);
- struct mypriv *priv=mtd->priv;
- u_char device = from >> priv->devshift;
- u_char cell = (int) (from - (device << priv->devshift)) / mtd->erasesize;
- int ret = 0, timeron = 0;
-
- if ((from & WINDOW_MASK) + len <= WINDOW_SIZE)
- *retlen = len;
- else
- *retlen = WINDOW_SIZE - (from & WINDOW_MASK);
-
- if (priv->devstat[device])
- {
-
- /* There is an erase in progress or pending for this device. Stop it */
- timeron = del_timer(&flashcard_timer);
-
- if (priv->cur_erases && priv->cur_erases->cell == cell)
-
- {
- /* The erase is on the current cell. Just return all 0xff */
- add_timer(&flashcard_timer);
-
-
- printk("Cell %d currently erasing. Setting to all 0xff\n",cell);
- memset(buf, 0xff, *retlen);
- return 0;
- }
- if (priv->devstat[device] == MTD_ERASING)
- {
- ret = suspend_erase(pageaddr);
- priv->devstat[device] = MTD_ERASE_SUSPEND;
-
- if (ret)
- {
- printk("flashcard: failed to suspend erase\n");
- add_timer (&flashcard_timer);
- return ret;
- }
- }
-
- }
-
- writew(IF_READ_ARRAY, (u_long)pageaddr & ~1);
-
- ret = 0;
- memcpy (buf, pageaddr, *retlen);
-
- writew(IF_READ_CSR, (u_long)pageaddr & ~1);
-
-
- if (priv->devstat[device] & MTD_ERASE_SUSPEND)
- {
- resume_erase(pageaddr);
- priv->devstat[device]=MTD_ERASING;
- }
-
-
- if (timeron) add_timer (&flashcard_timer);
-
- return ret;
-}
-
-
-int flashcard_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;
- u_char *endaddr, *startaddr;
- register u_char *pageaddr;
- u_char device = to >> priv->devshift;
-/* jiffies_t oldj=jiffies;*/
- int ret;
-
- while (priv->devstat[device])
- {
- flashcard_sync(mtd);
- }
-
- if ((to & WINDOW_MASK) + len <= WINDOW_SIZE)
- *retlen = len;
- else
- *retlen = WINDOW_SIZE - (to & WINDOW_MASK);
-
- pageaddr = pagein(mtd, to);
- startaddr = (u_char *)((u_long) pageaddr & ~1);
- endaddr = pageaddr+(*retlen);
-
-
-
- /* Set up to read */
- writew(IF_READ_CSR, startaddr);
-
- /* Make sure it's aligned by reading the first byte if necessary */
- if (to & 1)
- {
- /* Unaligned access */
-
- u_char cbuf;
-
- cbuf = *buf;
-
- if (!((u_long)pageaddr & 0xf))
- schedule();
-
- ret = byte_write(pageaddr, cbuf);
- if (ret) return ret;
-
- pageaddr++; buf++;
- }
-
-
- for ( ; pageaddr + 1 < endaddr; buf += 2, pageaddr += 2)
- {
- /* if ((u_long)pageaddr & 0xf) schedule();*/
-
- ret = word_write(pageaddr, *(__u16 *)buf);
- if (ret)
- return ret;
- }
-
- if (pageaddr != endaddr)
- {
- /* One more byte to write at the end. */
- u_char cbuf;
-
- cbuf = *buf;
-
- ret = byte_write(pageaddr, cbuf);
-
- if (ret) return ret;
- }
-
- return check_write(startaddr);
-/* printk("Time taken in flashcard_write: %lx jiffies\n",jiffies - oldj);*/
-}
-
-
-
-
-/*====================================================================*/
-
-static inline int byte_write (volatile u_char *addr, u_char byte)
-{
- register u_char status;
- register u_short i = 0;
-
- do {
- status = readb(addr);
- if (status & CSR_WR_READY)
- {
- writeb(IF_WRITE & 0xff, addr);
- writeb(byte, addr);
- return 0;
- }
- i++;
- } while(i < max_tries);
-
-
- printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status);
- return -EIO;
-}
-
-static inline int word_write (volatile u_char *addr, __u16 word)
-{
- register u_short status;
- register u_short i = 0;
-
- do {
- status = readw(addr);
- if ((status & CSR_WR_READY) == CSR_WR_READY)
- {
- writew(IF_WRITE, addr);
- writew(word, addr);
- return 0;
- }
- i++;
- } while(i < max_tries);
-
- printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status);
- return -EIO;
-}
-
-static inline void block_erase (volatile u_char *addr)
-{
- writew(IF_BLOCK_ERASE, addr);
- writew(IF_CONFIRM, addr);
-}
-
-
-static inline int check_erase(volatile u_char *addr)
-{
- __u16 status;
-
-/* writew(IF_READ_CSR, addr);*/
- status = readw(addr);
-
-
- if ((status & CSR_WR_READY) != CSR_WR_READY)
- return -EBUSY;
-
- if (status & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR))
- {
- printk(KERN_NOTICE "flashcard: erase failed, status 0x%x\n",
- status);
- return -EIO;
- }
-
- return 0;
-}
-
-static inline int suspend_erase(volatile u_char *addr)
-{
- __u16 status;
- u_long i = 0;
-
- writew(IF_ERASE_SUSPEND, addr);
- writew(IF_READ_CSR, addr);
-
- do {
- status = readw(addr);
- if ((status & CSR_WR_READY) == CSR_WR_READY)
- return 0;
- i++;
- } while(i < max_tries);
-
- printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status);
- return -EIO;
-
-}
-
-static inline void resume_erase(volatile u_char *addr)
-{
- __u16 status;
-
- writew(IF_READ_CSR, addr);
- status = readw(addr);
-
- /* Only give resume signal if the erase is really suspended */
- if (status & CSR_ERA_SUSPEND)
- writew(IF_CONFIRM, addr);
-}
-
-static inline void reset_block(volatile u_char *addr)
-{
- u_short i;
- __u16 status;
-
- writew(IF_CLEAR_CSR, addr);
-
- for (i = 0; i < 100; i++) {
- writew(IF_READ_CSR, addr);
- status = readw(addr);
- if (status != 0xffff) break;
- udelay(1000);
- }
-
- writew(IF_READ_CSR, addr);
-}
-
-static inline int check_write(volatile u_char *addr)
-{
- u_short status, i = 0;
-
- writew(IF_READ_CSR, addr);
-
- do {
- status = readw(addr);
- if (status & (CSR_WR_ERR | CSR_VPP_LOW))
- {
- printk(KERN_NOTICE "flashcard: write failure at %p, status 0x%x\n", addr, status);
- reset_block(addr);
- return -EIO;
- }
- if ((status & CSR_WR_READY) == CSR_WR_READY)
- return 0;
- i++;
- } while (i < max_tries);
-
- printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status);
- return -EIO;
-}
-
-
-/*====================================================================*/
-
-
-
-static void flashcard_periodic(unsigned long data)
-{
- register struct mtd_info *mtd = (struct mtd_info *)data;
- register struct mypriv *priv = mtd->priv;
- struct erase_info *erase = priv->cur_erases;
- u_char *pageaddr;
-
- del_timer (&flashcard_timer);
-
- if (!erase)
- return;
-
- pageaddr = pagein(mtd, erase->addr);
-
- if (erase->state == MTD_ERASE_PENDING)
- {
- block_erase(pageaddr);
- priv->devstat[erase->dev] = erase->state = MTD_ERASING;
- erase->time = jiffies;
- erase->retries = 0;
- }
- else if (erase->state == MTD_ERASING)
- {
- /* It's trying to erase. Check whether it's finished */
-
- int ret = check_erase(pageaddr);
-
- if (!ret)
- {
- /* It's finished OK */
- priv->devstat[erase->dev] = 0;
- priv->cur_erases = erase->next;
- erase->state = MTD_ERASE_DONE;
- if (erase->callback)
- (*(erase->callback))(erase);
- else
- kfree(erase);
- }
- else if (ret == -EIO)
- {
- if (++erase->retries > retry_limit)
- {
- printk("Failed too many times. Giving up\n");
- priv->cur_erases = erase->next;
- priv->devstat[erase->dev] = 0;
- erase->state = MTD_ERASE_FAILED;
- if (erase->callback)
- (*(erase->callback))(erase);
- else
- kfree(erase);
- }
- else
- priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING;
- }
- else if (erase->time + erase_timeout < jiffies)
- {
- printk("Flash erase timed out. The world is broken.\n");
-
- /* Just ignore and hope it goes away. For a while, read ops will give the CSR
- and writes won't work. */
-
- priv->cur_erases = erase->next;
- priv->devstat[erase->dev] = 0;
- erase->state = MTD_ERASE_FAILED;
- if (erase->callback)
- (*(erase->callback))(erase);
- else
- kfree(erase);
- }
- }
-
- if (priv->cur_erases)
- {
- flashcard_timer.expires = jiffies + HZ;
- add_timer (&flashcard_timer);
- }
- else
- wake_up_interruptible(&priv->wq);
-
-}
-
-#if defined (MODULE) && LINUX_VERSION_CODE < 0x20211
-#define init_doc1000 init_module
-#define cleanup_doc1000 cleanup_module
-#endif
-
-int __init init_doc1000(void)
-{
- struct mypriv *priv;
-
- if (!base)
- {
- printk(KERN_NOTICE "flashcard: No start address for memory device.\n");
- return -EINVAL;
- }
-
- mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
-
- if (!mymtd)
- {
- printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device.\n");
- return -ENOMEM;
- }
-
- memset(mymtd,0,sizeof(struct mtd_info));
-
- mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL);
- if (!mymtd->priv)
- {
- kfree(mymtd);
- printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device's private data.\n");
- return -ENOMEM;
- }
-
-
-
-
- priv=mymtd->priv;
- init_waitqueue_head(&priv->wq);
-
- memset (priv,0,sizeof(struct mypriv));
-
- priv->baseaddr = phys_to_virt(base);
- priv->numdevices = 4;
-
- mymtd->name = "M-Systems DiskOnChip 1000";
-
- mymtd->size = 0x100000;
- mymtd->flags = MTD_CLEAR_BITS | MTD_ERASEABLE;
- mymtd->erase = flashcard_erase;
- mymtd->point = NULL;
- mymtd->unpoint = NULL;
- mymtd->read = flashcard_read;
- mymtd->write = flashcard_write;
-
- mymtd->sync = flashcard_sync;
- mymtd->erasesize = 0x10000;
- // mymtd->interleave = 2;
- priv->devshift = 24;
- mymtd->type = MTD_NORFLASH;
-
- if (add_mtd_device(mymtd))
- {
- printk(KERN_NOTICE "MTD device registration failed!\n");
- kfree(mymtd->priv);
- kfree(mymtd);
- return -EAGAIN;
- }
-
- init_timer(&flashcard_timer);
- flashcard_timer.function = flashcard_periodic;
- flashcard_timer.data = (u_long)mymtd;
- return 0;
-}
-
-static void __init cleanup_doc1000(void)
-{
- kfree (mymtd->priv);
- del_mtd_device(mymtd);
- kfree(mymtd);
-}
-
-#if LINUX_VERSION_CODE >= 0x20211
-module_init(init_doc1000);
-module_exit(cleanup_doc1000);
-#endif
+++ /dev/null
-
-/*
- * Linux driver for Disk-On-Chip 2000 and Millennium
- * (c) 1999 Machine Vision Holdings, Inc.
- * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
- *
- * $Id: doc2000.c,v 1.39 2000/12/01 17:34:29 dwmw2 Exp $
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ids.h>
-#include <linux/mtd/doc2000.h>
-
-#define DOC_SUPPORT_2000
-#define DOC_SUPPORT_MILLENNIUM
-
-#ifdef DOC_SUPPORT_2000
-#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k)
-#else
-#define DoC_is_2000(doc) (0)
-#endif
-
-#ifdef DOC_SUPPORT_MILLENNIUM
-#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
-#else
-#define DoC_is_Millennium(doc) (0)
-#endif
-
-/* #define ECC_DEBUG */
-
-/* I have no idea why some DoC chips can not use memcpy_from|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
-*/
-
-static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, u_char *eccbuf);
-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);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
-
-static struct mtd_info *doc2klist = NULL;
-
-/* Perform the required delay cycles by reading from the appropriate register */
-static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles)
-{
- volatile char dummy;
- int i;
-
- for (i = 0; i < cycles; i++) {
- if (DoC_is_Millennium(doc))
- dummy = ReadDOC(doc->virtadr, NOP);
- else
- dummy = ReadDOC(doc->virtadr, DOCStatus);
- }
-
-}
-
-/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
-static int _DoC_WaitReady(struct DiskOnChip *doc)
-{
- unsigned long docptr = doc->virtadr;
- unsigned short c = 0xffff;
-
- DEBUG(MTD_DEBUG_LEVEL3,
- "_DoC_WaitReady called for out-of-line wait\n");
-
- /* Out-of-line routine to wait for chip response */
- while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
- ;
-
- if (c == 0)
- DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
-
- return (c == 0);
-}
-
-static inline int DoC_WaitReady(struct DiskOnChip *doc)
-{
- unsigned long docptr = doc->virtadr;
- /* This is inline, to optimise the common case, where it's ready instantly */
- int ret = 0;
-
- /* 4 read form NOP register should be issued in prior to the read from CDSNControl
- see Software Requirement 11.4 item 2. */
- DoC_Delay(doc, 4);
-
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- /* Call the out-of-line routine to wait */
- ret = _DoC_WaitReady(doc);
-
- /* issue 2 read from NOP register after reading from CDSNControl register
- see Software Requirement 11.4 item 2. */
- DoC_Delay(doc, 2);
-
- 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
- required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-
-static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
- unsigned char xtraflags)
-{
- unsigned long docptr = doc->virtadr;
-
- if (DoC_is_2000(doc))
- xtraflags |= CDSN_CTRL_FLASH_IO;
-
- /* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- if (DoC_is_Millennium(doc))
- WriteDOC(command, docptr, CDSNSlowIO);
-
- /* Send the command */
- WriteDOC_(command, docptr, doc->ioreg);
-
- /* Lower the CLE line */
- WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */
- return DoC_WaitReady(doc);
-}
-
-/* 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
- required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-
-static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
- unsigned char xtraflags1, unsigned char xtraflags2)
-{
- unsigned long docptr;
- int i;
-
- docptr = doc->virtadr;
-
- if (DoC_is_2000(doc))
- xtraflags1 |= CDSN_CTRL_FLASH_IO;
-
- /* Assert the ALE (Address Latch Enable) line to the flash chip */
- WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
-
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- /* Send the address */
- /* Devices with 256-byte page are addressed as:
- Column (bits 0-7), Page (bits 8-15, 16-23, 24-31)
- * there is no device on the market with page256
- and more than 24 bits.
- Devices with 512-byte page are addressed as:
- Column (bits 0-7), Page (bits 9-16, 17-24, 25-31)
- * 25-31 is sent only if the chip support it.
- * bit 8 changes the read command to be sent
- (NAND_CMD_READ0 or NAND_CMD_READ1).
- */
-
- if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) {
- if (DoC_is_Millennium(doc))
- WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
- WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
- }
-
- if (doc->page256) {
- ofs = ofs >> 8;
- } else {
- ofs = ofs >> 9;
- }
-
- if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) {
- for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) {
- if (DoC_is_Millennium(doc))
- WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
- WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
- }
- }
-
- DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
-
- /* FIXME: The SlowIO's for millennium could be replaced by
- a single WritePipeTerm here. mf. */
-
- /* Lower the ALE line */
- WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr,
- CDSNControl);
-
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- /* Wait for the chip to respond - Software requirement 11.4.1 */
- return DoC_WaitReady(doc);
-}
-
-/* Read a buffer from DoC, taking care of Millennium odditys */
-static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len)
-{
- int dummy;
- int modulus = 0xffff;
- unsigned long docptr;
- int i;
-
- docptr = doc->virtadr;
-
- if (len <= 0)
- return;
-
- if (DoC_is_Millennium(doc)) {
- /* Read the data via the internal pipeline through CDSN IO register,
- see Pipelined Read Operations 11.3 */
- dummy = ReadDOC(docptr, ReadPipeInit);
-
- /* Millennium should use the LastDataRead register - Pipeline Reads */
- len--;
-
- /* This is needed for correctly ECC calculation */
- modulus = 0xff;
- }
-
- for (i = 0; i < len; i++)
- buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus));
-
- if (DoC_is_Millennium(doc)) {
- buf[i] = ReadDOC(docptr, LastDataRead);
- }
-}
-
-/* Write a buffer to DoC, taking care of Millennium odditys */
-static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len)
-{
- unsigned long docptr;
- int i;
-
- docptr = doc->virtadr;
-
- if (len <= 0)
- return;
-
- for (i = 0; i < len; i++)
- WriteDOC_(buf[i], docptr, doc->ioreg + i);
-
- if (DoC_is_Millennium(doc)) {
- WriteDOC(0x00, docptr, WritePipeTerm);
- }
-}
-
-
-/* DoC_SelectChip: Select a given flash chip within the current floor */
-
-static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip)
-{
- unsigned long docptr = doc->virtadr;
-
- /* Software requirement 11.4.4 before writing DeviceSelect */
- /* Deassert the CE line to eliminate glitches on the FCE# outputs */
- WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl);
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- /* Select the individual flash chip requested */
- WriteDOC(chip, docptr, CDSNDeviceSelect);
- DoC_Delay(doc, 4);
-
- /* Reassert the CE line */
- WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr,
- CDSNControl);
- DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
-
- /* Wait for it to be ready */
- return DoC_WaitReady(doc);
-}
-
-/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
-
-static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor)
-{
- unsigned long docptr = doc->virtadr;
-
- /* Select the floor (bank) of chips required */
- WriteDOC(floor, docptr, FloorSelect);
-
- /* Wait for the chip to be ready */
- return DoC_WaitReady(doc);
-}
-
-/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
-
-static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
-{
- int mfr, id, i;
- volatile char dummy;
-
- /* Page in the required floor/chip */
- DoC_SelectFloor(doc, floor);
- DoC_SelectChip(doc, chip);
-
- /* Reset the chip */
- if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) {
- DEBUG(MTD_DEBUG_LEVEL2,
- "DoC_Command (reset) for %d,%d returned true\n",
- floor, chip);
- return 0;
- }
-
-
- /* Read the NAND chip ID: 1. Send ReadID command */
- if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) {
- DEBUG(MTD_DEBUG_LEVEL2,
- "DoC_Command (ReadID) for %d,%d returned true\n",
- floor, chip);
- return 0;
- }
-
- /* Read the NAND chip ID: 2. Send address byte zero */
- DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0);
-
- /* Read the manufacturer and device id codes from the device */
-
- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
- dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
- DoC_Delay(doc, 2);
- mfr = ReadDOC_(doc->virtadr, doc->ioreg);
-
- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
- dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
- DoC_Delay(doc, 2);
- id = ReadDOC_(doc->virtadr, doc->ioreg);
-
- /* No response - return failure */
- if (mfr == 0xff || mfr == 0)
- return 0;
-
- /* Check it's the same as the first chip we identified.
- * M-Systems say that any given DiskOnChip device should only
- * contain _one_ type of flash part, although that's not a
- * hardware restriction. */
- if (doc->mfr) {
- if (doc->mfr == mfr && doc->id == id)
- return 1; /* This is another the same the first */
- else
- printk(KERN_WARNING
- "Flash chip at floor %d, chip %d is different:\n",
- floor, chip);
- }
-
- /* Print and store the manufacturer and ID codes. */
- for (i = 0; nand_flash_ids[i].name != NULL; i++) {
- if (mfr == nand_flash_ids[i].manufacture_id &&
- id == nand_flash_ids[i].model_id) {
- printk(KERN_INFO
- "Flash chip found: Manufacturer ID: %2.2X, "
- "Chip ID: %2.2X (%s)\n", mfr, id,
- nand_flash_ids[i].name);
- if (!doc->mfr) {
- doc->mfr = mfr;
- doc->id = id;
- doc->chipshift =
- nand_flash_ids[i].chipshift;
- doc->page256 = nand_flash_ids[i].page256;
- doc->pageadrlen =
- nand_flash_ids[i].pageadrlen;
- doc->erasesize =
- nand_flash_ids[i].erasesize;
- return 1;
- }
- return 0;
- }
- }
-
-
- /* We haven't fully identified the chip. Print as much as we know. */
- printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n",
- id, mfr);
-
- printk(KERN_WARNING "Please report to dwmw2@infradead.org\n");
- return 0;
-}
-
-/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
-
-static void DoC_ScanChips(struct DiskOnChip *this)
-{
- int floor, chip;
- int numchips[MAX_FLOORS];
- int maxchips = MAX_CHIPS;
- int ret = 1;
-
- this->numchips = 0;
- this->mfr = 0;
- this->id = 0;
-
- if (DoC_is_Millennium(this))
- maxchips = MAX_CHIPS_MIL;
-
- /* For each floor, find the number of valid chips it contains */
- for (floor = 0; floor < MAX_FLOORS; floor++) {
- ret = 1;
- numchips[floor] = 0;
- for (chip = 0; chip < maxchips && ret != 0; chip++) {
-
- ret = DoC_IdentChip(this, floor, chip);
- if (ret) {
- numchips[floor]++;
- this->numchips++;
- }
- }
- }
-
- /* If there are none at all that we recognise, bail */
- if (!this->numchips) {
- printk("No flash chips recognised.\n");
- return;
- }
-
- /* Allocate an array to hold the information for each chip */
- this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
- if (!this->chips) {
- printk("No memory for allocating chip info structures\n");
- return;
- }
-
- ret = 0;
-
- /* Fill out the chip array with {floor, chipno} for each
- * detected chip in the device. */
- for (floor = 0; floor < MAX_FLOORS; floor++) {
- for (chip = 0; chip < numchips[floor]; chip++) {
- this->chips[ret].floor = floor;
- this->chips[ret].chip = chip;
- this->chips[ret].curadr = 0;
- this->chips[ret].curmode = 0x50;
- ret++;
- }
- }
-
- /* 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",
- this->numchips, this->totlen >> 20);
-}
-
-
-static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
-{
- int tmp1, tmp2, retval;
- if (doc1->physadr == doc2->physadr)
- return 1;
-
- /* Use the alias resolution register which was set aside for this
- * purpose. If it's value is the same on both chips, they might
- * be the same chip, and we write to one and check for a change in
- * the other. It's unclear if this register is usuable in the
- * DoC 2000 (it's in the Millennium docs), but it seems to work. */
- tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
- tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp1 != tmp2)
- return 0;
-
- WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution);
- tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp2 == (tmp1 + 1) % 0xff)
- retval = 1;
- else
- retval = 0;
-
- /* Restore register contents. May not be necessary, but do it just to
- * be safe. */
- WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
- return retval;
-}
-
-static const char im_name[] = "DoC2k_init";
-
-/* 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 <kaos@ocs.com.au> 29 Oct 2000.
- */
-static void DoC2k_init(struct mtd_info *mtd)
-{
- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
- struct DiskOnChip *old = NULL;
-
- /* We must avoid being called twice for the same device. */
-
- if (doc2klist)
- old = (struct DiskOnChip *) doc2klist->priv;
-
- while (old) {
- if (DoC2k_is_alias(old, this)) {
- printk(KERN_NOTICE
- "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
- this->physadr);
- iounmap((void *) this->virtadr);
- kfree(mtd);
- return;
- }
- if (old->nextdoc)
- old = (struct DiskOnChip *) old->nextdoc->priv;
- else
- old = NULL;
- }
-
-
- switch (this->ChipID) {
- case DOC_ChipID_Doc2k:
- mtd->name = "DiskOnChip 2000";
- this->ioreg = DoC_2k_CDSN_IO;
- break;
- case DOC_ChipID_DocMil:
- mtd->name = "DiskOnChip Millennium";
- this->ioreg = DoC_Mil_CDSN_IO;
- break;
- }
-
- printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
- this->physadr);
-
- mtd->type = MTD_NANDFLASH;
- mtd->flags = MTD_CAP_NANDFLASH;
- mtd->size = 0;
- mtd->erasesize = 0;
- mtd->oobblock = 512;
- mtd->oobsize = 16;
- mtd->module = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->point = NULL;
- mtd->unpoint = NULL;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_ecc = doc_read_ecc;
- mtd->write_ecc = doc_write_ecc;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
- mtd->sync = NULL;
-
- this->totlen = 0;
- this->numchips = 0;
-
- this->curfloor = -1;
- this->curchip = -1;
-
- /* Ident all the chips present. */
- DoC_ScanChips(this);
-
- if (!this->totlen) {
- kfree(mtd);
- iounmap((void *) this->virtadr);
- } else {
- this->nextdoc = doc2klist;
- doc2klist = mtd;
- mtd->size = this->totlen;
- mtd->erasesize = this->erasesize;
- add_mtd_device(mtd);
- return;
- }
-}
-
-static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t * retlen, u_char * buf)
-{
- /* Just a special case of doc_read_ecc */
- return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
-}
-
-static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
- size_t * retlen, u_char * buf, u_char * eccbuf)
-{
- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
- unsigned long docptr;
- struct Nand *mychip;
- unsigned char syndrome[6];
- volatile char dummy;
- int i, len256 = 0, ret=0;
-
- docptr = this->virtadr;
-
- /* Don't allow read past end of device */
- if (from >= this->totlen)
- return -EINVAL;
-
- /* Don't allow a single read to cross a 512-byte block boundary */
- if (from + len > ((from | 0x1ff) + 1))
- len = ((from | 0x1ff) + 1) - from;
-
- /* The ECC will not be calculated correctly if less than 512 is read */
- if (len != 0x200 && eccbuf)
- printk(KERN_WARNING
- "ECC needs a full sector read (adr: %lx size %lx)\n",
- (long) from, (long) len);
-
- /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
-
-
- /* Find the chip which is to be used and select it */
- mychip = &this->chips[from >> (this->chipshift)];
-
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
-
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- DoC_Command(this,
- (!this->page256
- && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
- CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
- CDSN_CTRL_ECC_IO);
-
- if (eccbuf) {
- /* Prime the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- }
-
- /* treat crossing 256-byte sector for 2M x 8bits devices */
- if (this->page256 && from + len > (from | 0xff) + 1) {
- len256 = (from | 0xff) + 1 - from;
- DoC_ReadBuf(this, buf, len256);
-
- DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
- CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
- }
-
- DoC_ReadBuf(this, &buf[len256], len - len256);
-
- /* Let the caller know we completed it */
- *retlen = len;
-
- if (eccbuf) {
- /* Read the ECC data through the DiskOnChip ECC logic */
- /* Note: this will work even with 2M x 8bit devices as */
- /* they have 8 bytes of OOB per 256 page. mf. */
- DoC_ReadBuf(this, eccbuf, 6);
-
- /* Flush the pipeline */
- if (DoC_is_Millennium(this)) {
- dummy = ReadDOC(docptr, ECCConf);
- dummy = ReadDOC(docptr, ECCConf);
- i = ReadDOC(docptr, ECCConf);
- } else {
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- i = ReadDOC(docptr, 2k_ECCStatus);
- }
-
- /* Check the ECC Status */
- if (i & 0x80) {
- int nb_errors;
- /* There was an ECC error */
-#ifdef ECC_DEBUG
- printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
-#endif
- /* Read the ECC syndrom through the DiskOnChip ECC logic.
- These syndrome will be all ZERO when there is no error */
- for (i = 0; i < 6; i++) {
- syndrome[i] =
- ReadDOC(docptr, ECCSyndrome0 + i);
- }
- nb_errors = doc_decode_ecc(buf, syndrome);
-
-#ifdef ECC_DEBUG
- printk("Errors corrected: %x\n", nb_errors);
-#endif
- if (nb_errors < 0) {
- /* We return error, but have actually done the read. Not that
- this can be told to user-space, via sys_read(), but at least
- MTD-aware stuff can know about it by checking *retlen */
- ret = -EIO;
- }
- }
-
-#ifdef PSYCHO_DEBUG
- printk("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (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);
- }
-
- return ret;
-}
-
-static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t * retlen, const u_char * buf)
-{
- char eccbuf[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
-}
-
-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)
-{
- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
- int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
- unsigned long docptr;
- volatile char dummy;
- int len256 = 0;
- struct Nand *mychip;
-
- docptr = this->virtadr;
-
- /* Don't allow write past end of device */
- if (to >= this->totlen)
- return -EINVAL;
-
- /* Don't allow a single write to cross a 512-byte block boundary */
- if (to + len > ((to | 0x1ff) + 1))
- len = ((to | 0x1ff) + 1) - to;
-
- /* The ECC will not be calculated correctly if less than 512 is written */
- if (len != 0x200 && eccbuf)
- printk(KERN_WARNING
- "ECC needs a full sector write (adr: %lx size %lx)\n",
- (long) to, (long) len);
-
- /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
-
- /* Find the chip which is to be used and select it */
- mychip = &this->chips[to >> (this->chipshift)];
-
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
-
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* Set device to main plane of flash */
- DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(this,
- (!this->page256
- && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
- CDSN_CTRL_WP);
-
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
-
- if (eccbuf) {
- /* Prime the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- }
-
- /* treat crossing 256-byte sector for 2M x 8bits devices */
- if (this->page256 && to + len > (to | 0xff) + 1) {
- len256 = (to | 0xff) + 1 - to;
- DoC_WriteBuf(this, buf, len256);
-
- DoC_Command(this, NAND_CMD_PAGEPROG, 0);
-
- DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- /* There's an implicit DoC_WaitReady() in DoC_Command */
-
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk("Error programming flash\n");
- /* Error in programming */
- *retlen = 0;
- return -EIO;
- }
-
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
- CDSN_CTRL_ECC_IO);
- }
-
- DoC_WriteBuf(this, &buf[len256], len - len256);
-
- if (eccbuf) {
- WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
- CDSNControl);
-
- if (DoC_is_Millennium(this)) {
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- } else {
- WriteDOC_(0, docptr, this->ioreg);
- WriteDOC_(0, docptr, this->ioreg);
- WriteDOC_(0, docptr, this->ioreg);
- }
-
- /* Read the ECC data through the DiskOnChip ECC logic */
- for (di = 0; di < 6; di++) {
- eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
- }
-
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
-
-#ifdef PSYCHO_DEBUG
- printk
- ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
- eccbuf[4], eccbuf[5]);
-#endif
- }
-
- DoC_Command(this, NAND_CMD_PAGEPROG, 0);
-
- DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- /* There's an implicit DoC_WaitReady() in DoC_Command */
-
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk("Error programming flash\n");
- /* Error in programming */
- *retlen = 0;
- return -EIO;
- }
-
- /* Let the caller know we completed it */
- *retlen = len;
-
- if (eccbuf) {
- unsigned char x[8];
- size_t dummy;
-
- /* Write the ECC data to flash */
- for (di=0; di<6; di++)
- x[di] = eccbuf[di];
-
- x[6]=0x55;
- x[7]=0x55;
-
- return doc_write_oob(mtd, to, 8, &dummy, x);
- }
-
- return 0;
-}
-
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t * retlen, u_char * buf)
-{
- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
- int len256 = 0;
- unsigned long docptr;
- struct Nand *mychip;
-
- docptr = this->virtadr;
-
- mychip = &this->chips[ofs >> this->chipshift];
-
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* update address for 2M x 8bit devices. OOB starts on the second */
- /* page to maintain compatibility with doc_read_ecc. */
- if (this->page256) {
- if (!(ofs & 0x8))
- ofs += 0x100;
- else
- ofs -= 0x8;
- }
-
- DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0);
-
- /* treat crossing 8-byte OOB data for 2M x 8bit devices */
- /* Note: datasheet says it should automaticaly wrap to the */
- /* next OOB block, but it didn't work here. mf. */
- if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
- len256 = (ofs | 0x7) + 1 - ofs;
- DoC_ReadBuf(this, buf, len256);
-
- DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff),
- CDSN_CTRL_WP, 0);
- }
-
- DoC_ReadBuf(this, &buf[len256], len - len256);
-
- *retlen = len;
- return 0;
-
-}
-
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t * retlen, const u_char * buf)
-{
- struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
- int len256 = 0;
- unsigned long docptr = this->virtadr;
- struct Nand *mychip = &this->chips[ofs >> this->chipshift];
- int dummy;
-
- // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
- // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* disable the ECC engine */
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
-
- /* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
-
- /* issue the Read2 command to set the pointer to the Spare Data Area. */
- DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
-
- /* update address for 2M x 8bit devices. OOB starts on the second */
- /* page to maintain compatibility with doc_read_ecc. */
- if (this->page256) {
- if (!(ofs & 0x8))
- ofs += 0x100;
- else
- ofs -= 0x8;
- }
-
- /* issue the Serial Data In command to initial the Page Program process */
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0);
-
- /* treat crossing 8-byte OOB data for 2M x 8bit devices */
- /* Note: datasheet says it should automaticaly wrap to the */
- /* next OOB block, but it didn't work here. mf. */
- if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
- len256 = (ofs | 0x7) + 1 - ofs;
- DoC_WriteBuf(this, buf, len256);
-
- DoC_Command(this, NAND_CMD_PAGEPROG, 0);
- DoC_Command(this, NAND_CMD_STATUS, 0);
- /* DoC_WaitReady() is implicit in DoC_Command */
-
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk("Error programming oob data\n");
- /* There was an error */
- *retlen = 0;
- return -EIO;
- }
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0);
- }
-
- DoC_WriteBuf(this, &buf[len256], len - len256);
-
- DoC_Command(this, NAND_CMD_PAGEPROG, 0);
- DoC_Command(this, NAND_CMD_STATUS, 0);
- /* DoC_WaitReady() is implicit in DoC_Command */
-
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk("Error programming oob data\n");
- /* There was an error */
- *retlen = 0;
- return -EIO;
- }
-
- *retlen = len;
- return 0;
-
-}
-
-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;
- unsigned long docptr;
- struct Nand *mychip;
-
- if (len != mtd->erasesize)
- printk(KERN_WARNING "Erase not right size (%lx != %lx)n",
- len, mtd->erasesize);
-
- docptr = this->virtadr;
-
- mychip = &this->chips[ofs >> this->chipshift];
-
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- instr->state = MTD_ERASE_PENDING;
-
- DoC_Command(this, NAND_CMD_ERASE1, 0);
- DoC_Address(this, ADDR_PAGE, ofs, 0, 0);
- DoC_Command(this, NAND_CMD_ERASE2, 0);
-
- instr->state = MTD_ERASING;
-
- DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk("Error writing\n");
- /* There was an error */
- instr->state = MTD_ERASE_FAILED;
- } else
- instr->state = MTD_ERASE_DONE;
-
- if (instr->callback)
- instr->callback(instr);
-
- return 0;
-}
-
-
-/****************************************************************************
- *
- * Module stuff
- *
- ****************************************************************************/
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define cleanup_doc2000 cleanup_module
-#define init_doc2000 init_module
-#endif
-
-int __init init_doc2000(void)
-{
- inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
- return 0;
-}
-
-static void __exit cleanup_doc2000(void)
-{
- struct mtd_info *mtd;
- struct DiskOnChip *this;
-
- while ((mtd = doc2klist)) {
- this = (struct DiskOnChip *) mtd->priv;
- doc2klist = this->nextdoc;
-
- del_mtd_device(mtd);
-
- iounmap((void *) this->virtadr);
- kfree(this->chips);
- kfree(mtd);
- }
- inter_module_unregister(im_name);
-}
-
-module_exit(cleanup_doc2000);
-module_init(init_doc2000);
+++ /dev/null
-
-/*
- * Linux driver for Disk-On-Chip Millennium
- * (c) 1999 Machine Vision Holdings, Inc.
- * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
- *
- * $Id: doc2001.c,v 1.24 2000/12/01 13:11:02 dwmw2 Exp $
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ids.h>
-#include <linux/mtd/doc2000.h>
-
-/* #define ECC_DEBUG */
-
-/* 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
-*/
-
-static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, u_char *eccbuf);
-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);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
-
-static struct mtd_info *docmillist = NULL;
-
-/* Perform the required delay cycles by reading from the NOP register */
-static void DoC_Delay(unsigned long docptr, unsigned short cycles)
-{
- volatile char dummy;
- int i;
-
- for (i = 0; i < cycles; i++)
- dummy = ReadDOC(docptr, NOP);
-}
-
-/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
-static int _DoC_WaitReady(unsigned long docptr)
-{
- unsigned short c = 0xffff;
-
- DEBUG(MTD_DEBUG_LEVEL3,
- "_DoC_WaitReady called for out-of-line wait\n");
-
- /* Out-of-line routine to wait for chip response */
- while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
- ;
-
- if (c == 0)
- DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
-
- return (c == 0);
-}
-
-static inline int DoC_WaitReady(unsigned long docptr)
-{
- /* This is inline, to optimise the common case, where it's ready instantly */
- int ret = 0;
-
- /* 4 read form NOP register should be issued in prior to the read from CDSNControl
- see Software Requirement 11.4 item 2. */
- DoC_Delay(docptr, 4);
-
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- /* Call the out-of-line routine to wait */
- ret = _DoC_WaitReady(docptr);
-
- /* issue 2 read from NOP register after reading from CDSNControl register
- see Software Requirement 11.4 item 2. */
- DoC_Delay(docptr, 2);
-
- 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
- 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,
- unsigned char xtraflags)
-{
- /* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
- DoC_Delay(docptr, 4);
-
- /* Send the command */
- WriteDOC(command, docptr, CDSNSlowIO);
- WriteDOC(command, docptr, Mil_CDSN_IO);
-
- /* 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
- 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,
- unsigned char xtraflags1, unsigned char xtraflags2)
-{
- /* Assert the ALE (Address Latch Enable) line to the flash chip */
- WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
- DoC_Delay(docptr, 4);
-
- /* Send the address */
- switch (numbytes)
- {
- case 1:
- /* Send single byte, bits 0-7. */
- WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
- WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO);
- 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);
- 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);
- break;
- default:
- return;
- }
-
- /* Lower the ALE line */
- WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl);
- DoC_Delay(docptr, 4);
-}
-
-/* DoC_SelectChip: Select a given flash chip within the current floor */
-static int DoC_SelectChip(unsigned long docptr, int chip)
-{
- /* Select the individual flash chip requested */
- WriteDOC(chip, docptr, CDSNDeviceSelect);
- DoC_Delay(docptr, 4);
-
- /* Wait for it to be ready */
- return DoC_WaitReady(docptr);
-}
-
-/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
-static int DoC_SelectFloor(unsigned long docptr, int floor)
-{
- /* Select the floor (bank) of chips required */
- WriteDOC(floor, docptr, FloorSelect);
-
- /* Wait for the chip to be ready */
- return DoC_WaitReady(docptr);
-}
-
-/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
-static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
-{
- int mfr, id, i;
- volatile char dummy;
-
- /* Page in the required floor/chip
- FIXME: is this supported by Millennium ?? */
- DoC_SelectFloor(doc->virtadr, floor);
- DoC_SelectChip(doc->virtadr, chip);
-
- /* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_WaitReady(doc->virtadr);
-
- /* Read the NAND chip ID: 1. Send ReadID command */
- DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP);
-
- /* Read the NAND chip ID: 2. Send address byte zero */
- 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);
- 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);
-
- /* No response - return failure */
- if (mfr == 0xff || mfr == 0)
- return 0;
-
- /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */
- for (i = 0; nand_flash_ids[i].name != NULL; i++) {
- if (mfr == nand_flash_ids[i].manufacture_id &&
- id == nand_flash_ids[i].model_id) {
- printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
- "Chip ID: %2.2X (%s)\n",
- mfr, id, nand_flash_ids[i].name);
- doc->mfr = mfr;
- doc->id = id;
- doc->chipshift = nand_flash_ids[i].chipshift;
- break;
- }
- }
-
- if (nand_flash_ids[i].name == NULL)
- return 0;
- else
- return 1;
-}
-
-/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
-static void DoC_ScanChips(struct DiskOnChip *this)
-{
- int floor, chip;
- int numchips[MAX_FLOORS_MIL];
- int ret;
-
- this->numchips = 0;
- this->mfr = 0;
- this->id = 0;
-
- /* For each floor, find the number of valid chips it contains */
- for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) {
- numchips[floor] = 0;
- for (chip = 0; chip < MAX_CHIPS_MIL && ret != 0; chip++) {
- ret = DoC_IdentChip(this, floor, chip);
- if (ret) {
- numchips[floor]++;
- this->numchips++;
- }
- }
- }
- /* If there are none at all that we recognise, bail */
- if (!this->numchips) {
- printk("No flash chips recognised.\n");
- return;
- }
-
- /* Allocate an array to hold the information for each chip */
- this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
- if (!this->chips){
- printk("No memory for allocating chip info structures\n");
- return;
- }
-
- /* Fill out the chip array with {floor, chipno} for each
- * detected chip in the device. */
- for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) {
- for (chip = 0 ; chip < numchips[floor] ; chip++) {
- this->chips[ret].floor = floor;
- this->chips[ret].chip = chip;
- this->chips[ret].curadr = 0;
- this->chips[ret].curmode = 0x50;
- ret++;
- }
- }
-
- /* 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",
- this->numchips ,this->totlen >> 20);
-}
-
-static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
-{
- int tmp1, tmp2, retval;
-
- if (doc1->physadr == doc2->physadr)
- return 1;
-
- /* Use the alias resolution register which was set aside for this
- * purpose. If it's value is the same on both chips, they might
- * be the same chip, and we write to one and check for a change in
- * the other. It's unclear if this register is usuable in the
- * DoC 2000 (it's in the Millenium docs), but it seems to work. */
- tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
- tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp1 != tmp2)
- return 0;
-
- WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution);
- tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp2 == (tmp1+1) % 0xff)
- retval = 1;
- else
- retval = 0;
-
- /* Restore register contents. May not be necessary, but do it just to
- * be safe. */
- WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
- return retval;
-}
-
-static const char im_name[] = "DoCMil_init";
-
-/* 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 <kaos@ocs.com.au> 29 Oct 2000.
- */
-static void DoCMil_init(struct mtd_info *mtd)
-{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- struct DiskOnChip *old = NULL;
-
- /* We must avoid being called twice for the same device. */
- if (docmillist)
- old = (struct DiskOnChip *)docmillist->priv;
-
- while (old) {
- if (DoCMil_is_alias(this, old)) {
- printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at "
- "0x%lX - already configured\n", this->physadr);
- iounmap((void *)this->virtadr);
- kfree(mtd);
- return;
- }
- if (old->nextdoc)
- old = (struct DiskOnChip *)old->nextdoc->priv;
- else
- old = NULL;
- }
-
- mtd->name = "DiskOnChip Millennium";
- printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",
- this->physadr);
-
- mtd->type = MTD_NANDFLASH;
- mtd->flags = MTD_CAP_NANDFLASH;
- mtd->size = 0;
-
- /* FIXME: erase size is not always 8kB */
- mtd->erasesize = 0x2000;
-
- mtd->oobblock = 512;
- mtd->oobsize = 16;
- mtd->module = THIS_MODULE;
- mtd->erase = doc_erase;
- mtd->point = NULL;
- mtd->unpoint = NULL;
- mtd->read = doc_read;
- mtd->write = doc_write;
- mtd->read_ecc = doc_read_ecc;
- mtd->write_ecc = doc_write_ecc;
- mtd->read_oob = doc_read_oob;
- mtd->write_oob = doc_write_oob;
- mtd->sync = NULL;
-
- this->totlen = 0;
- this->numchips = 0;
- this->curfloor = -1;
- this->curchip = -1;
-
- /* Ident all the chips present. */
- DoC_ScanChips(this);
-
- if (!this->totlen) {
- kfree(mtd);
- iounmap((void *)this->virtadr);
- } else {
- this->nextdoc = docmillist;
- docmillist = mtd;
- mtd->size = this->totlen;
- add_mtd_device(mtd);
- return;
- }
-}
-
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- /* Just a special case of doc_read_ecc */
- return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
-}
-
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, u_char *eccbuf)
-{
- int i, ret;
- volatile char dummy;
- unsigned char syndrome[6];
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- unsigned long docptr = this->virtadr;
- struct Nand *mychip = &this->chips[from >> (this->chipshift)];
-
- /* Don't allow read past end of device */
- if (from >= this->totlen)
- return -EINVAL;
-
- /* Don't allow a single read to cross a 512-byte block boundary */
- if (from + len > ((from | 0x1ff) + 1))
- len = ((from | 0x1ff) + 1) - from;
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* issue the Read0 or Read1 command depend on which half of the page
- we are accessing. Polling the Flash Ready bit after issue 3 bytes
- address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
- DoC_WaitReady(docptr);
-
- if (eccbuf) {
- /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_EN, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
- }
-
- /* Read the data via the internal pipeline through CDSN IO register,
- see Pipelined Read Operations 11.3 */
- dummy = ReadDOC(docptr, ReadPipeInit);
-#ifndef USE_MEMCPY
- for (i = 0; i < len-1; i++) {
- /* N.B. you have to increase the source address in this way or the
- ECC logic will not work properly */
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
- }
-#else
- memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
-#endif
- buf[len - 1] = ReadDOC(docptr, LastDataRead);
-
- /* Let the caller know we completed it */
- *retlen = len;
- ret = 0;
-
- if (eccbuf) {
- /* Read the ECC data from Spare Data Area,
- see Reed-Solomon EDC/ECC 11.1 */
- dummy = ReadDOC(docptr, ReadPipeInit);
-#ifndef USE_MEMCPY
- for (i = 0; i < 5; i++) {
- /* N.B. you have to increase the source address in this way or the
- ECC logic will not work properly */
- eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
- }
-#else
- memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5);
-#endif
- eccbuf[5] = ReadDOC(docptr, LastDataRead);
-
- /* Flush the pipeline */
- dummy = ReadDOC(docptr, ECCConf);
- dummy = ReadDOC(docptr, ECCConf);
-
- /* Check the ECC Status */
- if (ReadDOC(docptr, ECCConf) & 0x80) {
- int nb_errors;
- /* There was an ECC error */
-#ifdef ECC_DEBUG
- printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
-#endif
- /* Read the ECC syndrom through the DiskOnChip ECC logic.
- These syndrome will be all ZERO when there is no error */
- for (i = 0; i < 6; i++) {
- syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
- }
- nb_errors = doc_decode_ecc(buf, syndrome);
-#ifdef ECC_DEBUG
- printk("Errors corrected: %x\n", nb_errors);
-#endif
- if (nb_errors < 0) {
- /* We return error, but have actually done the read. Not that
- this can be told to user-space, via sys_read(), but at least
- MTD-aware stuff can know about it by checking *retlen */
- ret = -EIO;
- }
- }
-
-#ifdef PSYCHO_DEBUG
- printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (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);
- }
-
- return ret;
-}
-
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
-{
- char eccbuf[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
-}
-
-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;
- volatile char dummy;
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- unsigned long docptr = this->virtadr;
- struct Nand *mychip = &this->chips[to >> (this->chipshift)];
-
- /* Don't allow write past end of device */
- if (to >= this->totlen)
- return -EINVAL;
-
-#if 0
- /* Don't allow a single write to cross a 512-byte block boundary */
- if (to + len > ( (to | 0x1ff) + 1))
- len = ((to | 0x1ff) + 1) - to;
-#else
- /* Don't allow writes which aren't exactly one block */
- if (to & 0x1ff || len != 0x200)
- return -EINVAL;
-#endif
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(docptr, NAND_CMD_RESET, 0x00);
- DoC_WaitReady(docptr);
- /* Set device to main plane of flash */
- DoC_Command(docptr, NAND_CMD_READ0, 0x00);
-
- /* issue the Serial Data In command to initial the Page Program process */
- DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
- DoC_Address(docptr, 3, to, 0x00, 0x00);
- DoC_WaitReady(docptr);
-
- if (eccbuf) {
- /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
- }
-
- /* Write the data via the internal pipeline through CDSN IO register,
- see Pipelined Write Operations 11.2 */
-#ifndef USE_MEMCPY
- for (i = 0; i < len; i++) {
- /* N.B. you have to increase the source address in this way or the
- ECC logic will not work properly */
- WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
- }
-#else
- memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
-#endif
- WriteDOC(0x00, docptr, WritePipeTerm);
-
- if (eccbuf) {
- /* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic
- see Reed-Solomon EDC/ECC 11.1 */
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
-
- /* Read the ECC data through the DiskOnChip ECC logic */
- for (i = 0; i < 6; i++) {
- eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i);
- }
-
- /* ignore the ECC engine */
- WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
-
-#ifndef USE_MEMCPY
- /* Write the ECC data to flash */
- for (i = 0; i < 6; i++) {
- /* N.B. you have to increase the source address in this way or the
- ECC logic will not work properly */
- WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i);
- }
-#else
- memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6);
-#endif
-
- /* write the block status BLOCK_USED (0x5555) at the end of ECC data
- FIXME: this is only a hack for programming the IPL area for LinuxBIOS
- and should be replace with proper codes in user space utilities */
- WriteDOC(0x55, docptr, Mil_CDSN_IO);
- WriteDOC(0x55, docptr, Mil_CDSN_IO + 1);
-
- WriteDOC(0x00, docptr, WritePipeTerm);
-
-#ifdef PSYCHO_DEBUG
- printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
- eccbuf[4], eccbuf[5]);
-#endif
- }
-
- /* Commit the Page Program command and wait for ready
- see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
- DoC_WaitReady(docptr);
-
- /* Read the status of the flash device through CDSN Slow IO register
- see Software Requirement 11.4 item 5.*/
- DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
- dummy = ReadDOC(docptr, CDSNSlowIO);
- 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;
- }
-
- /* Let the caller know we completed it */
- *retlen = len;
-
- return 0;
-}
-
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, u_char *buf)
-{
-#ifndef USE_MEMCPY
- int i;
-#endif
- volatile char dummy;
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- unsigned long docptr = this->virtadr;
- struct Nand *mychip = &this->chips[ofs >> this->chipshift];
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* disable the ECC engine */
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
-
- /* issue the Read2 command to set the pointer to the Spare Data Area.
- Polling the Flash Ready bit after issue 3 bytes address in
- Sequence Read Mode, see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0x00);
- DoC_WaitReady(docptr);
-
- /* Read the data out via the internal pipeline through CDSN IO register,
- see Pipelined Read Operations 11.3 */
- dummy = ReadDOC(docptr, ReadPipeInit);
-#ifndef USE_MEMCPY
- for (i = 0; i < len-1; i++) {
- /* N.B. you have to increase the source address in this way or the
- 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
- buf[len - 1] = ReadDOC(docptr, LastDataRead);
-
- *retlen = len;
-
- return 0;
-}
-
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
- size_t *retlen, const u_char *buf)
-{
-#ifndef USE_MEMCPY
- int i;
-#endif
- volatile char dummy;
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- unsigned long docptr = this->virtadr;
- struct Nand *mychip = &this->chips[ofs >> this->chipshift];
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- /* disable the ECC engine */
- WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
-
- /* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_WaitReady(docptr);
- /* issue the Read2 command to set the pointer to the Spare Data Area. */
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
-
- /* issue the Serial Data In command to initial the Page Program process */
- DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
- DoC_Address(docptr, 3, ofs, 0x00, 0x00);
-
- /* Write the data via the internal pipeline through CDSN IO register,
- see Pipelined Write Operations 11.2 */
-#ifndef USE_MEMCPY
- for (i = 0; i < len; i++) {
- /* N.B. you have to increase the source address in this way or the
- ECC logic will not work properly */
- WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
- }
-#else
- memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
-#endif
- WriteDOC(0x00, docptr, WritePipeTerm);
-
- /* Commit the Page Program command and wait for ready
- see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
- DoC_WaitReady(docptr);
-
- /* Read the status of the flash device through CDSN Slow IO register
- see Software Requirement 11.4 item 5.*/
- DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
- dummy = ReadDOC(docptr, CDSNSlowIO);
- 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;
- }
-
- *retlen = len;
-
- return 0;
-}
-
-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;
- 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",
- len, mtd->erasesize);
-
- /* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
-
- instr->state = MTD_ERASE_PENDING;
-
- /* issue the Erase Setup command */
- DoC_Command(docptr, NAND_CMD_ERASE1, 0x00);
- DoC_Address(docptr, 2, ofs, 0x00, 0x00);
-
- /* Commit the Erase Start command and wait for ready
- see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, NAND_CMD_ERASE2, 0x00);
- DoC_WaitReady(docptr);
-
- instr->state = MTD_ERASING;
-
- /* Read the status of the flash device through CDSN Slow 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);
- DoC_Delay(docptr, 2);
- if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
- printk("Error Erasing at 0x%lx\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;
-
- if (instr->callback)
- instr->callback(instr);
-
- return 0;
-}
-
-/****************************************************************************
- *
- * Module stuff
- *
- ****************************************************************************/
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define cleanup_doc2001 cleanup_module
-#define init_doc2001 init_module
-#endif
-
-int __init init_doc2001(void)
-{
- inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
- return 0;
-}
-
-static void __exit cleanup_doc2001(void)
-{
- struct mtd_info *mtd;
- struct DiskOnChip *this;
-
- while((mtd=docmillist)) {
- this = (struct DiskOnChip *)mtd->priv;
- docmillist = this->nextdoc;
-
- del_mtd_device(mtd);
-
- iounmap((void *)this->virtadr);
- kfree(this->chips);
- kfree(mtd);
- }
- inter_module_unregister(im_name);
-}
-
-module_exit(cleanup_doc2001);
-module_init(init_doc2001);
-
-
+++ /dev/null
-/*
- * ECC algorithm for M-systems disk on chip. We use the excellent Reed
- * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
- * GNU GPL License. The rest is simply to convert the disk on chip
- * syndrom into a standard syndom.
- *
- * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
- * Copyright (C) 2000 Netgem S.A.
- *
- * $Id: docecc.c,v 1.1 2000/11/03 12:43:43 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
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/doc2000.h>
-
-/* need to undef it (from asm/termbits.h) */
-#undef B0
-
-#define MM 10 /* Symbol size in bits */
-#define KK (1023-4) /* Number of data symbols per block */
-#define B0 510 /* First root of generator polynomial, alpha form */
-#define PRIM 1 /* power of alpha used to generate roots of generator poly */
-#define NN ((1 << MM) - 1)
-
-typedef unsigned short dtype;
-
-/* 1+x^3+x^10 */
-static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
-
-/* This defines the type used to store an element of the Galois Field
- * used by the code. Make sure this is something larger than a char if
- * if anything larger than GF(256) is used.
- *
- * Note: unsigned char will work up to GF(256) but int seems to run
- * faster on the Pentium.
- */
-typedef int gf;
-
-/* No legal value in index form represents zero, so
- * we need a special value for this purpose
- */
-#define A0 (NN)
-
-/* Compute x % NN, where NN is 2**MM - 1,
- * without a slow divide
- */
-static inline gf
-modnn(int x)
-{
- while (x >= NN) {
- x -= NN;
- x = (x >> MM) + (x & NN);
- }
- return x;
-}
-
-#define min(a,b) ((a) < (b) ? (a) : (b))
-
-#define CLEAR(a,n) {\
-int ci;\
-for(ci=(n)-1;ci >=0;ci--)\
-(a)[ci] = 0;\
-}
-
-#define COPY(a,b,n) {\
-int ci;\
-for(ci=(n)-1;ci >=0;ci--)\
-(a)[ci] = (b)[ci];\
-}
-
-#define COPYDOWN(a,b,n) {\
-int ci;\
-for(ci=(n)-1;ci >=0;ci--)\
-(a)[ci] = (b)[ci];\
-}
-
-#define Ldec 1
-
-/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
- lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
- polynomial form -> index form index_of[j=alpha**i] = i
- alpha=2 is the primitive element of GF(2**m)
- HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
- Let @ represent the primitive element commonly called "alpha" that
- is the root of the primitive polynomial p(x). Then in GF(2^m), for any
- 0 <= i <= 2^m-2,
- @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
- where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
- of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
- example the polynomial representation of @^5 would be given by the binary
- representation of the integer "alpha_to[5]".
- Similarily, index_of[] can be used as follows:
- As above, let @ represent the primitive element of GF(2^m) that is
- the root of the primitive polynomial p(x). In order to find the power
- of @ (alpha) that has the polynomial representation
- a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
- we consider the integer "i" whose binary representation with a(0) being LSB
- and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
- "index_of[i]". Now, @^index_of[i] is that element whose polynomial
- representation is (a(0),a(1),a(2),...,a(m-1)).
- NOTE:
- The element alpha_to[2^m-1] = 0 always signifying that the
- representation of "@^infinity" = 0 is (0,0,0,...,0).
- Similarily, the element index_of[0] = A0 always signifying
- that the power of alpha which has the polynomial representation
- (0,0,...,0) is "infinity".
-
-*/
-
-static void
-generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
-{
- register int i, mask;
-
- mask = 1;
- Alpha_to[MM] = 0;
- for (i = 0; i < MM; i++) {
- Alpha_to[i] = mask;
- Index_of[Alpha_to[i]] = i;
- /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
- if (Pp[i] != 0)
- Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
- mask <<= 1; /* single left-shift */
- }
- Index_of[Alpha_to[MM]] = MM;
- /*
- * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
- * poly-repr of @^i shifted left one-bit and accounting for any @^MM
- * term that may occur when poly-repr of @^i is shifted.
- */
- mask >>= 1;
- for (i = MM + 1; i < NN; i++) {
- if (Alpha_to[i - 1] >= mask)
- Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
- else
- Alpha_to[i] = Alpha_to[i - 1] << 1;
- Index_of[Alpha_to[i]] = i;
- }
- Index_of[0] = A0;
- Alpha_to[NN] = 0;
-}
-
-/*
- * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
- * of the feedback shift register after having processed the data and
- * the ECC.
- *
- * Return number of symbols corrected, or -1 if codeword is illegal
- * or uncorrectable. If eras_pos is non-null, the detected error locations
- * are written back. NOTE! This array must be at least NN-KK elements long.
- * The corrected data are written in eras_val[]. They must be xor with the data
- * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
- *
- * First "no_eras" erasures are declared by the calling program. Then, the
- * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
- * If the number of channel errors is not greater than "t_after_eras" the
- * transmitted codeword will be recovered. Details of algorithm can be found
- * in R. Blahut's "Theory ... of Error-Correcting Codes".
-
- * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
- * will result. The decoder *could* check for this condition, but it would involve
- * extra time on every decoding operation.
- * */
-static int
-eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
- gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
- int no_eras)
-{
- int deg_lambda, el, deg_omega;
- int i, j, r,k;
- gf u,q,tmp,num1,num2,den,discr_r;
- gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
- * and syndrome poly */
- gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
- gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
- int syn_error, count;
-
- syn_error = 0;
- for(i=0;i<NN-KK;i++)
- syn_error |= bb[i];
-
- if (!syn_error) {
- /* if remainder is zero, data[] is a codeword and there are no
- * errors to correct. So return data[] unmodified
- */
- count = 0;
- goto finish;
- }
-
- for(i=1;i<=NN-KK;i++){
- s[i] = bb[0];
- }
- for(j=1;j<NN-KK;j++){
- if(bb[j] == 0)
- continue;
- tmp = Index_of[bb[j]];
-
- for(i=1;i<=NN-KK;i++)
- s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
- }
-
- /* undo the feedback register implicit multiplication and convert
- syndromes to index form */
-
- for(i=1;i<=NN-KK;i++) {
- tmp = Index_of[s[i]];
- if (tmp != A0)
- tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
- s[i] = tmp;
- }
-
- CLEAR(&lambda[1],NN-KK);
- lambda[0] = 1;
-
- if (no_eras > 0) {
- /* Init lambda to be the erasure locator polynomial */
- lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
- for (i = 1; i < no_eras; i++) {
- u = modnn(PRIM*eras_pos[i]);
- for (j = i+1; j > 0; j--) {
- tmp = Index_of[lambda[j - 1]];
- if(tmp != A0)
- lambda[j] ^= Alpha_to[modnn(u + tmp)];
- }
- }
-#if DEBUG >= 1
- /* Test code that verifies the erasure locator polynomial just constructed
- Needed only for decoder debugging. */
-
- /* find roots of the erasure location polynomial */
- for(i=1;i<=no_eras;i++)
- reg[i] = Index_of[lambda[i]];
- count = 0;
- for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
- q = 1;
- for (j = 1; j <= no_eras; j++)
- if (reg[j] != A0) {
- reg[j] = modnn(reg[j] + j);
- q ^= Alpha_to[reg[j]];
- }
- if (q != 0)
- continue;
- /* store root and error location number indices */
- root[count] = i;
- loc[count] = k;
- count++;
- }
- if (count != no_eras) {
- printf("\n lambda(x) is WRONG\n");
- count = -1;
- goto finish;
- }
-#if DEBUG >= 2
- printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
- for (i = 0; i < count; i++)
- printf("%d ", loc[i]);
- printf("\n");
-#endif
-#endif
- }
- for(i=0;i<NN-KK+1;i++)
- b[i] = Index_of[lambda[i]];
-
- /*
- * Begin Berlekamp-Massey algorithm to determine error+erasure
- * locator polynomial
- */
- r = no_eras;
- el = no_eras;
- while (++r <= NN-KK) { /* r is the step number */
- /* Compute discrepancy at the r-th step in poly-form */
- discr_r = 0;
- for (i = 0; i < r; i++){
- if ((lambda[i] != 0) && (s[r - i] != A0)) {
- discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
- }
- }
- discr_r = Index_of[discr_r]; /* Index form */
- if (discr_r == A0) {
- /* 2 lines below: B(x) <-- x*B(x) */
- COPYDOWN(&b[1],b,NN-KK);
- b[0] = A0;
- } else {
- /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
- t[0] = lambda[0];
- for (i = 0 ; i < NN-KK; i++) {
- if(b[i] != A0)
- t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
- else
- t[i+1] = lambda[i+1];
- }
- if (2 * el <= r + no_eras - 1) {
- el = r + no_eras - el;
- /*
- * 2 lines below: B(x) <-- inv(discr_r) *
- * lambda(x)
- */
- for (i = 0; i <= NN-KK; i++)
- b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
- } else {
- /* 2 lines below: B(x) <-- x*B(x) */
- COPYDOWN(&b[1],b,NN-KK);
- b[0] = A0;
- }
- COPY(lambda,t,NN-KK+1);
- }
- }
-
- /* Convert lambda to index form and compute deg(lambda(x)) */
- deg_lambda = 0;
- for(i=0;i<NN-KK+1;i++){
- lambda[i] = Index_of[lambda[i]];
- if(lambda[i] != A0)
- deg_lambda = i;
- }
- /*
- * Find roots of the error+erasure locator polynomial by Chien
- * Search
- */
- COPY(®[1],&lambda[1],NN-KK);
- count = 0; /* Number of roots of lambda(x) */
- for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
- q = 1;
- for (j = deg_lambda; j > 0; j--){
- if (reg[j] != A0) {
- reg[j] = modnn(reg[j] + j);
- q ^= Alpha_to[reg[j]];
- }
- }
- if (q != 0)
- continue;
- /* store root (index-form) and error location number */
- root[count] = i;
- loc[count] = k;
- /* If we've already found max possible roots,
- * abort the search to save time
- */
- if(++count == deg_lambda)
- break;
- }
- if (deg_lambda != count) {
- /*
- * deg(lambda) unequal to number of roots => uncorrectable
- * error detected
- */
- count = -1;
- goto finish;
- }
- /*
- * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
- * x**(NN-KK)). in index form. Also find deg(omega).
- */
- deg_omega = 0;
- for (i = 0; i < NN-KK;i++){
- tmp = 0;
- j = (deg_lambda < i) ? deg_lambda : i;
- for(;j >= 0; j--){
- if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
- tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
- }
- if(tmp != 0)
- deg_omega = i;
- omega[i] = Index_of[tmp];
- }
- omega[NN-KK] = A0;
-
- /*
- * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
- * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
- */
- for (j = count-1; j >=0; j--) {
- num1 = 0;
- for (i = deg_omega; i >= 0; i--) {
- if (omega[i] != A0)
- num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
- }
- num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
- den = 0;
-
- /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
- for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
- if(lambda[i+1] != A0)
- den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
- }
- if (den == 0) {
-#if DEBUG >= 1
- printf("\n ERROR: denominator = 0\n");
-#endif
- /* Convert to dual- basis */
- count = -1;
- goto finish;
- }
- /* Apply error to data */
- if (num1 != 0) {
- eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
- } else {
- eras_val[j] = 0;
- }
- }
- finish:
- for(i=0;i<count;i++)
- eras_pos[i] = loc[i];
- return count;
-}
-
-/***************************************************************************/
-/* The DOC specific code begins here */
-
-#define SECTOR_SIZE 512
-/* The sector bytes are packed into NB_DATA MM bits words */
-#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
-
-/*
- * Correct the errors in 'sector[]' by using 'ecc1[]' which is the
- * content of the feedback shift register applyied to the sector and
- * the ECC. Return the number of errors corrected (and correct them in
- * sector), or -1 if error
- */
-int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
-{
- int parity, i, nb_errors;
- gf bb[NN - KK + 1];
- gf error_val[NN-KK];
- int error_pos[NN-KK], pos, bitpos, index, val;
- dtype *Alpha_to, *Index_of;
-
- /* init log and exp tables here to save memory. However, it is slower */
- Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
- if (!Alpha_to)
- return -1;
-
- Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
- if (!Index_of) {
- kfree(Alpha_to);
- return -1;
- }
-
- generate_gf(Alpha_to, Index_of);
-
- parity = ecc1[1];
-
- bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
- bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
- bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
- bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
-
- nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
- error_val, error_pos, 0);
- if (nb_errors <= 0)
- goto the_end;
-
- /* correct the errors */
- for(i=0;i<nb_errors;i++) {
- pos = error_pos[i];
- if (pos >= NB_DATA && pos < KK) {
- nb_errors = -1;
- goto the_end;
- }
- if (pos < NB_DATA) {
- /* extract bit position (MSB first) */
- pos = 10 * (NB_DATA - 1 - pos) - 6;
- /* now correct the following 10 bits. At most two bytes
- can be modified since pos is even */
- index = (pos >> 3) ^ 1;
- bitpos = pos & 7;
- if ((index >= 0 && index < SECTOR_SIZE) ||
- index == (SECTOR_SIZE + 1)) {
- val = error_val[i] >> (2 + bitpos);
- parity ^= val;
- if (index < SECTOR_SIZE)
- sector[index] ^= val;
- }
- index = ((pos >> 3) + 1) ^ 1;
- bitpos = (bitpos + 10) & 7;
- if (bitpos == 0)
- bitpos = 8;
- if ((index >= 0 && index < SECTOR_SIZE) ||
- index == (SECTOR_SIZE + 1)) {
- val = error_val[i] << (8 - bitpos);
- parity ^= val;
- if (index < SECTOR_SIZE)
- sector[index] ^= val;
- }
- }
- }
-
- /* use parity to test extra errors */
- if ((parity & 0xff) != 0)
- nb_errors = -1;
-
- the_end:
- kfree(Alpha_to);
- kfree(Index_of);
- return nb_errors;
-}
-
+++ /dev/null
-
-/* Linux driver for Disk-On-Chip devices */
-/* Probe routines common to all DoC devices */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $ */
-
-
-
-/* DOC_PASSIVE_PROBE:
- In order to ensure that the BIOS checksum is correct at boot time, and
- hence that the onboard BIOS extension gets executed, the DiskOnChip
- goes into reset mode when it is read sequentially: all registers
- return 0xff until the chip is woken up again by writing to the
- DOCControl register.
-
- Unfortunately, this means that the probe for the DiskOnChip is unsafe,
- because one of the first things it does is write to where it thinks
- the DOCControl register should be - which may well be shared memory
- for another device. I've had machines which lock up when this is
- attempted. Hence the possibility to do a passive probe, which will fail
- to detect a chip in reset mode, but is at least guaranteed not to lock
- the machine.
-
- If you have this problem, uncomment the following line:
-#define DOC_PASSIVE_PROBE
-*/
-
-
-/* DOC_SINGLE_DRIVER:
- Millennium driver has been merged into DOC2000 driver.
-
- The newly-merged driver doesn't appear to work for writing. It's the
- same with the DiskOnChip 2000 and the Millennium. If you have a
- Millennium and you want write support to work, remove the definition
- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
-
- Otherwise, it's left on in the hope that it'll annoy someone with
- a Millennium enough that they go through and work out what the
- difference is :)
-*/
-#define DOC_SINGLE_DRIVER
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/types.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/doc2000.h>
-
-/* Where to look for the devices? */
-#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
-#define CONFIG_MTD_DOCPROBE_ADDRESS 0
-#endif
-
-
-static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
-MODULE_PARM(doc_config_location, "l");
-
-
-static unsigned long __initdata doc_locations[] = {
-#if defined (__alpha__) || defined(__i386__)
-#ifdef CONFIG_MTD_DOCPROBE_HIGH
- 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
- 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
- 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
- 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
- 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
-#else /* CONFIG_MTD_DOCPROBE_HIGH */
- 0xc8000, 0xca000, 0xcc000, 0xce000,
- 0xd0000, 0xd2000, 0xd4000, 0xd6000,
- 0xd8000, 0xda000, 0xdc000, 0xde000,
- 0xe0000, 0xe2000, 0xe4000, 0xe6000,
- 0xe8000, 0xea000, 0xec000, 0xee000,
-#endif /* CONFIG_MTD_DOCPROBE_HIGH */
-#elif defined(__ppc__)
- 0xe4000000,
-#else
-#warning Unknown architecture for DiskOnChip. No default probe locations defined
-#endif
- 0 };
-
-/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
-
-static inline int __init doccheck(unsigned long potential, unsigned long physadr)
-{
- unsigned long window=potential;
- unsigned char tmp, ChipID;
-#ifndef DOC_PASSIVE_PROBE
- unsigned char tmp2;
-#endif
-
- /* Routine copied from the Linux DOC driver */
-
-#ifdef CONFIG_MTD_DOCPROBE_55AA
- /* Check for 0x55 0xAA signature at beginning of window,
- this is no longer true once we remove the IPL (for Millennium */
- if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
- return 0;
-#endif /* CONFIG_MTD_DOCPROBE_55AA */
-
-#ifndef DOC_PASSIVE_PROBE
- /* It's not possible to cleanly detect the DiskOnChip - the
- * bootup procedure will put the device into reset mode, and
- * it's not possible to talk to it without actually writing
- * to the DOCControl register. So we store the current contents
- * of the DOCControl register's location, in case we later decide
- * that it's not a DiskOnChip, and want to put it back how we
- * found it.
- */
- tmp2 = ReadDOC(window, DOCControl);
-
- /* Reset the DiskOnChip ASIC */
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
- window, DOCControl);
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
- window, DOCControl);
-
- /* Enable the DiskOnChip ASIC */
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
- window, DOCControl);
- WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
- window, DOCControl);
-#endif /* !DOC_PASSIVE_PROBE */
-
- ChipID = ReadDOC(window, ChipID);
-
- switch (ChipID) {
- case DOC_ChipID_Doc2k:
- /* Check the TOGGLE bit in the ECC register */
- tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp)
- return ChipID;
- break;
-
- case DOC_ChipID_DocMil:
- /* Check the TOGGLE bit in the ECC register */
- tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp)
- return ChipID;
- break;
-
- default:
-#ifndef CONFIG_MTD_DOCPROBE_55AA
- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
- ChipID, physadr);
-#endif
-#ifndef DOC_PASSIVE_PROBE
- /* Put back the contents of the DOCControl register, in case it's not
- * actually a DiskOnChip.
- */
- WriteDOC(tmp2, window, DOCControl);
-#endif
- return 0;
- }
-
- printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n");
-
-#ifndef DOC_PASSIVE_PROBE
- /* Put back the contents of the DOCControl register: it's not a DiskOnChip */
- WriteDOC(tmp2, window, DOCControl);
-#endif
- return 0;
-}
-
-
-static void DoC_Probe(unsigned long physadr)
-{
- unsigned long docptr;
- struct DiskOnChip *this;
- struct mtd_info *mtd;
- int ChipID;
- char namebuf[15];
- char *name = namebuf;
- char *im_funcname = NULL;
- char *im_modname = NULL;
- void (*initroutine)(struct mtd_info *) = NULL;
-
- docptr = (unsigned long)ioremap(physadr, 0x2000);
-
- if (!docptr)
- return;
-
- if ((ChipID = doccheck(docptr, physadr))) {
-
- mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
-
- if (!mtd) {
- printk("Cannot allocate memory for data structures. Dropping.\n");
- iounmap((void *)docptr);
- return;
- }
-
- this = (struct DiskOnChip *)(&mtd[1]);
-
- memset((char *)mtd,0, sizeof(struct mtd_info));
- memset((char *)this, 0, sizeof(struct DiskOnChip));
-
- mtd->priv = this;
- this->virtadr = docptr;
- this->physadr = physadr;
- this->ChipID = ChipID;
- sprintf(namebuf, "with ChipID %2.2X", ChipID);
-
- switch(ChipID) {
- case DOC_ChipID_Doc2k:
- name="2000";
- im_funcname = "DoC2k_init";
- im_modname = "doc2000";
- break;
-
- case DOC_ChipID_DocMil:
- name="Millennium";
-#ifdef DOC_SINGLE_DRIVER
- im_funcname = "DoC2k_init";
- im_modname = "doc2000";
-#else
- im_funcname = "DoCMil_init";
- im_modname = "doc2001";
-#endif /* DOC_SINGLE_DRIVER */
- break;
- }
-
- if (im_funcname)
- initroutine = inter_module_get_request(im_funcname, im_modname);
-
- if (initroutine) {
- (*initroutine)(mtd);
- inter_module_put(im_funcname);
- return;
- }
- printk("Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
- }
- iounmap((void *)docptr);
-}
-
-
-/****************************************************************************
- *
- * Module stuff
- *
- ****************************************************************************/
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_doc init_module
-#endif
-
-int __init init_doc(void)
-{
- int i;
-
- 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");
-#endif
- if (doc_config_location) {
- printk("Using configured probe address 0x%lx\n", doc_config_location);
- DoC_Probe(doc_config_location);
- } else {
- for (i=0; doc_locations[i]; i++) {
- DoC_Probe(doc_locations[i]);
- }
- }
- /* So it looks like we've been used and we get unloaded */
- MOD_INC_USE_COUNT;
- MOD_DEC_USE_COUNT;
- return 0;
-
-}
-
-module_init(init_doc);
-
/* 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 <acme@conectiva.com.br>
+ * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
+ *
+ * Based on:
*/
/*======================================================================
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
contact M-Systems (http://www.m-sys.com) directly.
======================================================================*/
-#define FTL_DEBUG 5
-#ifdef FTL_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/module.h>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/mtd.h>
/*====================================================================*/
/* 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"
#include <linux/blk.h>
-#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
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
#endif
part: ftl_hd,
sizes: ftl_sizes,
- nr_real: 0
};
/*====================================================================*/
/* 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);
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;
}
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;
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++) {
(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) &&
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;
(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;
(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]))) {
}
}
- 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 */
/*======================================================================
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 {
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;
}
}
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) {
}
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;
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)
}
}
-
-#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)
{
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");
}
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_ftl);
module_exit(cleanup_ftl);
-#endif
+++ /dev/null
-
-/* JEDEC Flash Interface.
- * This is an older type of interface for self programming flash. It is
- * commonly use in older AMD chips and is obsolete compared with CFI.
- * It is called JEDEC because the JEDEC association distributes the ID codes
- * for the chips.
- *
- * See the AMD flash databook for information on how to operate the interface.
- *
- * This code does not support anything wider than 8 bit flash chips, I am
- * 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 $
- */
-
-#include <linux/mtd/jedec.h>
-
-struct mtd_info *jedec_probe(struct map_info *);
-int jedec_probe8(struct map_info *map,unsigned long base,
- struct jedec_private *priv);
-int jedec_probe16(struct map_info *map,unsigned long base,
- struct jedec_private *priv);
-int jedec_probe32(struct map_info *map,unsigned long base,
- struct jedec_private *priv);
-static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
- unsigned long len);
-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);
-
-/* 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},
- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH},
- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH},
- {}};
-
-static void jedec_sync(struct mtd_info *mtd) {};
-static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-
-/* Probe entry point */
- struct jedec_private priv;
- struct mtd_info __MTD;
-struct mtd_info *jedec_probe(struct map_info *map)
-{
- struct mtd_info *MTD = &__MTD;
- unsigned long Base;
- unsigned long SectorSize;
- unsigned count;
- unsigned I,Uniq;
- char Part[200];
- memset(&priv,0,sizeof(priv));
-
- if (map->bank_size == 0)
- map->bank_size = map->size;
-
- if (map->size/map->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)
- {
- // Perhaps zero could designate all tests?
- if (map->bus_width == 0)
- map->bus_width = 8;
-
- if (map->bus_width == 8)
- jedec_probe8(map,Base,&priv);
- if (map->bus_width == 16)
- jedec_probe16(map,Base,&priv);
- if (map->bus_width == 32)
- jedec_probe32(map,Base,&priv);
- }
-
- // Get the biggest sector size
- SectorSize = 0;
- for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- if (priv.chips[I].sectorsize > SectorSize)
- SectorSize = priv.chips[I].sectorsize;
- }
-
- // Quickly ensure that the other sector sizes are factors of the largest
- for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- if ((SectorSize/priv.chips[I].sectorsize)*priv.chips[I].sectorsize != SectorSize)
- {
- printk("mtd: Failed. Device has incompatible mixed sector sizes\n");
- return 0;
- }
- }
-
- /* Generate a part name that includes the number of different chips and
- other configuration information */
- count = 1;
- strncpy(Part,map->name,sizeof(Part)-10);
- Part[sizeof(Part)-11] = 0;
- strcat(Part," ");
- Uniq = 0;
- for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- const struct JEDECTable *JEDEC;
-
- if (priv.chips[I+1].jedec == priv.chips[I].jedec)
- {
- count++;
- continue;
- }
-
- // Locate the chip in the jedec table
- JEDEC = jedec_idtoinf(priv.chips[I].jedec >> 8,priv.chips[I].jedec);
- if (JEDEC == 0)
- {
- printk("mtd: Internal Error, JEDEC not set\n");
- return 0;
- }
-
- if (Uniq != 0)
- strcat(Part,",");
- Uniq++;
-
- if (count != 1)
- sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name);
- else
- sprintf(Part+strlen(Part),"%s",JEDEC->name);
- if (strlen(Part) > sizeof(Part)*2/3)
- break;
- count = 1;
- }
-
- /* Determine if the chips are organized in a linear fashion, or if there
- 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;
- 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;
- }
- }
- if (priv.is_banked == 1)
- strcat(Part,", banked");
-
- xprintf("Part: '%s'\n",Part);
-
- memset(MTD,0,sizeof(*MTD));
- strncpy(MTD->name,Part,sizeof(MTD->name));
- MTD->name[sizeof(MTD->name)-1] = 0;
- MTD->type = MTD_NORFLASH;
- MTD->flags = MTD_CAP_NORFLASH;
- MTD->erasesize = SectorSize*(map->bus_width/8);
- MTD->size = priv.size;
- //MTD->module = THIS_MODULE; // ? Maybe this should be the low level module?
- MTD->erase = flash_erase;
- if (priv.is_banked == 1)
- MTD->read = jedec_read_banked;
- else
- MTD->read = jedec_read;
- MTD->write = flash_write;
- MTD->sync = jedec_sync;
- MTD->priv = map;
- map->fldrv_priv = &priv;
-
- return MTD;
-}
-
-/* Helper for the JEDEC function, JEDEC numbers all have odd parity */
-static int checkparity(u_char C)
-{
- u_char parity = 0;
- while (C != 0)
- {
- parity ^= C & 1;
- C >>= 1;
- }
-
- return parity == 1;
-}
-
-
-/* Take an array of JEDEC numbers that represent interleved flash chips
- and process them. Check to make sure they are good JEDEC numbers, look
- them up and then add them to the chip list */
-int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count,
- unsigned long base,struct jedec_private *priv)
-{
- unsigned I,J;
- unsigned long Size;
- unsigned long SectorSize;
- const struct JEDECTable *JEDEC;
-
- // Test #2 JEDEC numbers exhibit odd parity
- for (I = 0; I != Count; I++)
- {
- if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0)
- return 0;
- }
-
- // Finally, just make sure all the chip sizes are the same
- JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
-
- if (JEDEC == 0)
- {
- printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
- return 0;
- }
-
- Size = JEDEC->size;
- SectorSize = JEDEC->sectorsize;
- for (I = 0; I != Count; I++)
- {
- JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
- if (JEDEC == 0)
- {
- printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
- return 0;
- }
-
- if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize)
- {
- printk("mtd: Failed. Interleved flash does not have matching characteristics\n");
- return 0;
- }
- }
-
- // Load the Chips
- for (I = 0; I != MAX_JEDEC_CHIPS; I++)
- {
- if (priv->chips[I].jedec == 0)
- break;
- }
-
- if (I + Count > MAX_JEDEC_CHIPS)
- {
- printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n");
- return 0;
- }
-
- // Add them to the table
- for (J = 0; J != Count; J++)
- {
- unsigned long Bank;
-
- JEDEC = jedec_idtoinf(Mfg[J],Id[J]);
- priv->chips[I].jedec = (Mfg[J] << 8) | Id[J];
- priv->chips[I].size = JEDEC->size;
- priv->chips[I].sectorsize = JEDEC->sectorsize;
- priv->chips[I].base = base + J;
- priv->chips[I].datashift = J*8;
- priv->chips[I].capabilities = JEDEC->capabilities;
- priv->chips[I].offset = priv->size + J;
-
- // log2 n :|
- priv->chips[I].addrshift = 0;
- 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 +
- (JEDEC->size << priv->chips[I].addrshift) - Bank)
- priv->bank_fill[Bank/map->bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank;
- I++;
- }
-
- priv->size += priv->chips[I-1].size*Count;
-
- return priv->chips[I-1].size;
-}
-
-/* Lookup the chip information from the JEDEC ID table. */
-const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id)
-{
- __u16 Id = (mfr << 8) | id;
- unsigned long I = 0;
- for (I = 0; JEDEC_table[I].jedec != 0; I++)
- if (JEDEC_table[I].jedec == Id)
- return JEDEC_table + I;
- return 0;
-}
-
-// Look for flash using an 8 bit bus interface
-int jedec_probe8(struct map_info *map,unsigned long base,
- struct jedec_private *priv)
-{
- return 0;
-}
-
-// Look for flash using a 16 bit bus interface (ie 2 8-bit chips)
-int jedec_probe16(struct map_info *map,unsigned long base,
- struct jedec_private *priv)
-{
- return 0;
-}
-
-// Look for flash using a 32 bit bus interface (ie 4 8-bit chips)
-int jedec_probe32(struct map_info *map,unsigned long base,
- struct jedec_private *priv)
-{
- #define flread(x) map->read32(map,base+((x)<<2))
- #define flwrite(v,x) map->write32(map,v,base+((x)<<2))
-
- const unsigned long AutoSel1 = 0xAAAAAAAA;
- const unsigned long AutoSel2 = 0x55555555;
- const unsigned long AutoSel3 = 0x90909090;
- const unsigned long Reset = 0x90909090;
- __u32 OldVal;
- __u8 Mfg[4];
- __u8 Id[4];
- 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);
-
- // Test #1, JEDEC numbers are readable from 0x??00/0x??01
- if (flread(0) != flread(0x100) ||
- flread(1) != flread(0x101))
- {
- flwrite(Reset,0x555);
- return 0;
- }
-
- // Split up the JEDEC numbers
- OldVal = flread(0);
- for (I = 0; I != 4; I++)
- Mfg[I] = (OldVal >> (I*8));
- OldVal = flread(1);
- for (I = 0; I != 4; I++)
- Id[I] = (OldVal >> (I*8));
-
- Size = handle_jedecs(map,Mfg,Id,4,base,priv);
- if (Size == 0)
- {
- flwrite(Reset,0x555);
- return 0;
- }
-
- /* Check if there is address wrap around within a single bank, if this
- returns JEDEC numbers then we assume that it is wrap around. Notice
- we call this routine with the JEDEC return still enabled, if two or
- 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)
- {
- if (flread(base+Size) != flread(base+Size + 0x100) ||
- flread(base+Size + 1) != flread(base+Size + 0x101))
- {
- jedec_probe32(map,base+Size,priv);
- }
- }
-
- // Reset.
- flwrite(0xF0F0F0F0,0x555);
-
- return 1;
-
- #undef flread
- #undef flwrite
-}
-
-/* Linear read. */
-static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- struct map_info *map = (struct map_info *)mtd->priv;
-
- map->copy_from(map, buf, from, len);
- *retlen = len;
- return 0;
-}
-
-/* Banked read. Take special care to jump past the holes in the bank
- mapping. This version assumes symetry in the holes.. */
-static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- struct map_info *map = (struct map_info *)mtd->priv;
- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
-
- *retlen = 0;
- while (len > 0)
- {
- // Determine what bank and offset into that bank the first byte is
- unsigned long bank = from & (~(priv->bank_fill[0]-1));
- unsigned long offset = from & (priv->bank_fill[0]-1);
- unsigned long get = len;
- if (priv->bank_fill[0] - offset < len)
- get = priv->bank_fill[0] - offset;
-
- bank /= priv->bank_fill[0];
- map->copy_from(map,buf + *retlen,bank*map->bank_size + offset,get);
-
- len -= get;
- *retlen += get;
- from += get;
- }
- return 0;
-}
-
-/* Pass the flags value that the flash return before it re-entered read
- mode. */
-static void jedec_flash_failed(unsigned char code)
-{
- /* Bit 5 being high indicates that there was an internal device
- failure, erasure time limits exceeded or something */
- if ((code & (1 << 5)) != 0)
- {
- printk("mtd: Internal Flash failure\n");
- return;
- }
- printk("mtd: Programming didn't take\n");
-}
-
-/* This uses the erasure function described in the AMD Flash Handbook,
- it will work for flashes with a fixed sector size only. Flashes with
- a selection of sector sizes (ie the AMD Am29F800B) will need a different
- routine. This routine tries to parallize erasing multiple chips/sectors
- where possible */
-static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- // Does IO to the currently selected chip
- #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift))
- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
-
- unsigned long Time = 0;
- unsigned long NoTime = 0;
- unsigned long start = instr->addr, len = instr->len;
- unsigned int I;
- struct map_info *map = (struct map_info *)mtd->priv;
- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
-
- // Verify the arguments..
- if (start + len > mtd->size ||
- (start % mtd->erasesize) != 0 ||
- (len % mtd->erasesize) != 0 ||
- (len/mtd->erasesize) == 0)
- return -EINVAL;
-
- jedec_flash_chip_scan(priv,start,len);
-
- // Start the erase sequence on each chip
- for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- unsigned long off;
- struct jedec_flash_chip *chip = priv->chips + I;
-
- 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");
- return -EIO;
- }
-
- flwrite(0xF0,chip->start + 0x555);
- flwrite(0xAA,chip->start + 0x555);
- flwrite(0x55,chip->start + 0x2AA);
- flwrite(0x80,chip->start + 0x555);
- 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)
- {
- // Check to make sure we didn't timeout
- flwrite(0x30,chip->start + off);
- if (off == 0)
- continue;
- if ((flread(chip->start + off) & (1 << 3)) != 0)
- {
- printk("mtd: Ack! We timed out the erase timer!\n");
- return -EIO;
- }
- }
-// restore_flags(flags);
- }
-
- /* We could split this into a timer routine and return early, performing
- background erasure.. Maybe later if the need warrents */
-
- /* Poll the flash for erasure completion, specs say this can take as long
- as 480 seconds to do all the sectors (for a 2 meg flash).
- Erasure time is dependant on chip age, temp and wear.. */
-
- /* This being a generic routine assumes a 32 bit bus. It does read32s
- and bundles interleved chips into the same grouping. This will work
- for all bus widths */
- Time = 0;
- NoTime = 0;
- for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- struct jedec_flash_chip *chip = priv->chips + I;
- unsigned long off = 0;
- unsigned todo[4] = {0,0,0,0};
- unsigned todo_left = 0;
- unsigned J;
-
- if (chip->length == 0)
- continue;
-
- /* Find all chips in this data line, realistically this is all
- or nothing up to the interleve count */
- for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
- {
- if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
- (chip->base & (~((1<<chip->addrshift)-1))))
- {
- todo_left++;
- todo[priv->chips[J].base & ((1<<chip->addrshift)-1)] = 1;
- }
- }
-
- xprintf("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1],
- (short)todo[2],(short)todo[3]);
-
- while (1)
- {
- __u32 Last[4];
- unsigned long Count = 0;
-
- /* 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);
- Count = 3;
- while (todo_left != 0)
- {
- for (J = 0; J != 4; J++)
- {
- __u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF;
- __u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF;
- __u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF;
- if (todo[J] == 0)
- continue;
-
- if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2)
- {
-// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2);
- continue;
- }
-
- if (Byte1 == Byte2)
- {
- jedec_flash_failed(Byte3);
- return -EIO;
- }
-
- todo[J] = 0;
- todo_left--;
- }
-
-/* 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);
- Count++;
-
- putc('.');
-
-/* // Count time, max of 15s per sector (according to AMD)
- if (Time > 15*len/mtd->erasesize*HZ)
- {
- printk("mtd: Flash Erase Timed out\n");
- return -EIO;
- } */
- }
-
- puts("out\n");
-
- // Skip to the next chip if we used chip erase
- if (chip->length == chip->size)
- off = chip->size;
- else
- off += chip->sectorsize;
-
- if (off >= chip->length)
- break;
- NoTime = 1;
- }
-
- for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
- {
- if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
- (chip->base & (~((1<<chip->addrshift)-1))))
- priv->chips[J].length = 0;
- }
- }
-
- puts("done\n");
- return 0;
-
- #undef flread
- #undef flwrite
-}
-
-/* This is the simple flash writing function. It writes to every byte, in
- sequence. It takes care of how to properly address the flash if
- the flash is interleved. It can only be used if all the chips in the
- array are identical!*/
-static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
- size_t *retlen, const u_char *buf)
-{
- /* Does IO to the currently selected chip. It takes the bank addressing
- base (which is divisable by the chip size) adds the necesary lower bits
- of addrshift (interleve index) and then adds the control register index. */
- #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
- #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
-
- struct map_info *map = (struct map_info *)mtd->priv;
- struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
- unsigned long base;
- unsigned long off;
-
- if (start + len > mtd->size)
- return -EIO;
-
- puts("Here");
-
- while (len != 0)
- {
- struct jedec_flash_chip *chip = priv->chips;
- unsigned long bank;
- unsigned long boffset;
-
- // Compute the base of the flash.
- off = 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;
- base = bank + boffset;
-
- xprintf("Flasing %X %X %X\n",base,chip->size,len);
-
- // Loop over this page
- for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
- {
- unsigned char oldbyte = map->read8(map,base+off);
- unsigned char Last[4];
- unsigned long Count = 0;
-
-// putc('.');
-
- if (oldbyte == *buf)
- continue;
- if (((~oldbyte) & *buf) != 0)
- printk("mtd: warn: Trying to set a 0 to a 1\n");
-
- // Write
- flwrite(0xAA,0x555);
- flwrite(0x55,0x2AA);
- flwrite(0xA0,0x555);
- map->write8(map,*buf,base + off);
- Last[0] = map->read8(map,base + off);
- Last[1] = map->read8(map,base + off);
- Last[2] = map->read8(map,base + off);
-
- /* Wait for the flash to finish the operation. We store the last 4
- status bytes that have been retrieved so we can determine why
- it failed. The toggle bits keep toggling when there is a
- failure */
- for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
- Count < 10000; Count++)
- Last[Count % 4] = map->read8(map,base + off);
- if (Last[(Count - 1) % 4] != *buf)
- {
- jedec_flash_failed(Last[(Count - 3) % 4]);
- return -EIO;
- }
- }
- }
- *retlen = len;
- return 0;
-}
-
-/* This is used to enhance the speed of the erase routine,
- when things are being done to multiple chips it is possible to
- parallize the operations, particularly full memory erases of multi
- chip memories benifit */
-static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
- unsigned long len)
-{
- unsigned int I;
-
- // Zero the records
- for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- priv->chips[I].start = priv->chips[I].length = 0;
-
- // Intersect the region with each chip
- for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
- {
- struct jedec_flash_chip *chip = priv->chips + I;
- unsigned long ByteStart;
- unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift);
-
- // End is before this chip or the start is after it
- if (start+len < chip->offset ||
- ChipEndByte - (1 << chip->addrshift) < start)
- continue;
-
- if (start < chip->offset)
- {
- ByteStart = chip->offset;
- chip->start = 0;
- }
- else
- {
- chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift;
- ByteStart = start;
- }
-
- if (start + len >= ChipEndByte)
- chip->length = (ChipEndByte - ByteStart) >> chip->addrshift;
- else
- chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift;
- }
-}
- /*}}}*/
+++ /dev/null
-/*
- * 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 $
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-
-#include <linux/mtd/map.h>
-
-
-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 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 <kaos@ocs.com.au> 29 Oct 2000.
- */
-
-static struct mtd_info *map_ram_probe(struct map_info *map)
-{
- struct mtd_info *mtd;
-
- /* Check the first byte is RAM */
- map->write8(map, 0x55, 0);
- if (map->read8(map, 0) != 0x55)
- return NULL;
-
- map->write8(map, 0xAA, 0);
- if (map->read8(map, 0) != 0xAA)
- return NULL;
-
- /* Check the last byte is RAM */
- map->write8(map, 0x55, map->size-1);
- if (map->read8(map, map->size-1) != 0x55)
- return NULL;
-
- map->write8(map, 0xAA, map->size-1);
- if (map->read8(map, map->size-1) != 0xAA)
- return NULL;
-
- /* OK. It seems to be RAM. */
-
- mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd)
- return NULL;
-
- memset(mtd, 0, sizeof(*mtd));
-
- map->im_name = im_name;
- map->fldrv_destroy = mapram_nop;
- mtd->priv = map;
- mtd->name = map->name;
- mtd->type = MTD_RAM;
- mtd->erasesize = 0x10000;
- mtd->size = map->size;
- mtd->erase = mapram_erase;
- mtd->read = mapram_read;
- mtd->write = mapram_write;
- mtd->sync = mapram_nop;
- mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
- mtd->erasesize = PAGE_SIZE;
-
- return mtd;
-}
-
-
-static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- struct map_info *map = (struct map_info *)mtd->priv;
-
- map->copy_from(map, buf, from, len);
- *retlen = len;
- return 0;
-}
-
-static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
- struct map_info *map = (struct map_info *)mtd->priv;
-
- map->copy_to(map, to, buf, len);
- *retlen = len;
- return 0;
-}
-
-static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- /* Yeah, it's inefficient. Who cares? It's faster than a _real_
- flash erase. */
- struct map_info *map = (struct map_info *)mtd->priv;
- unsigned long i;
-
- for (i=0; i<instr->len; i++)
- map->write8(map, 0xFF, instr->addr + i);
-
- if (instr->callback)
- instr->callback(instr);
-
- return 0;
-}
-
-static void mapram_nop(struct mtd_info *mtd)
-{
- /* Nothing to see here */
-}
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define map_ram_init init_module
-#define map_ram_exit cleanup_module
-#endif
-
-static int __init map_ram_init(void)
-{
- inter_module_register(im_name, THIS_MODULE, &map_ram_probe);
- return 0;
-}
-
-static void __exit map_ram_exit(void)
-{
- inter_module_unregister(im_name);
-}
-
-module_init(map_ram_init);
-module_exit(map_ram_exit);
+++ /dev/null
-/*
- * 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 $
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-
-#include <linux/mtd/map.h>
-
-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 *);
-
-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 <kaos@ocs.com.au> 29 Oct 2000.
- */
-
-struct mtd_info *map_rom_probe(struct map_info *map)
-{
- struct mtd_info *mtd;
-
- mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
- if (!mtd)
- return NULL;
-
- memset(mtd, 0, sizeof(*mtd));
-
- map->im_name = im_name;
- map->fldrv_destroy = maprom_nop;
- mtd->priv = map;
- mtd->name = map->name;
- mtd->type = MTD_ROM;
- mtd->size = map->size;
- mtd->read = maprom_read;
- mtd->write = maprom_write;
- mtd->sync = maprom_nop;
- mtd->flags = MTD_CAP_ROM;
- mtd->erasesize = 131072;
-
- return mtd;
-}
-
-
-static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- struct map_info *map = (struct map_info *)mtd->priv;
-
- map->copy_from(map, buf, from, len);
- *retlen = len;
- return 0;
-}
-
-static void maprom_nop(struct mtd_info *mtd)
-{
- /* Nothing to see here */
-}
-
-static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
- printk(KERN_NOTICE "maprom_write called\n");
- return -EIO;
-}
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define map_rom_init init_module
-#define map_rom_exit cleanup_module
-#endif
-
-static int __init map_rom_init(void)
-{
- inter_module_register(im_name, THIS_MODULE, &map_rom_probe);
- return 0;
-}
-
-static void __exit map_rom_exit(void)
-{
- inter_module_unregister(im_name);
-}
-
-module_init(map_rom_init);
-module_exit(map_rom_exit);
--- /dev/null
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * Copyright © 2001 Flaga hf. Medical Devices, Kári DavÃðsson <kd@flaga.is>
+ *
+ * $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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+
+/* 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);
--- /dev/null
+/*
+ * $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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+
+#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+#include <linux/delay.h>
+#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;i<PHYSMAP_NUMBER;i++) {
+ printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
+ memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info));
+ cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
+ if (!cstm_mips_ixx_map[i].map_priv_1) {
+ printk(KERN_WARNING "Failed to ioremap\n");
+ return -EIO;
+ }
+ cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
+ cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
+ cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
+ //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1));
+ }
+
+#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ setup_ITE_IVR_flash();
+#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
+
+ for (i=0;i<PHYSMAP_NUMBER;i++) {
+ parts = &cstm_mips_ixx_partitions[i][0];
+ jedec = 0;
+ mymtd = (struct mtd_info *)do_map_probe("cfi", &cstm_mips_ixx_map[i]);
+ //printk(KERN_NOTICE "phymap %d cfi_probe: mymtd is %x\n",i,(unsigned int)mymtd);
+ if (!mymtd) {
+ jedec = 1;
+ mymtd = (struct mtd_info *)do_map_probe("jedec", &cstm_mips_ixx_map[i]);
+ printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
+ }
+ if (mymtd) {
+ mymtd->module = 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<PHYSMAP_NUMBER;i++) {
+ mymtd = (struct mtd_info *)cstm_mips_ixx_map[i].map_priv_2;
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+ if (cstm_mips_ixx_map[i].map_priv_1) {
+ iounmap((void *)cstm_mips_ixx_map[i].map_priv_1);
+ cstm_mips_ixx_map[i].map_priv_1 = 0;
+ }
+ }
+}
+#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+void PCISetULongByOffset(__u32 DevNumber, __u32 FuncNumber, __u32 Offset, __u32 data)
+{
+ __u32 offset;
+
+ offset = ( unsigned long )( 0x80000000 | ( DevNumber << 11 ) + ( FuncNumber << 8 ) + Offset) ;
+
+ *(__u32 *)CC_CONFADDR = offset;
+ *(__u32 *)CC_CONFDATA = data;
+}
+void setup_ITE_IVR_flash()
+{
+ __u32 size, base;
+
+ size = 0x0e000000; // 32MiB
+ base = (0x08000000) >> 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);
--- /dev/null
+/*
+ * $Id: dbox2-flash.c,v 1.2 2001/04/26 15:42:43 dwmw2 Exp $
+ *
+ * Nokia / Sagem D-Box 2 flash driver
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+
+/* 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);
+
--- /dev/null
+/*
+ * MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip)
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is GPL
+ *
+ * $Id: dc21285.c,v 1.4 2001/04/26 15:40:23 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/hardware/dec21285.h>
+
+
+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);
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#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);
--- /dev/null
+/*
+ * $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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+
+#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);
+
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+
+/*
+** 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);
--- /dev/null
+/*
+ * $Id: nora.c,v 1.19 2001/04/26 15:40:23 dwmw2 Exp $
+ *
+ * This is so simple I love it.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+
+#define WINDOW_ADDR 0xd0000000
+#define WINDOW_SIZE 0x04000000
+
+static struct mtd_info *mymtd;
+
+__u8 nora_read8(struct map_info *map, unsigned long ofs)
+{
+ return *(__u8 *)(WINDOW_ADDR + ofs);
+}
+
+__u16 nora_read16(struct map_info *map, unsigned long ofs)
+{
+ return *(__u16 *)(WINDOW_ADDR + ofs);
+}
+
+__u32 nora_read32(struct map_info *map, unsigned long ofs)
+{
+ return *(__u32 *)(WINDOW_ADDR + ofs);
+}
+
+void nora_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+ memcpy(to, (void *)(WINDOW_ADDR + from), len);
+}
+
+void nora_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ *(__u8 *)(WINDOW_ADDR + adr) = d;
+}
+
+void nora_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ *(__u16 *)(WINDOW_ADDR + adr) = d;
+}
+
+void nora_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ *(__u32 *)(WINDOW_ADDR + adr) = d;
+}
+
+void nora_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+ memcpy((void *)(WINDOW_ADDR + to), from, len);
+}
+
+struct map_info nora_map = {
+ name: "NORA",
+ size: WINDOW_SIZE,
+ buswidth: 2,
+ read8: nora_read8,
+ read16: nora_read16,
+ read32: nora_read32,
+ copy_from: nora_copy_from,
+ write8: nora_write8,
+ write16: nora_write16,
+ write32: nora_write32,
+ copy_to: nora_copy_to
+};
+
+
+static int nora_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+ return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf);
+}
+
+static int nora_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+{
+ return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf);
+}
+
+static int nora_mtd_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ instr->addr += (unsigned long)mtd->priv;
+ return mymtd->erase(mymtd, instr);
+}
+
+static void nora_mtd_sync (struct mtd_info *mtd)
+{
+ mymtd->sync(mymtd);
+}
+
+static int nora_mtd_suspend (struct mtd_info *mtd)
+{
+ return mymtd->suspend(mymtd);
+}
+
+static void nora_mtd_resume (struct mtd_info *mtd)
+{
+ mymtd->resume(mymtd);
+}
+
+
+static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */
+ {
+ type: MTD_NORFLASH,
+ flags: MTD_CAP_NORFLASH,
+ size: 0x60000,
+ erasesize: 0x20000,
+ name: "NORA boot firmware",
+ module: THIS_MODULE,
+ erase: nora_mtd_erase,
+ read: nora_mtd_read,
+ write: nora_mtd_write,
+ suspend: nora_mtd_suspend,
+ resume: nora_mtd_resume,
+ sync: nora_mtd_sync,
+ priv: (void *)0
+ },
+ {
+ type: MTD_NORFLASH,
+ flags: MTD_CAP_NORFLASH,
+ size: 0x0a0000,
+ erasesize: 0x20000,
+ name: "NORA kernel",
+ module: THIS_MODULE,
+ erase: nora_mtd_erase,
+ read: nora_mtd_read,
+ write: nora_mtd_write,
+ suspend: nora_mtd_suspend,
+ resume: nora_mtd_resume,
+ sync: nora_mtd_sync,
+ priv: (void *)0x60000
+ },
+ {
+ type: MTD_NORFLASH,
+ flags: MTD_CAP_NORFLASH,
+ size: 0x900000,
+ erasesize: 0x20000,
+ name: "NORA root filesystem",
+ module: THIS_MODULE,
+ erase: nora_mtd_erase,
+ read: nora_mtd_read,
+ write: nora_mtd_write,
+ suspend: nora_mtd_suspend,
+ resume: nora_mtd_resume,
+ sync: nora_mtd_sync,
+ priv: (void *)0x100000
+ },
+ {
+ type: MTD_NORFLASH,
+ flags: MTD_CAP_NORFLASH,
+ size: 0x1600000,
+ erasesize: 0x20000,
+ name: "NORA second filesystem",
+ module: THIS_MODULE,
+ erase: nora_mtd_erase,
+ read: nora_mtd_read,
+ write: nora_mtd_write,
+ suspend: nora_mtd_suspend,
+ resume: nora_mtd_resume,
+ sync: nora_mtd_sync,
+ priv: (void *)0xa00000
+ }
+};
+
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_nora init_module
+#define cleanup_nora cleanup_module
+#endif
+
+int __init init_nora(void)
+{
+ printk(KERN_NOTICE "nora flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+
+ mymtd = do_map_probe("cfi", &nora_map);
+ if (mymtd) {
+ mymtd->module = THIS_MODULE;
+
+ add_mtd_device(&nora_mtds[2]);
+ add_mtd_device(&nora_mtds[0]);
+ add_mtd_device(&nora_mtds[1]);
+ add_mtd_device(&nora_mtds[3]);
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+static void __exit cleanup_nora(void)
+{
+ if (mymtd) {
+ del_mtd_device(&nora_mtds[3]);
+ del_mtd_device(&nora_mtds[1]);
+ del_mtd_device(&nora_mtds[0]);
+ del_mtd_device(&nora_mtds[2]);
+ map_destroy(mymtd);
+ }
+}
+
+module_init(init_nora);
+module_exit(cleanup_nora);
--- /dev/null
+/*
+ * $Id: ocelot.c,v 1.4 2001/06/08 15:36:27 dwmw2 Exp $
+ *
+ * Flash on Momenco Ocelot
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#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);
+
--- /dev/null
+// $Id: octagon-5066.c,v 1.17 2001/06/02 14:30:44 dwmw2 Exp $
+/* ######################################################################
+
+ Octagon 5066 MTD Driver.
+
+ The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
+ comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
+ is replacable by flash. Both units are mapped through a multiplexer
+ into a 32k memory window at 0xe8000. The control register for the
+ multiplexing unit is located at IO 0x208 with a bit map of
+ 0-5 Page Selection in 32k increments
+ 6-7 Device selection:
+ 00 SSD off
+ 01 SSD 0 (Socket)
+ 10 SSD 1 (Flash chip)
+ 11 undefined
+
+ On each SSD, the first 128k is reserved for use by the bios
+ (actually it IS the bios..) This only matters if you are booting off the
+ flash, you must not put a file system starting there.
+
+ The driver tries to do a detection algorithm to guess what sort of devices
+ are plugged into the sockets.
+
+ ##################################################################### */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#include <linux/mtd/map.h>
+
+#define WINDOW_START 0xe8000
+#define WINDOW_LENGTH 0x8000
+#define WINDOW_SHIFT 27
+#define WINDOW_MASK 0x7FFF
+#define PAGE_IO 0x208
+
+static volatile char page_n_dev = 0;
+static unsigned long iomapadr;
+static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED;
+
+/*
+ * We use map_priv_1 to identify which device we are.
+ */
+
+static void __oct5066_page(struct map_info *map, __u8 byte)
+{
+ outb(byte,PAGE_IO);
+ page_n_dev = byte;
+}
+
+static inline void oct5066_page(struct map_info *map, unsigned long ofs)
+{
+ __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
+
+ if (page_n_dev != byte)
+ __oct5066_page(map, byte);
+}
+
+
+static __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
+{
+ __u8 ret;
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, ofs);
+ ret = readb(iomapadr + (ofs & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+ return ret;
+}
+
+static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
+{
+ __u16 ret;
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, ofs);
+ ret = readw(iomapadr + (ofs & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+ return ret;
+}
+
+static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
+{
+ __u32 ret;
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, ofs);
+ ret = readl(iomapadr + (ofs & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+ return ret;
+}
+
+static void oct5066_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(&oct5066_spin);
+ oct5066_page(map, from);
+ memcpy_fromio(to, iomapadr + from, thislen);
+ spin_unlock(&oct5066_spin);
+ to += thislen;
+ from += thislen;
+ len -= thislen;
+ }
+}
+
+static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, adr);
+ writeb(d, iomapadr + (adr & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+}
+
+static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, adr);
+ writew(d, iomapadr + (adr & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+}
+
+static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ spin_lock(&oct5066_spin);
+ oct5066_page(map, adr);
+ writel(d, iomapadr + (adr & WINDOW_MASK));
+ spin_unlock(&oct5066_spin);
+}
+
+static void oct5066_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(&oct5066_spin);
+ oct5066_page(map, to);
+ memcpy_toio(iomapadr + to, from, thislen);
+ spin_unlock(&oct5066_spin);
+ to += thislen;
+ from += thislen;
+ len -= thislen;
+ }
+}
+
+static struct map_info oct5066_map[2] = {
+ {
+ name: "Octagon 5066 Socket",
+ size: 512 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 1<<6
+ },
+ {
+ name: "Octagon 5066 Internal Flash",
+ size: 2 * 1024 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 2<<6
+ }
+};
+
+static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
+
+// OctProbe - Sense if this is an octagon card
+// ---------------------------------------------------------------------
+/* Perform a simple validity test, we map the window select SSD0 and
+ 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(void)
+{
+ unsigned int Base = (1 << 6);
+ unsigned long I;
+ unsigned long Values[10];
+ for (I = 0; I != 20; I++)
+ {
+ outb(Base + (I%10),PAGE_IO);
+ if (I < 10)
+ {
+ // Record the value and check for uniqueness
+ Values[I%10] = readl(iomapadr);
+ if (I > 0 && Values[I%10] == Values[0])
+ return -EAGAIN;
+ }
+ else
+ {
+ // Make sure we get the same values on the second pass
+ if (Values[I%10] != readl(iomapadr))
+ return -EAGAIN;
+ }
+ }
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_oct5066 init_module
+#define cleanup_oct5066 cleanup_module
+#endif
+
+void cleanup_oct5066(void)
+{
+ int i;
+ for (i=0; i<2; i++) {
+ if (oct5066_mtd[i]) {
+ del_mtd_device(oct5066_mtd[i]);
+ map_destroy(oct5066_mtd[i]);
+ }
+ }
+ iounmap((void *)iomapadr);
+ release_region(PAGE_IO,1);
+}
+
+int __init init_oct5066(void)
+{
+ int i;
+
+ // Do an autoprobe sequence
+ if (check_region(PAGE_IO,1) != 0)
+ {
+ printk("5066: Page Register in Use\n");
+ return -EAGAIN;
+ }
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+ if (!iomapadr) {
+ printk("Failed to ioremap memory region\n");
+ return -EIO;
+ }
+ if (OctProbe() != 0)
+ {
+ printk("5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
+ iounmap((void *)iomapadr);
+ return -EAGAIN;
+ }
+
+ request_region(PAGE_IO,1,"Octagon SSD");
+
+ // Print out our little header..
+ printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
+ WINDOW_START+WINDOW_LENGTH);
+
+ for (i=0; i<2; i++) {
+ oct5066_mtd[i] = do_map_probe("cfi", &oct5066_map[i]);
+ if (!oct5066_mtd[i])
+ oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]);
+ if (!oct5066_mtd[i])
+ oct5066_mtd[i] = do_map_probe("ram", &oct5066_map[i]);
+ if (!oct5066_mtd[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]);
+ }
+ }
+
+ if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
+ cleanup_oct5066();
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+module_init(init_oct5066);
+module_exit(cleanup_oct5066);
--- /dev/null
+/*
+ * $Id: physmap.c,v 1.13 2001/06/10 00:14:55 dwmw2 Exp $
+ *
+ * Normal mappings of chips in physical memory
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/config.h>
+
+
+#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
+#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
+#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
+
+static struct mtd_info *mymtd;
+
+__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+{
+ return __raw_readb(map->map_priv_1 + ofs);
+}
+
+__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+{
+ return __raw_readw(map->map_priv_1 + ofs);
+}
+
+__u32 physmap_read32(struct map_info *map, unsigned long 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)
+{
+ memcpy_fromio(to, map->map_priv_1 + from, len);
+}
+
+void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ __raw_writeb(d, map->map_priv_1 + adr);
+ mb();
+}
+
+void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ __raw_writew(d, map->map_priv_1 + adr);
+ mb();
+}
+
+void physmap_write32(struct map_info *map, __u32 d, unsigned long 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)
+{
+ memcpy_toio(map->map_priv_1 + to, from, len);
+}
+
+struct map_info physmap_map = {
+ name: "Physically mapped flash",
+ size: WINDOW_SIZE,
+ buswidth: BUSWIDTH,
+ read8: physmap_read8,
+ read16: physmap_read16,
+ read32: physmap_read32,
+ copy_from: physmap_copy_from,
+ write8: physmap_write8,
+ write16: physmap_write16,
+ write32: physmap_write32,
+ copy_to: physmap_copy_to
+};
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_physmap init_module
+#define cleanup_physmap cleanup_module
+#endif
+
+int __init init_physmap(void)
+{
+ printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+ physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+ if (!physmap_map.map_priv_1) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
+ mymtd = do_map_probe("cfi", &physmap_map);
+ if (mymtd) {
+ mymtd->module = THIS_MODULE;
+
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+ iounmap((void *)physmap_map.map_priv_1);
+ return -ENXIO;
+}
+
+static void __exit cleanup_physmap(void)
+{
+ if (mymtd) {
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+ if (physmap_map.map_priv_1) {
+ iounmap((void *)physmap_map.map_priv_1);
+ physmap_map.map_priv_1 = 0;
+ }
+}
+
+module_init(init_physmap);
+module_exit(cleanup_physmap);
+
--- /dev/null
+/*
+ * pnc2000.c - mapper for Photron PNC-2000 board.
+ *
+ * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
+ *
+ * This code is GPL
+ *
+ * $Id: pnc2000.c,v 1.8 2001/06/10 00:09:45 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+
+#define WINDOW_ADDR 0xbf000000
+#define WINDOW_SIZE 0x00400000
+
+/*
+ * MAP DRIVER STUFF
+ */
+
+__u8 pnc_read8(struct map_info *map, unsigned long ofs)
+{
+ return *(__u8 *)(WINDOW_ADDR + ofs);
+}
+
+__u16 pnc_read16(struct map_info *map, unsigned long ofs)
+{
+ return *(__u16 *)(WINDOW_ADDR + ofs);
+}
+
+__u32 pnc_read32(struct map_info *map, unsigned long ofs)
+{
+ return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
+}
+
+void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+ memcpy(to, (void *)(WINDOW_ADDR + from), len);
+}
+
+void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ *(__u8 *)(WINDOW_ADDR + adr) = d;
+}
+
+void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ *(__u16 *)(WINDOW_ADDR + adr) = d;
+}
+
+void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ *(__u32 *)(WINDOW_ADDR + adr) = d;
+}
+
+void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+ memcpy((void *)(WINDOW_ADDR + to), from, len);
+}
+
+struct map_info pnc_map = {
+ name: "PNC-2000",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: pnc_read8,
+ read16: pnc_read16,
+ read32: pnc_read32,
+ copy_from: pnc_copy_from,
+ write8: pnc_write8,
+ write16: pnc_write16,
+ write32: pnc_write32,
+ copy_to: pnc_copy_to
+};
+
+
+/*
+ * MTD 'PARTITIONING' STUFF
+ */
+static struct mtd_partition pnc_partitions[3] = {
+ {
+ name: "PNC-2000 boot firmware",
+ size: 0x20000,
+ offset: 0
+ },
+ {
+ name: "PNC-2000 kernel",
+ size: 0x1a0000,
+ offset: 0x20000
+ },
+ {
+ name: "PNC-2000 filesystem",
+ size: 0x240000,
+ offset: 0x1c0000
+ }
+};
+
+/*
+ * This is the master MTD device for which all the others are just
+ * auto-relocating aliases.
+ */
+static struct mtd_info *mymtd;
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_pnc2000 init_module
+#define cleanup_pnc2000 cleanup_module
+#endif
+
+int __init init_pnc2000(void)
+{
+ printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+
+ mymtd = do_map_probe("cfi", &pnc_map);
+ if (mymtd) {
+ mymtd->module = THIS_MODULE;
+ return add_mtd_partitions(mymtd, pnc_partitions, 3);
+ }
+
+ return -ENXIO;
+}
+
+static void __exit cleanup_pnc2000(void)
+{
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+}
+
+module_init(init_pnc2000);
+module_exit(cleanup_pnc2000);
--- /dev/null
+/*
+ * $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
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+
+#define WINDOW_ADDR 0xfe000000
+#define WINDOW_SIZE 0x800000
+
+static struct mtd_info *mymtd;
+
+__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
+{
+ return readb(map->map_priv_1 + ofs);
+}
+
+__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
+{
+ return readw(map->map_priv_1 + ofs);
+}
+
+__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
+{
+ return readl(map->map_priv_1 + ofs);
+}
+
+void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+ memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+}
+
+void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ writeb(d, map->map_priv_1 + adr);
+}
+
+void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ writew(d, map->map_priv_1 + adr);
+}
+
+void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ writel(d, map->map_priv_1 + adr);
+}
+
+void rpxlite_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);
+}
+
+struct map_info rpxlite_map = {
+ name: "RPX",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: rpxlite_read8,
+ read16: rpxlite_read16,
+ read32: rpxlite_read32,
+ copy_from: rpxlite_copy_from,
+ write8: rpxlite_write8,
+ write16: rpxlite_write16,
+ write32: rpxlite_write32,
+ copy_to: rpxlite_copy_to
+};
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_rpxlite init_module
+#define cleanup_rpxlite cleanup_module
+#endif
+
+int __init init_rpxlite(void)
+{
+ printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+ rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+
+ if (!rpxlite_map.map_priv_1) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
+ mymtd = do_map_probe("cfi", &rpxlite_map);
+ if (mymtd) {
+ mymtd->module = THIS_MODULE;
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+ iounmap((void *)rpxlite_map.map_priv_1);
+ return -ENXIO;
+}
+
+static void __exit cleanup_rpxlite(void)
+{
+ if (mymtd) {
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+ if (rpxlite_map.map_priv_1) {
+ iounmap((void *)rpxlite_map.map_priv_1);
+ rpxlite_map.map_priv_1 = 0;
+ }
+}
+
+module_init(init_rpxlite);
+module_exit(cleanup_rpxlite);
--- /dev/null
+/*
+ * Flash memory access on SA11x0 based devices
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * $Id: sa1100-flash.c,v 1.15 2001/06/02 18:29:22 nico Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+
+
+#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);
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+// 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);
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+
+/*
+** 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);
--- /dev/null
+/* $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 <linux>/drivers/sbus/char/flash.c instead.
+ *
+ * Copyright (c) 2001 Eric Brower (ebrower@usa.net)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/ebus.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+#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 <ebrower@usa.net>");
+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);
--- /dev/null
+// $Id: vmax301.c,v 1.22 2001/06/02 14:30:44 dwmw2 Exp $
+/* ######################################################################
+
+ Tempustech VMAX SBC301 MTD Driver.
+
+ The VMAx 301 is a SBC based on . It
+ comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
+ more flash. Each unit has it's own 8k mapping into a settable region
+ (0xD8000). There are two 8k mappings for each MTD, the first is always set
+ to the lower 8k of the device the second is paged. Writing a 16 bit page
+ value to anywhere in the first 8k will cause the second 8k to page around.
+
+ To boot the device a bios extension must be installed into the first 8k
+ of flash that is smart enough to copy itself down, page in the rest of
+ itself and begin executing.
+
+ ##################################################################### */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#include <linux/mtd/map.h>
+
+
+#define WINDOW_START 0xd8000
+#define WINDOW_LENGTH 0x2000
+#define WINDOW_SHIFT 25
+#define WINDOW_MASK 0x1FFF
+
+/* Actually we could use two spinlocks, but we'd have to have
+ more private space in the struct map_info. We lose a little
+ performance like this, but we'd probably lose more by having
+ the extra indirection from having one of the map->map_priv
+ fields pointing to yet another private struct.
+*/
+static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED;
+
+static void __vmax301_page(struct map_info *map, unsigned long page)
+{
+ writew(page, map->map_priv_2 - WINDOW_LENGTH);
+ map->map_priv_1 = page;
+}
+
+static inline void vmax301_page(struct map_info *map,
+ unsigned long ofs)
+{
+ unsigned long page = (ofs >> WINDOW_SHIFT);
+ if (map->map_priv_1 != page)
+ __vmax301_page(map, page);
+}
+
+static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
+{
+ __u8 ret;
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, ofs);
+ ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+ return ret;
+}
+
+static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
+{
+ __u16 ret;
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, ofs);
+ ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+ return ret;
+}
+
+static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
+{
+ __u32 ret;
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, ofs);
+ ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+ return ret;
+}
+
+static void vmax301_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(&vmax301_spin);
+ vmax301_page(map, from);
+ memcpy_fromio(to, map->map_priv_2 + from, thislen);
+ spin_unlock(&vmax301_spin);
+ to += thislen;
+ from += thislen;
+ len -= thislen;
+ }
+}
+
+static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, adr);
+ writeb(d, map->map_priv_2 + (adr & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+}
+
+static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, adr);
+ writew(d, map->map_priv_2 + (adr & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+}
+
+static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr)
+{
+ spin_lock(&vmax301_spin);
+ vmax301_page(map, adr);
+ writel(d, map->map_priv_2 + (adr & WINDOW_MASK));
+ spin_unlock(&vmax301_spin);
+}
+
+static void vmax301_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(&vmax301_spin);
+ vmax301_page(map, to);
+ memcpy_toio(map->map_priv_2 + to, from, thislen);
+ spin_unlock(&vmax301_spin);
+ to += thislen;
+ from += thislen;
+ len -= thislen;
+ }
+}
+
+static struct map_info vmax_map[2] = {
+ {
+ name: "VMAX301 Internal Flash",
+ size: 3*2*1024*1024,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + WINDOW_LENGTH,
+ map_priv_2: 0xFFFFFFFF
+ },
+ {
+ name: "VMAX301 Socket",
+ size: 0,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
+ map_priv_2: 0xFFFFFFFF
+ }
+};
+
+static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_vmax301 init_module
+#define cleanup_vmax301 cleanup_module
+#endif
+
+static void __exit cleanup_vmax301(void)
+{
+ int i;
+
+ for (i=0; i<2; i++) {
+ if (vmax_mtd[i]) {
+ del_mtd_device(vmax_mtd[i]);
+ map_destroy(vmax_mtd[i]);
+ }
+ }
+ iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
+}
+
+int __init init_vmax301(void)
+{
+ int i;
+ unsigned long iomapadr;
+ // Print out our little header..
+ printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
+ WINDOW_START+4*WINDOW_LENGTH);
+
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
+ if (!iomapadr) {
+ printk("Failed to ioremap memory region\n");
+ return -EIO;
+ }
+ /* Put the address in the map's private data area.
+ We store the actual MTD IO address rather than the
+ address of the first half, because it's used more
+ often.
+ */
+ vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
+ vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START);
+
+ for (i=0; i<2; i++) {
+ vmax_mtd[i] = do_map_probe("cfi", &vmax_map[i]);
+ if (!vmax_mtd[i])
+ vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
+ if (!vmax_mtd[i])
+ vmax_mtd[i] = do_map_probe("ram", &vmax_map[i]);
+ if (!vmax_mtd[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]);
+ }
+ }
+
+ if (!vmax_mtd[1] && !vmax_mtd[2]) {
+ iounmap((void *)iomapadr);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+module_init(init_vmax301);
+module_exit(cleanup_vmax301);
+++ /dev/null
-/*
- * mixmem - a block device driver for flash rom found on the
- * piggyback board of the multi-purpose mixcom card
- *
- * Author: Gergely Madarasz <gorgo@itc.hu>
- *
- * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
- *
- * This code is GPL
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <asm/io.h>
-#include <linux/init.h>
-#include <linux/mtd/mapped.h>
-
-#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);
--- /dev/null
+/*
+ * $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 <linux/module.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+
+#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 <linux/blk.h>
+
+#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);
/*
- * $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.
*
#include <linux/mtd/compatmac.h>
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
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;
#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");
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");
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 */
/*======================================================================
}
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)))
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);
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;
ret = -EOPNOTSUPP;
else
ret = mtd->lock(mtd, adrs[0], adrs[1]);
+ break;
}
case MEMUNLOCK:
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 */
/*
- * $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.
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
#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);
*
* 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 <linux/module.h>
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
- loff_t offset;
+ u_int32_t offset;
int index;
struct list_head list;
};
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
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
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) {
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;
+++ /dev/null
-/*
- * mtdram - a test mtd device
- * $Id: mtdram.c,v 1.15 2000/07/13 12:40:46 scote1 Exp $
- * Author: Alexander Larsson <alex@cendio.se>
- *
- * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
- *
- * This code is GPL
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/mtd/compatmac.h>
-#include <linux/mtd/mtd.h>
-
-
-#define MTDRAM_TOTAL_SIZE (CONFIG_MTDRAM_TOTAL_SIZE * 1024)
-#define MTDRAM_ERASE_SIZE (CONFIG_MTDRAM_ERASE_SIZE * 1024)
-
-
-// We could store these in the mtd structure, but we only support 1 device..
-static struct mtd_info *mtd_info;
-
-
-static int
-ram_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
- if (instr->addr + instr->len > mtd->size)
- return -EINVAL;
-
- memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
-
- instr->state = MTD_ERASE_DONE;
-
- if (instr->callback)
- (*(instr->callback))(instr);
- return 0;
-}
-
-static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
-{
- if (from + len > mtd->size)
- return -EINVAL;
-
- *mtdbuf = mtd->priv + from;
- *retlen = len;
- return 0;
-}
-
-static void ram_unpoint (struct mtd_info *mtd, u_char *addr)
-{
-}
-
-static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf)
-{
- if (from + len > mtd->size)
- return -EINVAL;
-
- memcpy(buf, mtd->priv + from, len);
-
- *retlen=len;
- return 0;
-}
-
-static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
-{
- if (to + len > mtd->size)
- return -EINVAL;
-
- memcpy ((char *)mtd->priv + to, buf, len);
-
- *retlen=len;
- return 0;
-}
-
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef 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)
-{
- if (mtd_info) {
- del_mtd_device(mtd_info);
- if (mtd_info->priv)
- vfree(mtd_info->priv);
- 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)
- return 0;
-
- memset(mtd_info, 0, sizeof(*mtd_info));
-
- // Setup the MTD structure
- mtd_info->name = "mtdram test device";
- mtd_info->type = MTD_RAM;
- mtd_info->flags = MTD_CAP_RAM;
- mtd_info->size = MTDRAM_TOTAL_SIZE;
- mtd_info->erasesize = MTDRAM_ERASE_SIZE;
- 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) {
- kfree(mtd_info);
- mtd_info = NULL;
- return -ENOMEM;
- }
- mtd_info->erase = ram_erase;
- mtd_info->point = ram_point;
- mtd_info->unpoint = ram_unpoint;
- mtd_info->read = ram_read;
- mtd_info->write = ram_write;
-
- if (add_mtd_device(mtd_info)) {
- vfree(mtd_info->priv);
- kfree(mtd_info);
- mtd_info = NULL;
- return -EIO;
- }
-
- return 0;
-}
-
-#if LINUX_VERSION_CODE > 0x20300
-module_init(init_mtdram);
-module_exit(cleanup_mtdram);
-#endif
--- /dev/null
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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 <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#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 ; i<len ; i++)
+ writeb (buf[i], this->IO_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 ; i<len ; i++) {
+ if (buf[i] != readb (this->IO_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);
--- /dev/null
+/*
+ * 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 <linux/types.h>
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+
+/*
+ * 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
+++ /dev/null
-/* Linux driver for NAND Flash Translation Layer */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@infradead.org> */
-/* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 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. */
-
-#define PRERELEASE
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/blkpg.h>
-#ifdef CONFIG_KMOD
-#include <linux/kmod.h>
-#endif
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nftl.h>
-#include <linux/mtd/compatmac.h>
-
-/* maximum number of loops while examining next block, to have a
- chance to detect consistency problems (they should never happen
- because of the checks done in the mounting */
-
-#define MAX_LOOPS 10000
-
-/* NFTL block device stuff */
-#define MAJOR_NR NFTL_MAJOR
-#define DEVICE_REQUEST nftl_request
-#define DEVICE_OFF(device)
-
-
-#include <linux/blk.h>
-#include <linux/hdreg.h>
-
-/* Linux-specific block device functions */
-
-/* I _HATE_ the Linux block device setup more than anything else I've ever
- * encountered, except ...
- */
-
-static int nftl_sizes[256] = {0,};
-static int nftl_blocksizes[256] = {0,};
-
-/* .. for the Linux partition table handling. */
-struct hd_struct part_table[256] = {{0,0},};
-
-#if LINUX_VERSION_CODE < 0x20328
-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 */
-#if LINUX_VERSION_CODE < 0x20328
- MAX_NFTLS, /* maximum number of real */
- dummy_init, /* init function */
-#endif
- part_table, /* hd struct */
- nftl_sizes, /* block sizes */
- 0, /* number */
- NULL, /* internal use, not presently used */
- NULL /* next */
-};
-
-struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL};
-
-static void NFTL_setup(struct mtd_info *mtd)
-{
- int i;
- struct NFTLrecord *nftl;
- unsigned long temp;
- int firstfree = -1;
-
- DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
-
- for (i = 0; i < MAX_NFTLS; i++) {
- if (!NFTLs[i] && firstfree == -1)
- firstfree = i;
- else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
- /* This is a Spare Media Header for an NFTL we've already found */
- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
- return;
- }
- }
- if (firstfree == -1) {
- printk(KERN_WARNING "No more NFTL slot available\n");
- return;
- }
-
- nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
- if (!nftl) {
- printk(KERN_WARNING "Out of memory for NFTL data structures\n");
- return;
- }
-
- init_MUTEX(&nftl->mutex);
-
- /* get physical parameters */
- nftl->EraseSize = mtd->erasesize;
- nftl->nb_blocks = mtd->size / mtd->erasesize;
- nftl->mtd = mtd;
-
- if (NFTL_mount(nftl) < 0) {
- printk(KERN_WARNING "Could not mount NFTL device\n");
- kfree(nftl);
- return;
- }
-
- /* OK, it's a new one. Set up all the data structures. */
-#ifdef PSYCHO_DEBUG
- printk("Found new NFTL nftl%c\n", firstfree + 'a');
-#endif
-
- /* linux stuff */
- nftl->usecount = 0;
- nftl->cylinders = 1024;
- nftl->heads = 16;
-
- temp = nftl->cylinders * nftl->heads;
- nftl->sectors = nftl->nr_sects / temp;
- if (nftl->nr_sects % temp) {
- nftl->sectors++;
- temp = nftl->cylinders * nftl->sectors;
- nftl->heads = nftl->nr_sects / temp;
-
- if (nftl->nr_sects % temp) {
- nftl->heads++;
- temp = nftl->heads * nftl->sectors;
- nftl->cylinders = nftl->nr_sects / temp;
- }
- }
-
- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
- printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
- "match size of 0x%lx.\n", nftl->nr_sects);
- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
- nftl->cylinders, nftl->heads , nftl->sectors,
- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
-
- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
- }
- NFTLs[firstfree] = nftl;
- /* Finally, set up the block device sizes */
- nftl_sizes[firstfree * 16] = nftl->nr_sects;
- //nftl_blocksizes[firstfree*16] = 512;
- part_table[firstfree * 16].nr_sects = nftl->nr_sects;
-
- /* partition check ... */
-#if LINUX_VERSION_CODE < 0x20328
- resetup_one_dev(&nftl_gendisk, firstfree);
-#else
- grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects);
-#endif
-}
-
-static void NFTL_unsetup(int i)
-{
- struct NFTLrecord *nftl = NFTLs[i];
-
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
-
- NFTLs[i] = NULL;
-
- if (nftl->ReplUnitTable)
- kfree(nftl->ReplUnitTable);
- if (nftl->EUNtable)
- kfree(nftl->EUNtable);
-
- kfree(nftl);
-}
-
-/* Search the MTD device for NFTL partitions */
-static void NFTL_notify_add(struct mtd_info *mtd)
-{
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
-
- if (mtd) {
- if (!mtd->read_oob) {
- /* If this MTD doesn't have out-of-band data,
- then there's no point continuing */
- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
- return;
- }
- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
- mtd->read, mtd->size, mtd->erasesize);
-
- NFTL_setup(mtd);
- }
-}
-
-static void NFTL_notify_remove(struct mtd_info *mtd)
-{
- int i;
-
- for (i = 0; i < MAX_NFTLS; i++) {
- if (NFTLs[i] && NFTLs[i]->mtd == mtd)
- NFTL_unsetup(i);
- }
-}
-
-#ifdef CONFIG_NFTL_RW
-
-/* Actual NFTL access routines */
-/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
- * when the give Virtual Unit Chain
- */
-static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
-{
- /* For a given Virtual Unit Chain: find or create a free block and
- add it to the chain */
- /* We're passed the number of the last EUN in the chain, to save us from
- having to look it up again */
- u16 pot = nftl->LastFreeEUN;
- int silly = -1;
-
- /* Normally, we force a fold to happen before we run out of free blocks completely */
- if (!desperate && nftl->numfreeEUNs < 2) {
- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
- return 0xffff;
- }
-
- /* Scan for a free block */
- do {
- if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
- nftl->LastFreeEUN = pot;
- nftl->numfreeEUNs--;
- return pot;
- }
-
- /* This will probably point to the MediaHdr unit itself,
- right at the beginning of the partition. But that unit
- (and the backup unit too) should have the UCI set
- up so that it's not selected for overwriting */
- if (++pot > nftl->lastEUN)
- pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
-
- if (!silly--) {
- printk("Argh! No free blocks found! LastFreeEUN = %d, "
- "FirstEUN = %d\n", nftl->LastFreeEUN,
- le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
- return 0xffff;
- }
- } while (pot != nftl->LastFreeEUN);
-
- return 0xffff;
-}
-
-static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
-{
- u16 BlockMap[MAX_SECTORS_PER_UNIT];
- unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
- unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
- unsigned int thisEUN;
- int block;
- int silly;
- unsigned int targetEUN;
- struct nftl_oob oob;
- int inplace = 1;
- size_t retlen;
-
- memset(BlockMap, 0xff, sizeof(BlockMap));
- memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
-
- thisEUN = nftl->EUNtable[thisVUC];
-
- if (thisEUN == BLOCK_NIL) {
- printk(KERN_WARNING "Trying to fold non-existent "
- "Virtual Unit Chain %d!\n", thisVUC);
- return BLOCK_NIL;
- }
-
- /* Scan to find the Erase Unit which holds the actual data for each
- 512-byte block within the Chain.
- */
- silly = MAX_LOOPS;
- targetEUN = BLOCK_NIL;
- while (thisEUN <= nftl->lastEUN ) {
- unsigned int status, foldmark;
-
- targetEUN = thisEUN;
- for (block = 0; block < nftl->EraseSize / 512; block ++) {
- MTD_READOOB(nftl->mtd,
- (thisEUN * nftl->EraseSize) + (block * 512),
- 16 , &retlen, (char *)&oob);
- if (block == 2) {
- foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
- if (foldmark == FOLD_MARK_IN_PROGRESS) {
- DEBUG(MTD_DEBUG_LEVEL1,
- "Write Inhibited on EUN %d\n", thisEUN);
- inplace = 0;
- } else {
- /* There's no other reason not to do inplace,
- except ones that come later. So we don't need
- to preserve inplace */
- inplace = 1;
- }
- }
- status = oob.b.Status | oob.b.Status1;
- BlockLastState[block] = status;
-
- switch(status) {
- case SECTOR_FREE:
- BlockFreeFound[block] = 1;
- break;
-
- case SECTOR_USED:
- if (!BlockFreeFound[block])
- BlockMap[block] = thisEUN;
- else
- printk(KERN_WARNING
- "SECTOR_USED found after SECTOR_FREE "
- "in Virtual Unit Chain %d for block %d\n",
- thisVUC, block);
- break;
- case SECTOR_IGNORE:
- case SECTOR_DELETED:
- break;
- default:
- printk("Unknown status for block %d in EUN %d: %x\n",
- block, thisEUN, status);
- }
- }
-
- if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
- thisVUC);
- return BLOCK_NIL;
- }
-
- thisEUN = nftl->ReplUnitTable[thisEUN];
- }
-
- if (inplace) {
- /* We're being asked to be a fold-in-place. Check
- that all blocks are either present or SECTOR_FREE
- in the target block. If not, we're going to have
- to fold out-of-place anyway.
- */
- for (block = 0; block < nftl->EraseSize / 512 ; block++) {
- if (BlockLastState[block] != SECTOR_FREE &&
- BlockMap[block] != targetEUN) {
- DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
- "block %d was %x lastEUN, "
- "and is in EUN %d (%s) %d\n",
- thisVUC, block, BlockLastState[block],
- BlockMap[block],
- BlockMap[block]== targetEUN ? "==" : "!=",
- targetEUN);
- inplace = 0;
- break;
- }
- }
-
- if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
- pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
- BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
- SECTOR_FREE) {
- DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
- "Folding out of place.\n", targetEUN);
- inplace = 0;
- }
- }
-
- if (!inplace) {
- DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
- "Trying out-of-place\n", thisVUC);
- /* We need to find a targetEUN to fold into. */
- targetEUN = NFTL_findfreeblock(nftl, 1);
- if (targetEUN == BLOCK_NIL) {
- /* Ouch. Now we're screwed. We need to do a
- fold-in-place of another chain to make room
- for this one. We need a better way of selecting
- which chain to fold, because makefreeblock will
- only ask us to fold the same one again.
- */
- printk(KERN_WARNING
- "NFTL_findfreeblock(desperate) returns 0xffff.\n");
- return BLOCK_NIL;
- }
- } else {
- /* We put a fold mark in the chain we are folding only if
- we fold in place to help the mount check code. If we do
- not fold in place, it is possible to find the valid
- chain by selecting the longer one */
- oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
- oob.u.c.unused = 0xffffffff;
- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
- 8, &retlen, (char *)&oob.u);
- }
-
- /* OK. We now know the location of every block in the Virtual Unit Chain,
- and the Erase Unit into which we are supposed to be copying.
- Go for it.
- */
- DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
- for (block = 0; block < nftl->EraseSize / 512 ; block++) {
- unsigned char movebuf[512];
- int ret;
-
- /* If it's in the target EUN already, or if it's pending write, do nothing */
- if (BlockMap[block] == targetEUN ||
- (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
- continue;
- }
-
- /* copy only in non free block (free blocks can only
- happen in case of media errors or deleted blocks) */
- if (BlockMap[block] == BLOCK_NIL)
- continue;
-
- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
- + (block * 512), 512, &retlen, movebuf, (char *)&oob);
- if (ret < 0) {
- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
- + (block * 512), 512, &retlen,
- movebuf, (char *)&oob);
- if (ret != -EIO)
- printk("Error went away on retry.\n");
- }
- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob);
- }
-
- /* add the header so that it is now a valid chain */
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
- = cpu_to_le16(thisVUC);
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
-
- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
- 8, &retlen, (char *)&oob.u);
-
- /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
-
- /* At this point, we have two different chains for this Virtual Unit, and no way to tell
- them apart. If we crash now, we get confused. However, both contain the same data, so we
- shouldn't actually lose data in this case. It's just that when we load up on a medium which
- has duplicate chains, we need to free one of the chains because it's not necessary any more.
- */
- thisEUN = nftl->EUNtable[thisVUC];
- DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
-
- /* For each block in the old chain (except the targetEUN of course),
- free it and make it available for future use */
- while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
- unsigned int EUNtmp;
-
- EUNtmp = nftl->ReplUnitTable[thisEUN];
-
- if (NFTL_formatblock(nftl, thisEUN) < 0) {
- /* could not erase : mark block as reserved
- * FixMe: Update Bad Unit Table on disk
- */
- nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
- } else {
- /* correctly erased : mark it as free */
- nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
- nftl->numfreeEUNs++;
- }
- thisEUN = EUNtmp;
- }
-
- /* Make this the new start of chain for thisVUC */
- nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
- nftl->EUNtable[thisVUC] = targetEUN;
-
- return targetEUN;
-}
-
-u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
-{
- /* This is the part that needs some cleverness applied.
- For now, I'm doing the minimum applicable to actually
- get the thing to work.
- Wear-levelling and other clever stuff needs to be implemented
- and we also need to do some assessment of the results when
- the system loses power half-way through the routine.
- */
- u16 LongestChain = 0;
- u16 ChainLength = 0, thislen;
- u16 chain, EUN;
-
- for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) {
- EUN = nftl->EUNtable[chain];
- thislen = 0;
-
- while (EUN <= nftl->lastEUN) {
- thislen++;
- //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
- EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
- if (thislen > 0xff00) {
- printk("Endless loop in Virtual Chain %d: Unit %x\n",
- chain, EUN);
- }
- if (thislen > 0xff10) {
- /* Actually, don't return failure. Just ignore this chain and
- get on with it. */
- thislen = 0;
- break;
- }
- }
-
- if (thislen > ChainLength) {
- //printk("New longest chain is %d with length %d\n", chain, thislen);
- ChainLength = thislen;
- LongestChain = chain;
- }
- }
-
- if (ChainLength < 2) {
- printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
- "Failing request\n");
- return 0xffff;
- }
-
- return NFTL_foldchain (nftl, LongestChain, pendingblock);
-}
-
-/* NFTL_findwriteunit: Return the unit number into which we can write
- for this block. Make it available if it isn't already
-*/
-static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
-{
- u16 lastEUN;
- u16 thisVUC = block / (nftl->EraseSize / 512);
- unsigned int writeEUN;
- unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
- size_t retlen;
- int silly, silly2 = 3;
- struct nftl_oob oob;
-
- do {
- /* Scan the media to find a unit in the VUC which has
- a free space for the block in question.
- */
-
- /* This condition catches the 0x[7f]fff cases, as well as
- being a sanity check for past-end-of-media access
- */
- lastEUN = BLOCK_NIL;
- writeEUN = nftl->EUNtable[thisVUC];
- silly = MAX_LOOPS;
- while (writeEUN <= nftl->lastEUN) {
- struct nftl_bci bci;
- size_t retlen;
- unsigned int status;
-
- lastEUN = writeEUN;
-
- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
- 8, &retlen, (char *)&bci);
-
- DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
- block , writeEUN, le16_to_cpu(bci.Status));
-
- status = bci.Status | bci.Status1;
- switch(status) {
- case SECTOR_FREE:
- return writeEUN;
-
- case SECTOR_DELETED:
- case SECTOR_USED:
- case SECTOR_IGNORE:
- break;
- default:
- // Invalid block. Don't use it any more. Must implement.
- break;
- }
-
- if (!silly--) {
- printk(KERN_WARNING
- "Infinite loop in Virtual Unit Chain 0x%x\n",
- thisVUC);
- return 0xffff;
- }
-
- /* Skip to next block in chain */
- writeEUN = nftl->ReplUnitTable[writeEUN];
- }
-
- /* OK. We didn't find one in the existing chain, or there
- is no existing chain. */
-
- /* Try to find an already-free block */
- writeEUN = NFTL_findfreeblock(nftl, 0);
-
- if (writeEUN == BLOCK_NIL) {
- /* That didn't work - there were no free blocks just
- waiting to be picked up. We're going to have to fold
- a chain to make room.
- */
-
- /* First remember the start of this chain */
- //u16 startEUN = nftl->EUNtable[thisVUC];
-
- //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
- writeEUN = NFTL_makefreeblock(nftl, 0xffff);
-
- if (writeEUN == BLOCK_NIL) {
- /* Ouch. This should never happen - we should
- always be able to make some room somehow.
- If we get here, we've allocated more storage
- space than actual media, or our makefreeblock
- routine is missing something.
- */
- printk(KERN_WARNING "Cannot make free space.\n");
- return BLOCK_NIL;
- }
- //printk("Restarting scan\n");
- lastEUN = BLOCK_NIL;
- continue;
- }
-
- /* We've found a free block. Insert it into the chain. */
-
- if (lastEUN != BLOCK_NIL) {
- thisVUC |= 0x8000; /* It's a replacement block */
- } else {
- /* The first block in a new chain */
- nftl->EUNtable[thisVUC] = writeEUN;
- }
-
- /* set up the actual EUN we're writing into */
- /* Both in our cache... */
- nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
-
- /* ... and on the flash itself */
- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
- &retlen, (char *)&oob.u);
-
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
-
- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
- &retlen, (char *)&oob.u);
-
- /* we link the new block to the chain only after the
- block is ready. It avoids the case where the chain
- could point to a free block */
- if (lastEUN != BLOCK_NIL) {
- /* Both in our cache... */
- nftl->ReplUnitTable[lastEUN] = writeEUN;
- /* ... and on the flash itself */
- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
- 8, &retlen, (char *)&oob.u);
-
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
- = cpu_to_le16(writeEUN);
-
- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
- 8, &retlen, (char *)&oob.u);
- }
-
- return writeEUN;
-
- } while (silly2--);
-
- printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
- thisVUC);
- return 0xffff;
-}
-
-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
-{
- u16 writeEUN;
- unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
- size_t retlen;
- u8 eccbuf[6];
-
- writeEUN = NFTL_findwriteunit(nftl, block);
-
- if (writeEUN == BLOCK_NIL) {
- printk(KERN_WARNING
- "NFTL_writeblock(): Cannot find block to write to\n");
- /* If we _still_ haven't got a block to use, we're screwed */
- return 1;
- }
-
- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
- 512, &retlen, (char *)buffer, (char *)eccbuf);
- /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
-
- return 0;
-}
-#endif /* CONFIG_NFTL_RW */
-
-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
-{
- u16 lastgoodEUN;
- u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
- unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
- unsigned int status;
- int silly = MAX_LOOPS;
- size_t retlen;
- struct nftl_bci bci;
-
- lastgoodEUN = BLOCK_NIL;
-
- if (thisEUN != BLOCK_NIL) {
- while (thisEUN < nftl->nb_blocks) {
- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
- 8, &retlen, (char *)&bci) < 0)
- status = SECTOR_IGNORE;
- else
- status = bci.Status | bci.Status1;
-
- switch (status) {
- case SECTOR_FREE:
- /* no modification of a sector should follow a free sector */
- goto the_end;
- case SECTOR_DELETED:
- lastgoodEUN = BLOCK_NIL;
- break;
- case SECTOR_USED:
- lastgoodEUN = thisEUN;
- break;
- case SECTOR_IGNORE:
- break;
- default:
- printk("Unknown status for block %d in EUN %d: %x\n",
- block, thisEUN, status);
- break;
- }
-
- if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
- block / (nftl->EraseSize / 512));
- return 1;
- }
- thisEUN = nftl->ReplUnitTable[thisEUN];
- }
- }
-
- the_end:
- if (lastgoodEUN == BLOCK_NIL) {
- /* the requested block is not on the media, return all 0x00 */
- memset(buffer, 0, 512);
- } else {
- loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
- size_t retlen;
- u_char eccbuf[6];
- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf))
- return -EIO;
- }
- return 0;
-}
-
-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
-{
- struct NFTLrecord *nftl;
-
- nftl = NFTLs[MINOR(inode->i_rdev) / 16];
-
- if (!nftl) return -EINVAL;
-
- switch (cmd) {
- case HDIO_GETGEO: {
- struct hd_geometry g;
-
- g.heads = nftl->heads;
- g.sectors = nftl->sectors;
- g.cylinders = nftl->cylinders;
- g.start = part_table[MINOR(inode->i_rdev)].start_sect;
- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
- }
- case BLKGETSIZE: /* Return device size */
- if (!arg) return -EINVAL;
- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
- (long *) arg);
-
- case BLKFLSBUF:
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- fsync_dev(inode->i_rdev);
- invalidate_buffers(inode->i_rdev);
- if (nftl->mtd->sync)
- nftl->mtd->sync(nftl->mtd);
- return 0;
-
- case BLKRRPART:
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- if (nftl->usecount > 1) return -EBUSY;
-#if LINUX_VERSION_CODE < 0x20328
- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16);
-#else
- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16,
- 1<<4, nftl->nr_sects);
-#endif
- return 0;
-
-#if (LINUX_VERSION_CODE < 0x20303)
- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
-#else
- case BLKROSET:
- case BLKROGET:
- case BLKSSZGET:
- return blk_ioctl(inode->i_rdev, cmd, arg);
-#endif
-
- default:
- return -EINVAL;
- }
-}
-
-void nftl_request(RQFUNC_ARG)
-{
- unsigned int dev, block, nsect;
- struct NFTLrecord *nftl;
- char *buffer;
- struct request *req;
- int res;
-
- while (1) {
- INIT_REQUEST; /* blk.h */
- req = CURRENT;
-
- /* We can do this because the generic code knows not to
- touch the request at the head of the queue */
- spin_unlock_irq(&io_request_lock);
-
- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
- (req->cmd == READ) ? "Read " : "Write",
- req->sector, req->current_nr_sectors);
-
- dev = MINOR(req->rq_dev);
- block = req->sector;
- nsect = req->current_nr_sectors;
- buffer = req->buffer;
- res = 1; /* succeed */
-
- if (dev >= MAX_NFTLS * 16) {
- /* there is no such partition */
- printk("nftl: bad minor number: device = %s\n",
- kdevname(req->rq_dev));
- res = 0; /* fail */
- goto repeat;
- }
-
- nftl = NFTLs[dev / 16];
- DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
- down(&nftl->mutex);
- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
-
- if (block + nsect > part_table[dev].nr_sects) {
- /* access past the end of device */
- printk("nftl%c%d: bad access: block = %d, count = %d\n",
- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
- up(&nftl->mutex);
- res = 0; /* fail */
- goto repeat;
- }
-
- block += part_table[dev].start_sect;
-
- if (req->cmd == READ) {
- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
-
- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
- /* Read a single sector to req->buffer + (512 * i) */
- if (NFTL_readblock(nftl, block, buffer)) {
- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
- up(&nftl->mutex);
- res = 0;
- goto repeat;
- }
- }
-
- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
- up(&nftl->mutex);
- goto repeat;
- } else if (req->cmd == WRITE) {
- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
- "(req->nr_sectors == %lx)\n", nsect, block,
- req->nr_sectors);
-#ifdef CONFIG_NFTL_RW
- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
- /* Read a single sector to req->buffer + (512 * i) */
- if (NFTL_writeblock(nftl, block, buffer)) {
- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
- up(&nftl->mutex);
- res = 0;
- goto repeat;
- }
- }
- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
-#else
- res = 0; /* Writes always fail */
-#endif /* CONFIG_NFTL_RW */
- up(&nftl->mutex);
- goto repeat;
- } else {
- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
- up(&nftl->mutex);
- res = 0;
- goto repeat;
- }
- repeat:
- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
- spin_lock_irq(&io_request_lock);
- end_request(res);
- }
-}
-
-static int nftl_open(struct inode *ip, struct file *fp)
-{
- int nftlnum = MINOR(ip->i_rdev) / 16;
- struct NFTLrecord *thisNFTL;
- thisNFTL = NFTLs[nftlnum];
-
- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
-
-#ifdef CONFIG_KMOD
- if (!thisNFTL && nftlnum == 0) {
- request_module("docprobe");
- thisNFTL = NFTLs[nftlnum];
- }
-#endif
- if (!thisNFTL) {
- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
- nftlnum, ip->i_rdev, ip, fp);
- return -ENODEV;
- }
-
-#ifndef CONFIG_NFTL_RW
- if (fp->f_mode & FMODE_WRITE)
- return -EROFS;
-#endif /* !CONFIG_NFTL_RW */
-
- thisNFTL->usecount++;
- MOD_INC_USE_COUNT;
- if (!get_mtd_device(thisNFTL->mtd, -1)) {
- MOD_DEC_USE_COUNT;
- return /* -E'SBUGGEREDOFF */ -ENXIO;
- }
-
- return 0;
-}
-
-static int nftl_release(struct inode *inode, struct file *fp)
-{
- struct NFTLrecord *thisNFTL;
-
- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
-
- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
-
- invalidate_device(inode->i_rdev, 1);
-
- if (thisNFTL->mtd->sync)
- thisNFTL->mtd->sync(thisNFTL->mtd);
- thisNFTL->usecount--;
- MOD_DEC_USE_COUNT;
-
- put_mtd_device(thisNFTL->mtd);
-
- return 0;
-}
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations nftl_fops = {
- read: block_read,
- write: block_write,
- ioctl: nftl_ioctl,
- open: nftl_open,
- release: nftl_release,
- fsync: block_fsync,
-};
-#else
-static struct block_device_operations nftl_fops =
-{
- open: nftl_open,
- release: nftl_release,
- ioctl: nftl_ioctl
-};
-#endif
-
-
-
-/****************************************************************************
- *
- * Module stuff
- *
- ****************************************************************************/
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_nftl init_module
-#define cleanup_nftl cleanup_module
-#endif
-
-static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL};
-
-/* static int __init init_nftl(void) */
-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");
-#endif
-
- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
- printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
- return -EBUSY;
- } else {
-#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = nftl_request;
-#else
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
-#endif
- /* set block size to 1kB each */
- for (i = 0; i < 256; i++) {
- nftl_blocksizes[i] = 1024;
- }
- blksize_size[MAJOR_NR] = nftl_blocksizes;
-
- nftl_gendisk.next = gendisk_head;
- gendisk_head = &nftl_gendisk;
- }
-
- register_mtd_user(&nftl_notifier);
-
- return 0;
-}
-
-static void __exit cleanup_nftl(void)
-{
- struct gendisk *gd, **gdp;
-
- unregister_mtd_user(&nftl_notifier);
- unregister_blkdev(MAJOR_NR, "nftl");
-
-#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = 0;
-#else
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-#endif
-
- /* remove ourself from generic harddisk list
- FIXME: why can't I found this partition on /proc/partition */
- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
- if (*gdp == &nftl_gendisk) {
- gd = *gdp; *gdp = gd->next;
- break;
- }
-}
-
-module_init(init_nftl);
-module_exit(cleanup_nftl);
--- /dev/null
+/* Linux driver for NAND Flash Translation Layer */
+/* (c) 1999 Machine Vision Holdings, Inc. */
+/* Author: David Woodhouse <dwmw2@infradead.org> */
+/* $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. The author places no additional
+ restrictions of any kind on it.
+ */
+
+#define PRERELEASE
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/blkpg.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+#include <linux/mtd/compatmac.h>
+
+/* maximum number of loops while examining next block, to have a
+ chance to detect consistency problems (they should never happen
+ because of the checks done in the mounting */
+
+#define MAX_LOOPS 10000
+
+/* NFTL block device stuff */
+#define MAJOR_NR NFTL_MAJOR
+#define DEVICE_REQUEST nftl_request
+#define DEVICE_OFF(device)
+
+
+#include <linux/blk.h>
+#include <linux/hdreg.h>
+
+/* Linux-specific block device functions */
+
+/* I _HATE_ the Linux block device setup more than anything else I've ever
+ * encountered, except ...
+ */
+
+static int nftl_sizes[256];
+static int nftl_blocksizes[256];
+
+/* .. for the Linux partition table handling. */
+struct hd_struct part_table[256];
+
+#if LINUX_VERSION_CODE < 0x20328
+static void dummy_init (struct gendisk *crap)
+{}
+#endif
+
+static struct gendisk nftl_gendisk = {
+ major: MAJOR_NR,
+ major_name: "nftl",
+ minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */
+ max_p: (1<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */
+#if LINUX_VERSION_CODE < 0x20328
+ max_nr: MAX_NFTLS, /* maximum number of real */
+ init: dummy_init, /* init function */
+#endif
+ part: part_table, /* hd struct */
+ sizes: nftl_sizes, /* block sizes */
+};
+
+struct NFTLrecord *NFTLs[MAX_NFTLS];
+
+static void NFTL_setup(struct mtd_info *mtd)
+{
+ int i;
+ struct NFTLrecord *nftl;
+ unsigned long temp;
+ int firstfree = -1;
+
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
+
+ for (i = 0; i < MAX_NFTLS; i++) {
+ if (!NFTLs[i] && firstfree == -1)
+ firstfree = i;
+ else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
+ /* This is a Spare Media Header for an NFTL we've already found */
+ DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
+ return;
+ }
+ }
+ if (firstfree == -1) {
+ printk(KERN_WARNING "No more NFTL slot available\n");
+ return;
+ }
+
+ nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
+ if (!nftl) {
+ printk(KERN_WARNING "Out of memory for NFTL data structures\n");
+ return;
+ }
+
+ init_MUTEX(&nftl->mutex);
+
+ /* get physical parameters */
+ nftl->EraseSize = mtd->erasesize;
+ nftl->nb_blocks = mtd->size / mtd->erasesize;
+ nftl->mtd = mtd;
+
+ if (NFTL_mount(nftl) < 0) {
+ printk(KERN_WARNING "Could not mount NFTL device\n");
+ kfree(nftl);
+ return;
+ }
+
+ /* OK, it's a new one. Set up all the data structures. */
+#ifdef PSYCHO_DEBUG
+ printk("Found new NFTL nftl%c\n", firstfree + 'a');
+#endif
+
+ /* linux stuff */
+ nftl->usecount = 0;
+ nftl->cylinders = 1024;
+ nftl->heads = 16;
+
+ temp = nftl->cylinders * nftl->heads;
+ nftl->sectors = nftl->nr_sects / temp;
+ if (nftl->nr_sects % temp) {
+ nftl->sectors++;
+ temp = nftl->cylinders * nftl->sectors;
+ nftl->heads = nftl->nr_sects / temp;
+
+ if (nftl->nr_sects % temp) {
+ nftl->heads++;
+ temp = nftl->heads * nftl->sectors;
+ nftl->cylinders = nftl->nr_sects / temp;
+ }
+ }
+
+ if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
+ printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
+ "match size of 0x%lx.\n", nftl->nr_sects);
+ printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
+ nftl->cylinders, nftl->heads , nftl->sectors,
+ (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+
+ /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
+ }
+ NFTLs[firstfree] = nftl;
+ /* Finally, set up the block device sizes */
+ nftl_sizes[firstfree * 16] = nftl->nr_sects;
+ //nftl_blocksizes[firstfree*16] = 512;
+ part_table[firstfree * 16].nr_sects = nftl->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<<NFTL_PARTN_BITS, nftl->nr_sects);
+#endif
+}
+
+static void NFTL_unsetup(int i)
+{
+ struct NFTLrecord *nftl = NFTLs[i];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
+
+ NFTLs[i] = NULL;
+
+ if (nftl->ReplUnitTable)
+ kfree(nftl->ReplUnitTable);
+ if (nftl->EUNtable)
+ kfree(nftl->EUNtable);
+
+ nftl_gendisk.nr_real--;
+ kfree(nftl);
+}
+
+/* Search the MTD device for NFTL partitions */
+static void NFTL_notify_add(struct mtd_info *mtd)
+{
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
+
+ if (mtd) {
+ if (!mtd->read_oob) {
+ /* If this MTD doesn't have out-of-band data,
+ then there's no point continuing */
+ DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
+ return;
+ }
+ DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
+ mtd->read, mtd->size, mtd->erasesize);
+
+ NFTL_setup(mtd);
+ }
+}
+
+static void NFTL_notify_remove(struct mtd_info *mtd)
+{
+ int i;
+
+ for (i = 0; i < MAX_NFTLS; i++) {
+ if (NFTLs[i] && NFTLs[i]->mtd == mtd)
+ NFTL_unsetup(i);
+ }
+}
+
+#ifdef CONFIG_NFTL_RW
+
+/* Actual NFTL access routines */
+/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
+ * when the give Virtual Unit Chain
+ */
+static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
+{
+ /* For a given Virtual Unit Chain: find or create a free block and
+ add it to the chain */
+ /* We're passed the number of the last EUN in the chain, to save us from
+ having to look it up again */
+ u16 pot = nftl->LastFreeEUN;
+ int silly = -1;
+
+ /* Normally, we force a fold to happen before we run out of free blocks completely */
+ if (!desperate && nftl->numfreeEUNs < 2) {
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
+ return 0xffff;
+ }
+
+ /* Scan for a free block */
+ do {
+ if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
+ nftl->LastFreeEUN = pot;
+ nftl->numfreeEUNs--;
+ return pot;
+ }
+
+ /* This will probably point to the MediaHdr unit itself,
+ right at the beginning of the partition. But that unit
+ (and the backup unit too) should have the UCI set
+ up so that it's not selected for overwriting */
+ if (++pot > nftl->lastEUN)
+ pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
+
+ if (!silly--) {
+ printk("Argh! No free blocks found! LastFreeEUN = %d, "
+ "FirstEUN = %d\n", nftl->LastFreeEUN,
+ le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
+ return 0xffff;
+ }
+ } while (pot != nftl->LastFreeEUN);
+
+ return 0xffff;
+}
+
+static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
+{
+ u16 BlockMap[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
+ unsigned int thisEUN;
+ int block;
+ int silly;
+ unsigned int targetEUN;
+ struct nftl_oob oob;
+ int inplace = 1;
+ size_t retlen;
+
+ memset(BlockMap, 0xff, sizeof(BlockMap));
+ memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
+
+ thisEUN = nftl->EUNtable[thisVUC];
+
+ if (thisEUN == BLOCK_NIL) {
+ printk(KERN_WARNING "Trying to fold non-existent "
+ "Virtual Unit Chain %d!\n", thisVUC);
+ return BLOCK_NIL;
+ }
+
+ /* Scan to find the Erase Unit which holds the actual data for each
+ 512-byte block within the Chain.
+ */
+ silly = MAX_LOOPS;
+ targetEUN = BLOCK_NIL;
+ while (thisEUN <= nftl->lastEUN ) {
+ unsigned int status, foldmark;
+
+ targetEUN = thisEUN;
+ for (block = 0; block < nftl->EraseSize / 512; block ++) {
+ MTD_READOOB(nftl->mtd,
+ (thisEUN * nftl->EraseSize) + (block * 512),
+ 16 , &retlen, (char *)&oob);
+ if (block == 2) {
+ foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
+ if (foldmark == FOLD_MARK_IN_PROGRESS) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "Write Inhibited on EUN %d\n", thisEUN);
+ inplace = 0;
+ } else {
+ /* There's no other reason not to do inplace,
+ except ones that come later. So we don't need
+ to preserve inplace */
+ inplace = 1;
+ }
+ }
+ status = oob.b.Status | oob.b.Status1;
+ BlockLastState[block] = status;
+
+ switch(status) {
+ case SECTOR_FREE:
+ BlockFreeFound[block] = 1;
+ break;
+
+ case SECTOR_USED:
+ if (!BlockFreeFound[block])
+ BlockMap[block] = thisEUN;
+ else
+ printk(KERN_WARNING
+ "SECTOR_USED found after SECTOR_FREE "
+ "in Virtual Unit Chain %d for block %d\n",
+ thisVUC, block);
+ break;
+ case SECTOR_IGNORE:
+ case SECTOR_DELETED:
+ break;
+ default:
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
+ }
+ }
+
+ if (!silly--) {
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
+ return BLOCK_NIL;
+ }
+
+ thisEUN = nftl->ReplUnitTable[thisEUN];
+ }
+
+ if (inplace) {
+ /* We're being asked to be a fold-in-place. Check
+ that all blocks are either present or SECTOR_FREE
+ in the target block. If not, we're going to have
+ to fold out-of-place anyway.
+ */
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
+ if (BlockLastState[block] != SECTOR_FREE &&
+ BlockMap[block] != targetEUN) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
+ "block %d was %x lastEUN, "
+ "and is in EUN %d (%s) %d\n",
+ thisVUC, block, BlockLastState[block],
+ BlockMap[block],
+ BlockMap[block]== targetEUN ? "==" : "!=",
+ targetEUN);
+ inplace = 0;
+ break;
+ }
+ }
+
+ if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
+ pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
+ BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
+ SECTOR_FREE) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
+ "Folding out of place.\n", targetEUN);
+ inplace = 0;
+ }
+ }
+
+ if (!inplace) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
+ "Trying out-of-place\n", thisVUC);
+ /* We need to find a targetEUN to fold into. */
+ targetEUN = NFTL_findfreeblock(nftl, 1);
+ if (targetEUN == BLOCK_NIL) {
+ /* Ouch. Now we're screwed. We need to do a
+ fold-in-place of another chain to make room
+ for this one. We need a better way of selecting
+ which chain to fold, because makefreeblock will
+ only ask us to fold the same one again.
+ */
+ printk(KERN_WARNING
+ "NFTL_findfreeblock(desperate) returns 0xffff.\n");
+ return BLOCK_NIL;
+ }
+ } else {
+ /* We put a fold mark in the chain we are folding only if
+ we fold in place to help the mount check code. If we do
+ not fold in place, it is possible to find the valid
+ chain by selecting the longer one */
+ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+ oob.u.c.unused = 0xffffffff;
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+ /* OK. We now know the location of every block in the Virtual Unit Chain,
+ and the Erase Unit into which we are supposed to be copying.
+ Go for it.
+ */
+ DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
+ unsigned char movebuf[512];
+ int ret;
+
+ /* If it's in the target EUN already, or if it's pending write, do nothing */
+ if (BlockMap[block] == targetEUN ||
+ (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
+ continue;
+ }
+
+ /* copy only in non free block (free blocks can only
+ happen in case of media errors or deleted blocks) */
+ if (BlockMap[block] == BLOCK_NIL)
+ continue;
+
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen, movebuf, (char *)&oob);
+ if (ret < 0) {
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen,
+ movebuf, (char *)&oob);
+ if (ret != -EIO)
+ printk("Error went away on retry.\n");
+ }
+ MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob);
+ }
+
+ /* add the header so that it is now a valid chain */
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
+ = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
+
+ /* At this point, we have two different chains for this Virtual Unit, and no way to tell
+ them apart. If we crash now, we get confused. However, both contain the same data, so we
+ shouldn't actually lose data in this case. It's just that when we load up on a medium which
+ has duplicate chains, we need to free one of the chains because it's not necessary any more.
+ */
+ thisEUN = nftl->EUNtable[thisVUC];
+ DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
+
+ /* For each block in the old chain (except the targetEUN of course),
+ free it and make it available for future use */
+ while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
+ unsigned int EUNtmp;
+
+ EUNtmp = nftl->ReplUnitTable[thisEUN];
+
+ if (NFTL_formatblock(nftl, thisEUN) < 0) {
+ /* could not erase : mark block as reserved
+ * FixMe: Update Bad Unit Table on disk
+ */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
+ } else {
+ /* correctly erased : mark it as free */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
+ nftl->numfreeEUNs++;
+ }
+ thisEUN = EUNtmp;
+ }
+
+ /* Make this the new start of chain for thisVUC */
+ nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
+ nftl->EUNtable[thisVUC] = targetEUN;
+
+ return targetEUN;
+}
+
+u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
+{
+ /* This is the part that needs some cleverness applied.
+ For now, I'm doing the minimum applicable to actually
+ get the thing to work.
+ Wear-levelling and other clever stuff needs to be implemented
+ and we also need to do some assessment of the results when
+ the system loses power half-way through the routine.
+ */
+ u16 LongestChain = 0;
+ u16 ChainLength = 0, thislen;
+ u16 chain, EUN;
+
+ for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) {
+ EUN = nftl->EUNtable[chain];
+ thislen = 0;
+
+ while (EUN <= nftl->lastEUN) {
+ thislen++;
+ //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
+ EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
+ if (thislen > 0xff00) {
+ printk("Endless loop in Virtual Chain %d: Unit %x\n",
+ chain, EUN);
+ }
+ if (thislen > 0xff10) {
+ /* Actually, don't return failure. Just ignore this chain and
+ get on with it. */
+ thislen = 0;
+ break;
+ }
+ }
+
+ if (thislen > ChainLength) {
+ //printk("New longest chain is %d with length %d\n", chain, thislen);
+ ChainLength = thislen;
+ LongestChain = chain;
+ }
+ }
+
+ if (ChainLength < 2) {
+ printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
+ "Failing request\n");
+ return 0xffff;
+ }
+
+ return NFTL_foldchain (nftl, LongestChain, pendingblock);
+}
+
+/* NFTL_findwriteunit: Return the unit number into which we can write
+ for this block. Make it available if it isn't already
+*/
+static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
+{
+ u16 lastEUN;
+ u16 thisVUC = block / (nftl->EraseSize / 512);
+ unsigned int writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
+ size_t retlen;
+ int silly, silly2 = 3;
+ struct nftl_oob oob;
+
+ do {
+ /* Scan the media to find a unit in the VUC which has
+ a free space for the block in question.
+ */
+
+ /* This condition catches the 0x[7f]fff cases, as well as
+ being a sanity check for past-end-of-media access
+ */
+ lastEUN = BLOCK_NIL;
+ writeEUN = nftl->EUNtable[thisVUC];
+ silly = MAX_LOOPS;
+ while (writeEUN <= nftl->lastEUN) {
+ struct nftl_bci bci;
+ size_t retlen;
+ unsigned int status;
+
+ lastEUN = writeEUN;
+
+ MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+ block , writeEUN, le16_to_cpu(bci.Status));
+
+ status = bci.Status | bci.Status1;
+ switch(status) {
+ case SECTOR_FREE:
+ return writeEUN;
+
+ case SECTOR_DELETED:
+ case SECTOR_USED:
+ case SECTOR_IGNORE:
+ break;
+ default:
+ // Invalid block. Don't use it any more. Must implement.
+ break;
+ }
+
+ if (!silly--) {
+ printk(KERN_WARNING
+ "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
+ return 0xffff;
+ }
+
+ /* Skip to next block in chain */
+ writeEUN = nftl->ReplUnitTable[writeEUN];
+ }
+
+ /* OK. We didn't find one in the existing chain, or there
+ is no existing chain. */
+
+ /* Try to find an already-free block */
+ writeEUN = NFTL_findfreeblock(nftl, 0);
+
+ if (writeEUN == BLOCK_NIL) {
+ /* That didn't work - there were no free blocks just
+ waiting to be picked up. We're going to have to fold
+ a chain to make room.
+ */
+
+ /* First remember the start of this chain */
+ //u16 startEUN = nftl->EUNtable[thisVUC];
+
+ //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.
+ If we get here, we've allocated more storage
+ space than actual media, or our makefreeblock
+ routine is missing something.
+ */
+ printk(KERN_WARNING "Cannot make free space.\n");
+ return BLOCK_NIL;
+ }
+ //printk("Restarting scan\n");
+ lastEUN = BLOCK_NIL;
+ continue;
+ }
+
+ /* We've found a free block. Insert it into the chain. */
+
+ if (lastEUN != BLOCK_NIL) {
+ thisVUC |= 0x8000; /* It's a replacement block */
+ } else {
+ /* The first block in a new chain */
+ nftl->EUNtable[thisVUC] = writeEUN;
+ }
+
+ /* set up the actual EUN we're writing into */
+ /* Both in our cache... */
+ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
+
+ /* ... and on the flash itself */
+ MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+
+ MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ /* we link the new block to the chain only after the
+ block is ready. It avoids the case where the chain
+ could point to a free block */
+ if (lastEUN != BLOCK_NIL) {
+ /* Both in our cache... */
+ nftl->ReplUnitTable[lastEUN] = writeEUN;
+ /* ... and on the flash itself */
+ MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
+ = cpu_to_le16(writeEUN);
+
+ MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+ return writeEUN;
+
+ } while (silly2--);
+
+ printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
+ thisVUC);
+ return 0xffff;
+}
+
+static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
+{
+ u16 writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ size_t retlen;
+ u8 eccbuf[6];
+
+ writeEUN = NFTL_findwriteunit(nftl, block);
+
+ if (writeEUN == BLOCK_NIL) {
+ printk(KERN_WARNING
+ "NFTL_writeblock(): Cannot find block to write to\n");
+ /* If we _still_ haven't got a block to use, we're screwed */
+ return 1;
+ }
+
+ MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 512, &retlen, (char *)buffer, (char *)eccbuf);
+ /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
+
+ return 0;
+}
+#endif /* CONFIG_NFTL_RW */
+
+static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
+{
+ u16 lastgoodEUN;
+ u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ unsigned int status;
+ int silly = MAX_LOOPS;
+ size_t retlen;
+ struct nftl_bci bci;
+
+ lastgoodEUN = BLOCK_NIL;
+
+ if (thisEUN != BLOCK_NIL) {
+ while (thisEUN < nftl->nb_blocks) {
+ if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+ status = bci.Status | bci.Status1;
+
+ switch (status) {
+ case SECTOR_FREE:
+ /* no modification of a sector should follow a free sector */
+ goto the_end;
+ case SECTOR_DELETED:
+ lastgoodEUN = BLOCK_NIL;
+ break;
+ case SECTOR_USED:
+ lastgoodEUN = thisEUN;
+ break;
+ case SECTOR_IGNORE:
+ break;
+ default:
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
+ break;
+ }
+
+ if (!silly--) {
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ block / (nftl->EraseSize / 512));
+ return 1;
+ }
+ thisEUN = nftl->ReplUnitTable[thisEUN];
+ }
+ }
+
+ the_end:
+ if (lastgoodEUN == BLOCK_NIL) {
+ /* the requested block is not on the media, return all 0x00 */
+ memset(buffer, 0, 512);
+ } else {
+ loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
+ size_t retlen;
+ u_char eccbuf[6];
+ if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf))
+ return -EIO;
+ }
+ return 0;
+}
+
+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) >> NFTL_PARTN_BITS];
+
+ if (!nftl) return -EINVAL;
+
+ switch (cmd) {
+ case HDIO_GETGEO: {
+ struct hd_geometry g;
+
+ g.heads = nftl->heads;
+ g.sectors = nftl->sectors;
+ g.cylinders = nftl->cylinders;
+ g.start = part_table[MINOR(inode->i_rdev)].start_sect;
+ return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
+ }
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
+
+ case BLKFLSBUF:
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ if (nftl->mtd->sync)
+ nftl->mtd->sync(nftl->mtd);
+ return 0;
+
+ 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<<NFTL_PARTN_BITS) - 1;
+ while (p-- > 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) >> NFTL_PARTN_BITS);
+#else
+ grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS,
+ 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+#endif
+ return 0;
+
+#if (LINUX_VERSION_CODE < 0x20303)
+ RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+#else
+ case BLKROSET:
+ case BLKROGET:
+ case BLKSSZGET:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+#endif
+
+ default:
+ return -EINVAL;
+ }
+}
+
+void nftl_request(RQFUNC_ARG)
+{
+ unsigned int dev, block, nsect;
+ struct NFTLrecord *nftl;
+ char *buffer;
+ struct request *req;
+ int res;
+
+ while (1) {
+ INIT_REQUEST; /* blk.h */
+ req = CURRENT;
+
+ /* We can do this because the generic code knows not to
+ touch the request at the head of the queue */
+ spin_unlock_irq(&io_request_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
+ DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
+ (req->cmd == READ) ? "Read " : "Write",
+ req->sector, req->current_nr_sectors);
+
+ dev = MINOR(req->rq_dev);
+ block = req->sector;
+ nsect = req->current_nr_sectors;
+ buffer = req->buffer;
+ res = 1; /* succeed */
+
+ if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) {
+ /* there is no such partition */
+ printk("nftl: bad minor number: device = %s\n",
+ kdevname(req->rq_dev));
+ res = 0; /* fail */
+ goto repeat;
+ }
+
+ nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
+ DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+ down(&nftl->mutex);
+ DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
+
+ if (block + nsect > part_table[dev].nr_sects) {
+ /* access past the end of device */
+ printk("nftl%c%d: bad access: block = %d, count = %d\n",
+ (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
+ up(&nftl->mutex);
+ res = 0; /* fail */
+ goto repeat;
+ }
+
+ block += part_table[dev].start_sect;
+
+ if (req->cmd == READ) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
+
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+ /* Read a single sector to req->buffer + (512 * i) */
+ if (NFTL_readblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
+ up(&nftl->mutex);
+ res = 0;
+ goto repeat;
+ }
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
+ up(&nftl->mutex);
+ goto repeat;
+ } else if (req->cmd == WRITE) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block,
+ req->nr_sectors);
+#ifdef CONFIG_NFTL_RW
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+ /* Read a single sector to req->buffer + (512 * i) */
+ if (NFTL_writeblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
+ up(&nftl->mutex);
+ res = 0;
+ goto repeat;
+ }
+ }
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
+#else
+ res = 0; /* Writes always fail */
+#endif /* CONFIG_NFTL_RW */
+ up(&nftl->mutex);
+ goto repeat;
+ } else {
+ DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
+ up(&nftl->mutex);
+ res = 0;
+ goto repeat;
+ }
+ repeat:
+ DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
+ spin_lock_irq(&io_request_lock);
+ end_request(res);
+ }
+}
+
+static int nftl_open(struct inode *ip, struct file *fp)
+{
+ int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS;
+ struct NFTLrecord *thisNFTL;
+ thisNFTL = NFTLs[nftlnum];
+
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
+
+#ifdef CONFIG_KMOD
+ if (!thisNFTL && nftlnum == 0) {
+ request_module("docprobe");
+ thisNFTL = NFTLs[nftlnum];
+ }
+#endif
+ if (!thisNFTL) {
+ DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
+ nftlnum, ip->i_rdev, ip, fp);
+ return -ENODEV;
+ }
+
+#ifndef CONFIG_NFTL_RW
+ if (fp->f_mode & FMODE_WRITE)
+ return -EROFS;
+#endif /* !CONFIG_NFTL_RW */
+
+ thisNFTL->usecount++;
+ MOD_INC_USE_COUNT;
+ if (!get_mtd_device(thisNFTL->mtd, -1)) {
+ MOD_DEC_USE_COUNT;
+ return /* -E'SBUGGEREDOFF */ -ENXIO;
+ }
+
+ return 0;
+}
+
+static int nftl_release(struct inode *inode, struct file *fp)
+{
+ struct NFTLrecord *thisNFTL;
+
+ thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
+
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+
+ invalidate_device(inode->i_rdev, 1);
+
+ if (thisNFTL->mtd->sync)
+ thisNFTL->mtd->sync(thisNFTL->mtd);
+ thisNFTL->usecount--;
+ MOD_DEC_USE_COUNT;
+
+ put_mtd_device(thisNFTL->mtd);
+
+ return 0;
+}
+#if LINUX_VERSION_CODE < 0x20326
+static struct file_operations nftl_fops = {
+ read: block_read,
+ write: block_write,
+ ioctl: nftl_ioctl,
+ open: nftl_open,
+ release: nftl_release,
+ fsync: block_fsync,
+};
+#else
+static struct block_device_operations nftl_fops =
+{
+ open: nftl_open,
+ release: nftl_release,
+ ioctl: nftl_ioctl
+};
+#endif
+
+
+
+/****************************************************************************
+ *
+ * Module stuff
+ *
+ ****************************************************************************/
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define init_nftl init_module
+#define cleanup_nftl cleanup_module
+#endif
+
+static struct mtd_notifier nftl_notifier = {
+ add: NFTL_notify_add,
+ remove: NFTL_notify_remove
+};
+
+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: nftlcore.c,v 1.73 2001/06/09 01:09:43 dwmw2 Exp $\n");
+#endif
+
+ if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
+ printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
+ return -EBUSY;
+ } else {
+#if LINUX_VERSION_CODE < 0x20320
+ blk_dev[MAJOR_NR].request_fn = nftl_request;
+#else
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
+#endif
+ /* set block size to 1kB each */
+ for (i = 0; i < 256; i++) {
+ nftl_blocksizes[i] = 1024;
+ }
+ blksize_size[MAJOR_NR] = nftl_blocksizes;
+
+ nftl_gendisk.next = gendisk_head;
+ gendisk_head = &nftl_gendisk;
+ }
+
+ register_mtd_user(&nftl_notifier);
+
+ return 0;
+}
+
+static void __exit cleanup_nftl(void)
+{
+ struct gendisk *gd, **gdp;
+
+ unregister_mtd_user(&nftl_notifier);
+ unregister_blkdev(MAJOR_NR, "nftl");
+
+#if LINUX_VERSION_CODE < 0x20320
+ blk_dev[MAJOR_NR].request_fn = 0;
+#else
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+#endif
+
+ /* remove ourself from generic harddisk list
+ FIXME: why can't I found this partition on /proc/partition */
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &nftl_gendisk) {
+ gd = *gdp; *gdp = gd->next;
+ break;
+ }
+}
+
+module_init(init_nftl);
+module_exit(cleanup_nftl);
* 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
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
}
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);
{
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,
+++ /dev/null
-/*
- * $Id: nora.c,v 1.17 2000/12/03 19:32:21 dwmw2 Exp $
- *
- * This is so simple I love it.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-
-
-#define WINDOW_ADDR 0xd0000000
-#define WINDOW_SIZE 0x04000000
-
-static struct mtd_info *mymtd;
-
-__u8 nora_read8(struct map_info *map, unsigned long ofs)
-{
- return *(__u8 *)(WINDOW_ADDR + ofs);
-}
-
-__u16 nora_read16(struct map_info *map, unsigned long ofs)
-{
- return *(__u16 *)(WINDOW_ADDR + ofs);
-}
-
-__u32 nora_read32(struct map_info *map, unsigned long ofs)
-{
- return *(__u32 *)(WINDOW_ADDR + ofs);
-}
-
-void nora_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
- memcpy(to, (void *)(WINDOW_ADDR + from), len);
-}
-
-void nora_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- *(__u8 *)(WINDOW_ADDR + adr) = d;
-}
-
-void nora_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- *(__u16 *)(WINDOW_ADDR + adr) = d;
-}
-
-void nora_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- *(__u32 *)(WINDOW_ADDR + adr) = d;
-}
-
-void nora_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
- memcpy((void *)(WINDOW_ADDR + to), from, len);
-}
-
-struct map_info nora_map = {
- name: "NORA",
- size: WINDOW_SIZE,
- buswidth: 2,
- read8: nora_read8,
- read16: nora_read16,
- read32: nora_read32,
- copy_from: nora_copy_from,
- write8: nora_write8,
- write16: nora_write16,
- write32: nora_write32,
- copy_to: nora_copy_to
-};
-
-
-static int nora_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int nora_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
- return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int nora_mtd_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- instr->addr += (unsigned long)mtd->priv;
- return mymtd->erase(mymtd, instr);
-}
-
-static void nora_mtd_sync (struct mtd_info *mtd)
-{
- mymtd->sync(mymtd);
-}
-
-static int nora_mtd_suspend (struct mtd_info *mtd)
-{
- return mymtd->suspend(mymtd);
-}
-
-static void nora_mtd_resume (struct mtd_info *mtd)
-{
- mymtd->resume(mymtd);
-}
-
-
-static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */
- {
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x60000,
- erasesize: 0x20000,
- name: "NORA boot firmware",
- module: THIS_MODULE,
- erase: nora_mtd_erase,
- read: nora_mtd_read,
- write: nora_mtd_write,
- suspend: nora_mtd_suspend,
- resume: nora_mtd_resume,
- sync: nora_mtd_sync,
- priv: (void *)0
- },
- {
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x0a0000,
- erasesize: 0x20000,
- name: "NORA kernel",
- module: THIS_MODULE,
- erase: nora_mtd_erase,
- read: nora_mtd_read,
- write: nora_mtd_write,
- suspend: nora_mtd_suspend,
- resume: nora_mtd_resume,
- sync: nora_mtd_sync,
- priv: (void *)0x60000
- },
- {
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x900000,
- erasesize: 0x20000,
- name: "NORA root filesystem",
- module: THIS_MODULE,
- erase: nora_mtd_erase,
- read: nora_mtd_read,
- write: nora_mtd_write,
- suspend: nora_mtd_suspend,
- resume: nora_mtd_resume,
- sync: nora_mtd_sync,
- priv: (void *)0x100000
- },
- {
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x1600000,
- erasesize: 0x20000,
- name: "NORA second filesystem",
- module: THIS_MODULE,
- erase: nora_mtd_erase,
- read: nora_mtd_read,
- write: nora_mtd_write,
- suspend: nora_mtd_suspend,
- resume: nora_mtd_resume,
- sync: nora_mtd_sync,
- priv: (void *)0xa00000
- }
-};
-
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_nora init_module
-#define cleanup_nora cleanup_module
-#endif
-
-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);
- if (mymtd) {
-#ifdef MODULE
- mymtd->module = &__this_module;
-#endif
-
- add_mtd_device(&nora_mtds[2]);
- add_mtd_device(&nora_mtds[0]);
- add_mtd_device(&nora_mtds[1]);
- add_mtd_device(&nora_mtds[3]);
- return 0;
- }
-
- return -ENXIO;
-}
-
-static void __exit cleanup_nora(void)
-{
- if (mymtd) {
- del_mtd_device(&nora_mtds[3]);
- del_mtd_device(&nora_mtds[1]);
- del_mtd_device(&nora_mtds[0]);
- del_mtd_device(&nora_mtds[2]);
- map_destroy(mymtd);
- }
-}
-
-module_init(init_nora);
-module_exit(cleanup_nora);
+++ /dev/null
-// $Id: octagon-5066.c,v 1.12.2.1 2001/02/15 10:12:48 dwmw2 Exp $
-/* ######################################################################
-
- Octagon 5066 MTD Driver.
-
- The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It
- comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that
- is replacable by flash. Both units are mapped through a multiplexer
- into a 32k memory window at 0xe8000. The control register for the
- multiplexing unit is located at IO 0x208 with a bit map of
- 0-5 Page Selection in 32k increments
- 6-7 Device selection:
- 00 SSD off
- 01 SSD 0 (Socket)
- 10 SSD 1 (Flash chip)
- 11 undefined
-
- On each SSD, the first 128k is reserved for use by the bios
- (actually it IS the bios..) This only matters if you are booting off the
- flash, you must not put a file system starting there.
-
- The driver tries to do a detection algorithm to guess what sort of devices
- are plugged into the sockets.
-
- ##################################################################### */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <asm/io.h>
-
-#include <linux/mtd/map.h>
-
-#define WINDOW_START 0xe8000
-#define WINDOW_LENGTH 0x8000
-#define WINDOW_SHIFT 27
-#define WINDOW_MASK 0x7FFF
-#define PAGE_IO 0x208
-
-static volatile char page_n_dev = 0;
-static unsigned long iomapadr;
-static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED;
-
-/*
- * We use map_priv_1 to identify which device we are.
- */
-
-static void __oct5066_page(struct map_info *map, __u8 byte)
-{
- outb(byte,PAGE_IO);
- page_n_dev = byte;
-}
-
-static inline void oct5066_page(struct map_info *map, unsigned long ofs)
-{
- __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT);
-
- if (page_n_dev != byte)
- __oct5066_page(map, byte);
-}
-
-
-static __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
-{
- __u8 ret;
- spin_lock(&oct5066_spin);
- oct5066_page(map, ofs);
- ret = readb(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
- return ret;
-}
-
-static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&oct5066_spin);
- oct5066_page(map, ofs);
- ret = readw(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
- return ret;
-}
-
-static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&oct5066_spin);
- oct5066_page(map, ofs);
- ret = readl(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
- return ret;
-}
-
-static void oct5066_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(&oct5066_spin);
- oct5066_page(map, from);
- memcpy_fromio(to, iomapadr + from, thislen);
- spin_unlock(&oct5066_spin);
- to += thislen;
- from += thislen;
- len -= thislen;
- }
-}
-
-static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- spin_lock(&oct5066_spin);
- oct5066_page(map, adr);
- writeb(d, iomapadr + (adr & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
-}
-
-static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- spin_lock(&oct5066_spin);
- oct5066_page(map, adr);
- writew(d, iomapadr + (adr & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
-}
-
-static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- spin_lock(&oct5066_spin);
- oct5066_page(map, adr);
- writel(d, iomapadr + (adr & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
-}
-
-static void oct5066_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(&oct5066_spin);
- oct5066_page(map, to);
- memcpy_toio(iomapadr + to, from, thislen);
- spin_unlock(&oct5066_spin);
- to += thislen;
- from += thislen;
- len -= thislen;
- }
-}
-
-static struct map_info oct5066_map[2] = {
- {
- name: "Octagon 5066 Socket",
- size: 512 * 1024,
- buswidth: 1,
- read8: oct5066_read8,
- read16: oct5066_read16,
- read32: oct5066_read32,
- copy_from: oct5066_copy_from,
- write8: oct5066_write8,
- write16: oct5066_write16,
- write32: oct5066_write32,
- copy_to: oct5066_copy_to,
- map_priv_1: 1<<6
- },
- {
- name: "Octagon 5066 Internal Flash",
- size: 2 * 1024 * 1024,
- buswidth: 1,
- read8: oct5066_read8,
- read16: oct5066_read16,
- read32: oct5066_read32,
- copy_from: oct5066_copy_from,
- write8: oct5066_write8,
- write16: oct5066_write16,
- write32: oct5066_write32,
- copy_to: oct5066_copy_to,
- map_priv_1: 2<<6
- }
-};
-
-static struct mtd_info *oct5066_mtd[2] = {NULL, NULL};
-
-// OctProbe - Sense if this is an octagon card
-// ---------------------------------------------------------------------
-/* Perform a simple validity test, we map the window select SSD0 and
- 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()
-{
- unsigned int Base = (1 << 6);
- unsigned long I;
- unsigned long Values[10];
- for (I = 0; I != 20; I++)
- {
- outb(Base + (I%10),PAGE_IO);
- if (I < 10)
- {
- // Record the value and check for uniqueness
- Values[I%10] = readl(iomapadr);
- if (I > 0 && Values[I%10] == Values[0])
- return -EAGAIN;
- }
- else
- {
- // Make sure we get the same values on the second pass
- if (Values[I%10] != readl(iomapadr))
- return -EAGAIN;
- }
- }
- return 0;
-}
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_oct5066 init_module
-#define cleanup_oct5066 cleanup_module
-#endif
-
-void cleanup_oct5066(void)
-{
- int i;
- for (i=0; i<2; i++) {
- if (oct5066_mtd[i]) {
- del_mtd_device(oct5066_mtd[i]);
- map_destroy(oct5066_mtd[i]);
- }
- }
- iounmap((void *)iomapadr);
- release_region(PAGE_IO,1);
-}
-
-int __init init_oct5066(void)
-{
- int i;
-
- // Do an autoprobe sequence
- if (check_region(PAGE_IO,1) != 0)
- {
- printk("5066: Page Register in Use\n");
- return -EAGAIN;
- }
- iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
- if (!iomapadr) {
- printk("Failed to ioremap memory region\n");
- return -EIO;
- }
- if (OctProbe() != 0)
- {
- printk("5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
- iounmap((void *)iomapadr);
- return -EAGAIN;
- }
-
- request_region(PAGE_IO,1,"Octagon SSD");
-
- // Print out our little header..
- printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START,
- WINDOW_START+WINDOW_LENGTH);
-
- for (i=0; i<2; i++) {
- oct5066_mtd[i] = do_cfi_probe(&oct5066_map[i]);
- if (!oct5066_mtd[i])
- oct5066_mtd[i] = do_jedec_probe(&oct5066_map[i]);
- if (!oct5066_mtd[i])
- oct5066_mtd[i] = do_ram_probe(&oct5066_map[i]);
- if (!oct5066_mtd[i])
- oct5066_mtd[i] = do_rom_probe(&oct5066_map[i]);
- if (oct5066_mtd[i]) {
- oct5066_mtd[i]->module = THIS_MODULE;
- add_mtd_device(oct5066_mtd[i]);
- }
- }
-
- if (!oct5066_mtd[0] && !oct5066_mtd[1]) {
- cleanup_oct5066();
- return -ENXIO;
- }
-
- return 0;
-}
-
-module_init(init_oct5066);
-module_exit(cleanup_oct5066);
+++ /dev/null
-/*
- * $Id: physmap.c,v 1.8 2000/11/27 08:50:22 dwmw2 Exp $
- *
- * Normal mappings of chips in physical memory
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/config.h>
-
-
-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
-
-static struct mtd_info *mymtd;
-
-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
-{
- return readb(map->map_priv_1 + ofs);
-}
-
-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
-{
- return readw(map->map_priv_1 + ofs);
-}
-
-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
-{
- return readl(map->map_priv_1 + ofs);
-}
-
-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
- memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- writeb(d, map->map_priv_1 + adr);
-}
-
-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- writew(d, map->map_priv_1 + adr);
-}
-
-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- writel(d, map->map_priv_1 + adr);
-}
-
-void physmap_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 physmap_map = {
- name: "Physically mapped flash",
- size: WINDOW_SIZE,
- buswidth: BUSWIDTH,
- read8: physmap_read8,
- read16: physmap_read16,
- read32: physmap_read32,
- copy_from: physmap_copy_from,
- write8: physmap_write8,
- write16: physmap_write16,
- write32: physmap_write32,
- copy_to: physmap_copy_to
-};
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_physmap init_module
-#define cleanup_physmap cleanup_module
-#endif
-
-int __init init_physmap(void)
-{
- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
-
- if (!physmap_map.map_priv_1) {
- printk("Failed to ioremap\n");
- return -EIO;
- }
- mymtd = do_cfi_probe(&physmap_map);
- if (mymtd) {
-#ifdef MODULE
- mymtd->module = &__this_module;
-#endif
- add_mtd_device(mymtd);
- return 0;
- }
-
- iounmap((void *)physmap_map.map_priv_1);
- return -ENXIO;
-}
-
-static void __exit cleanup_physmap(void)
-{
- if (mymtd) {
- del_mtd_device(mymtd);
- map_destroy(mymtd);
- }
- if (physmap_map.map_priv_1) {
- iounmap((void *)physmap_map.map_priv_1);
- physmap_map.map_priv_1 = 0;
- }
-}
-
-module_init(init_physmap);
-module_exit(cleanup_physmap);
-
+++ /dev/null
-/*
- * $Id: pmc551.c,v 1.11 2000/11/23 13:40:12 dwmw2 Exp $
- *
- * PMC551 PCI Mezzanine Ram Device
- *
- * Author:
- * Mark Ferrell <mferrell@mvista.com>
- * 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
- *
- * 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.
- *
- * 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.
- *
- * Credits:
- * Saeed Karamooz <saeed@ramix.com> 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
- * <dhinds@allegro.stanford.edu> 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.
- *
- * Corey Minyard <minyard@nortelnetworks.com>
- * * 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.
- *
- * 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.
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/major.h>
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <stdarg.h>
-#include <linux/pci.h>
-
-#ifndef CONFIG_PCI
-#error Enable PCI in your kernel config
-#endif
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/pmc551.h>
-#include <linux/mtd/compatmac.h>
-
-#if LINUX_VERSION_CODE > 0x20300
-#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
-#else
-#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
-#endif
-
-static struct mtd_info *pmc551list = NULL;
-
-static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- struct mypriv *priv = mtd->priv;
- u32 start_addr_highbits;
- u32 end_addr_highbits;
- u32 start_addr_lowbits;
- u32 end_addr_lowbits;
- unsigned long end;
-
- end = instr->addr + instr->len;
-
- /* Is it too much memory? The second check find if we wrap around
- past the end of a u32. */
- if ((end > mtd->size) || (end < instr->addr)) {
- return -EINVAL;
- }
-
- start_addr_highbits = instr->addr & PMC551_ADDR_HIGH_MASK;
- end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
- start_addr_lowbits = instr->addr & PMC551_ADDR_LOW_MASK;
- end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
-
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- (priv->mem_map0_base_val
- | start_addr_highbits));
- if (start_addr_highbits == end_addr_highbits) {
- /* The whole thing fits within one access, so just one shot
- will do it. */
- memset(priv->start + start_addr_lowbits,
- 0xff,
- instr->len);
- } else {
- /* We have to do multiple writes to get all the data
- written. */
- memset(priv->start + start_addr_lowbits,
- 0xff,
- priv->aperture_size - start_addr_lowbits);
- start_addr_highbits += priv->aperture_size;
- while (start_addr_highbits != end_addr_highbits) {
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- (priv->mem_map0_base_val
- | start_addr_highbits));
- memset(priv->start,
- 0xff,
- priv->aperture_size);
- start_addr_highbits += priv->aperture_size;
- }
- priv->curr_mem_map0_val = (priv->mem_map0_base_val
- | start_addr_highbits);
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
- memset(priv->start,
- 0xff,
- end_addr_lowbits);
- }
-
- instr->state = MTD_ERASE_DONE;
-
- if (instr->callback) {
- (*(instr->callback))(instr);
- }
-
- return 0;
-}
-
-
-static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr)
-{}
-
-
-static int pmc551_read (struct mtd_info *mtd,
- loff_t from,
- size_t len,
- size_t *retlen,
- u_char *buf)
-{
- struct mypriv *priv = (struct mypriv *)mtd->priv;
- u32 start_addr_highbits;
- u32 end_addr_highbits;
- u32 start_addr_lowbits;
- u32 end_addr_lowbits;
- unsigned long end;
- u_char *copyto = buf;
-
-
- /* Is it past the end? */
- if (from > mtd->size) {
- return -EINVAL;
- }
-
- end = from + len;
- start_addr_highbits = from & PMC551_ADDR_HIGH_MASK;
- end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
- start_addr_lowbits = from & PMC551_ADDR_LOW_MASK;
- end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
-
-
- /* Only rewrite the first value if it doesn't match our current
- values. Most operations are on the same page as the previous
- value, so this is a pretty good optimization. */
- if (priv->curr_mem_map0_val !=
- (priv->mem_map0_base_val | start_addr_highbits)) {
- priv->curr_mem_map0_val = (priv->mem_map0_base_val
- | start_addr_highbits);
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
- }
-
- if (start_addr_highbits == end_addr_highbits) {
- /* The whole thing fits within one access, so just one shot
- will do it. */
- memcpy(copyto,
- priv->start + start_addr_lowbits,
- len);
- copyto += len;
- } else {
- /* We have to do multiple writes to get all the data
- written. */
- memcpy(copyto,
- priv->start + start_addr_lowbits,
- priv->aperture_size - start_addr_lowbits);
- copyto += priv->aperture_size - start_addr_lowbits;
- start_addr_highbits += priv->aperture_size;
- while (start_addr_highbits != end_addr_highbits) {
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- (priv->mem_map0_base_val
- | start_addr_highbits));
- memcpy(copyto,
- priv->start,
- priv->aperture_size);
- copyto += priv->aperture_size;
- start_addr_highbits += priv->aperture_size;
- if (start_addr_highbits >= mtd->size) {
- /* Make sure we have the right value here. */
- priv->curr_mem_map0_val
- = (priv->mem_map0_base_val
- | start_addr_highbits);
- goto out;
- }
- }
- priv->curr_mem_map0_val = (priv->mem_map0_base_val
- | start_addr_highbits);
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
- memcpy(copyto,
- priv->start,
- end_addr_lowbits);
- copyto += end_addr_lowbits;
- }
-
-out:
- *retlen = copyto - buf;
- return 0;
-}
-
-static int pmc551_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;
- u32 start_addr_highbits;
- u32 end_addr_highbits;
- u32 start_addr_lowbits;
- u32 end_addr_lowbits;
- unsigned long end;
- const u_char *copyfrom = buf;
-
-
- /* Is it past the end? */
- if (to > mtd->size) {
- return -EINVAL;
- }
-
- end = to + len;
- start_addr_highbits = to & PMC551_ADDR_HIGH_MASK;
- end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
- start_addr_lowbits = to & PMC551_ADDR_LOW_MASK;
- end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
-
-
- /* Only rewrite the first value if it doesn't match our current
- values. Most operations are on the same page as the previous
- value, so this is a pretty good optimization. */
- if (priv->curr_mem_map0_val !=
- (priv->mem_map0_base_val | start_addr_highbits)) {
- priv->curr_mem_map0_val = (priv->mem_map0_base_val
- | start_addr_highbits);
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
- }
-
- if (start_addr_highbits == end_addr_highbits) {
- /* The whole thing fits within one access, so just one shot
- will do it. */
- memcpy(priv->start + start_addr_lowbits,
- copyfrom,
- len);
- copyfrom += len;
- } else {
- /* We have to do multiple writes to get all the data
- written. */
- memcpy(priv->start + start_addr_lowbits,
- copyfrom,
- priv->aperture_size - start_addr_lowbits);
- copyfrom += priv->aperture_size - start_addr_lowbits;
- start_addr_highbits += priv->aperture_size;
- while (start_addr_highbits != end_addr_highbits) {
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- (priv->mem_map0_base_val
- | start_addr_highbits));
- memcpy(priv->start,
- copyfrom,
- priv->aperture_size);
- copyfrom += priv->aperture_size;
- start_addr_highbits += priv->aperture_size;
- if (start_addr_highbits >= mtd->size) {
- /* Make sure we have the right value here. */
- priv->curr_mem_map0_val
- = (priv->mem_map0_base_val
- | start_addr_highbits);
- goto out;
- }
- }
- priv->curr_mem_map0_val = (priv->mem_map0_base_val
- | start_addr_highbits);
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
- memcpy(priv->start,
- copyfrom,
- end_addr_lowbits);
- copyfrom += end_addr_lowbits;
- }
-
-out:
- *retlen = copyfrom - buf;
- return 0;
-}
-
-/*
- * Fixup routines for the V370PDC
- * PCI device ID 0x020011b0
- *
- * This function basicly kick starts the DRAM oboard the card and gets it
- * ready to be used. Before this is done the device reads VERY erratic, so
- * much that it can crash the Linux 2.2.x series kernels when a user cat's
- * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL
- * register. FIXME: stop spinning on registers .. must implement a timeout
- * mechanism
- * returns the size of the memory region found.
- */
-static u32 fixup_pmc551 (struct pci_dev *dev)
-{
-#ifdef CONFIG_MTD_PMC551_BUGFIX
- u32 dram_data;
-#endif
- u32 size, dcmd, cfg, dtmp;
- u16 cmd, tmp, i;
- u8 bcmd, counter;
-
- /* Sanity Check */
- if(!dev) {
- return -ENODEV;
- }
-
- /*
- * Attempt to reset the card
- * FIXME: Stop Spinning registers
- */
- counter=0;
- /* unlock registers */
- pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5 );
- /* read in old data */
- pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
- /* bang the reset line up and down for a few */
- for(i=0;i<10;i++) {
- counter=0;
- bcmd &= ~0x80;
- while(counter++ < 100) {
- pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
- }
- counter=0;
- bcmd |= 0x80;
- while(counter++ < 100) {
- pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
- }
- }
- bcmd |= (0x40|0x20);
- pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
-
- /*
- * Take care and turn off the memory on the device while we
- * tweak the configurations
- */
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- tmp = cmd & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY);
- pci_write_config_word(dev, PCI_COMMAND, tmp);
-
- /*
- * Disable existing aperture before probing memory size
- */
- pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd);
- dtmp=(dcmd|PMC551_PCI_MEM_MAP_ENABLE|PMC551_PCI_MEM_MAP_REG_EN);
- pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp);
- /*
- * Grab old BAR0 config so that we can figure out memory size
- * This is another bit of kludge going on. The reason for the
- * redundancy is I am hoping to retain the original configuration
- * previously assigned to the card by the BIOS or some previous
- * fixup routine in the kernel. So we read the old config into cfg,
- * then write all 1's to the memory space, read back the result into
- * "size", and then write back all the old config.
- */
- pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &cfg );
-#ifndef CONFIG_MTD_PMC551_BUGFIX
- pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, ~0 );
- pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &size );
- pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
- size=~(size&PCI_BASE_ADDRESS_MEM_MASK)+1;
-#else
- /*
- * Get the size of the memory by reading all the DRAM size values
- * and adding them up.
- *
- * KLUDGE ALERT: the boards we are using have invalid column and
- * row mux values. We fix them here, but this will break other
- * memory configurations.
- */
- pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data);
- size = PMC551_DRAM_BLK_GET_SIZE(dram_data);
- dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
- dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
- pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data);
-
- pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data);
- size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
- dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
- dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
- pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data);
-
- pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data);
- size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
- dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
- dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
- pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data);
-
- pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data);
- size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
- dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
- dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
- pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data);
-
- /*
- * Oops .. something went wrong
- */
- if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) {
- return -ENODEV;
- }
-#endif /* CONFIG_MTD_PMC551_BUGFIX */
-
- if ((cfg&PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
- return -ENODEV;
- }
-
- /*
- * Precharge Dram
- */
- pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 );
- pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf );
-
- /*
- * Wait until command has gone through
- * FIXME: register spinning issue
- */
- do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd );
- if(counter++ > 100)break;
- } while ( (PCI_COMMAND_IO) & cmd );
-
- /*
- * Turn on auto refresh
- * The loop is taken directly from Ramix's example code. I assume that
- * this must be held high for some duration of time, but I can find no
- * documentation refrencing the reasons why.
- *
- */
- for ( i = 1; i<=8 ; i++) {
- pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df);
-
- /*
- * Make certain command has gone through
- * FIXME: register spinning issue
- */
- counter=0;
- do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
- if(counter++ > 100)break;
- } while ( (PCI_COMMAND_IO) & cmd );
- }
-
- pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020);
- pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff);
-
- /*
- * Wait until command completes
- * FIXME: register spinning issue
- */
- counter=0;
- do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd);
- if(counter++ > 100)break;
- } while ( (PCI_COMMAND_IO) & cmd );
-
- pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd);
- dcmd |= 0x02000000;
- pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd);
-
- /*
- * Check to make certain fast back-to-back, if not
- * then set it so
- */
- pci_read_config_word( dev, PCI_STATUS, &cmd);
- if((cmd&PCI_COMMAND_FAST_BACK) == 0) {
- cmd |= PCI_COMMAND_FAST_BACK;
- pci_write_config_word( dev, PCI_STATUS, cmd);
- }
-
- /*
- * Check to make certain the DEVSEL is set correctly, this device
- * has a tendancy to assert DEVSEL and TRDY when a write is performed
- * to the memory when memory is read-only
- */
- if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) {
- cmd &= ~PCI_STATUS_DEVSEL_MASK;
- pci_write_config_word( dev, PCI_STATUS, cmd );
- }
- /*
- * 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
- */
- cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
- pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
-
- /*
- * Turn PCI memory and I/O bus access back on
- */
- pci_write_config_word( dev, PCI_COMMAND,
- PCI_COMMAND_MEMORY | PCI_COMMAND_IO );
-#ifdef CONFIG_MTD_PMC551_DEBUG
- /*
- * Some screen fun
- */
- printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%lx\n",
- (size<1024)?size:(size<1048576)?size/1024:size/1024/1024,
- (size<1024)?'B':(size<1048576)?'K':'M',
- size, ((dcmd&(0x1<<3)) == 0)?"non-":"",
- PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
-
- /*
- * Check to see the state of the memory
- */
- pci_read_config_dword( dev, PMC551_DRAM_BLK0, &dcmd );
- printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n"
- "pmc551: DRAM_BLK0 Size: %d at %d\n"
- "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n",
- (((0x1<<1)&dcmd) == 0)?"RW":"RO",
- (((0x1<<0)&dcmd) == 0)?"Off":"On",
- PMC551_DRAM_BLK_GET_SIZE(dcmd),
- ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
-
- pci_read_config_dword( dev, PMC551_DRAM_BLK1, &dcmd );
- printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n"
- "pmc551: DRAM_BLK1 Size: %d at %d\n"
- "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n",
- (((0x1<<1)&dcmd) == 0)?"RW":"RO",
- (((0x1<<0)&dcmd) == 0)?"Off":"On",
- PMC551_DRAM_BLK_GET_SIZE(dcmd),
- ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
-
- pci_read_config_dword( dev, PMC551_DRAM_BLK2, &dcmd );
- printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n"
- "pmc551: DRAM_BLK2 Size: %d at %d\n"
- "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n",
- (((0x1<<1)&dcmd) == 0)?"RW":"RO",
- (((0x1<<0)&dcmd) == 0)?"Off":"On",
- PMC551_DRAM_BLK_GET_SIZE(dcmd),
- ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
-
- pci_read_config_dword( dev, PMC551_DRAM_BLK3, &dcmd );
- printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n"
- "pmc551: DRAM_BLK3 Size: %d at %d\n"
- "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n",
- (((0x1<<1)&dcmd) == 0)?"RW":"RO",
- (((0x1<<0)&dcmd) == 0)?"Off":"On",
- PMC551_DRAM_BLK_GET_SIZE(dcmd),
- ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
-
- pci_read_config_word( dev, PCI_COMMAND, &cmd );
- printk( KERN_DEBUG "pmc551: Memory Access %s\n",
- (((0x1<<1)&cmd) == 0)?"off":"on" );
- printk( KERN_DEBUG "pmc551: I/O Access %s\n",
- (((0x1<<0)&cmd) == 0)?"off":"on" );
-
- pci_read_config_word( dev, PCI_STATUS, &cmd );
- printk( KERN_DEBUG "pmc551: Devsel %s\n",
- ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast":
- ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium":
- ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" );
-
- printk( KERN_DEBUG "pmc551: %sFast Back-to-Back\n",
- ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" );
-
- pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
- printk( KERN_DEBUG "pmc551: EEPROM is under %s control\n"
- "pmc551: System Control Register is %slocked to PCI access\n"
- "pmc551: System Control Register is %slocked to EEPROM access\n",
- (bcmd&0x1)?"software":"hardware",
- (bcmd&0x20)?"":"un", (bcmd&0x40)?"":"un");
-#endif
- return size;
-}
-
-/*
- * Kernel version specific module stuffages
- */
-#if LINUX_VERSION_CODE < 0x20211
-#ifdef MODULE
-#define init_pmc551 init_module
-#define cleanup_pmc551 cleanup_module
-#endif
-#define __exit
-#endif
-
-#if defined(MODULE)
-MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>");
-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(asize, "i");
-MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1M-1024M]");
-#endif
-/*
- * Stuff these outside the ifdef so as to not bust compiled in driver support
- */
-static int msize=0;
-#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
-static int asize=CONFIG_MTD_PMC551_APERTURE_SIZE
-#else
-static int asize=0;
-#endif
-
-/*
- * PMC551 Card Initialization
- */
-int __init init_pmc551(void)
-{
- struct pci_dev *PCI_Device = NULL;
- struct mypriv *priv;
- int count, found=0;
- struct mtd_info *mtd;
- u32 length = 0;
-
- if(msize) {
- if (msize < 6 || msize > 11 ) {
- printk(KERN_NOTICE "pmc551: Invalid memory size\n");
- return -ENODEV;
- }
- msize = (512*1024)<<msize;
- }
-
- if(asize) {
- if (asize < 1 || asize > 11 ) {
- printk(KERN_NOTICE "pmc551: Invalid aperture size\n");
- return -ENODEV;
- }
- asize = (512*1024)<<asize;
- }
-
- printk(KERN_INFO PMC551_VERSION);
-
- if(!pci_present()) {
- printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
- return -ENODEV;
- }
-
- /*
- * PCU-bus chipset probe.
- */
- for( count = 0; count < MAX_MTD_DEVICES; count++ ) {
-
- if ( (PCI_Device = pci_find_device( PCI_VENDOR_ID_V3_SEMI,
- PCI_DEVICE_ID_V3_SEMI_V370PDC, PCI_Device ) ) == NULL) {
- break;
- }
-
- printk(KERN_NOTICE "pmc551: Found PCI V370PDC IRQ:%d\n",
- PCI_Device->irq);
-
- /*
- * The PMC551 device acts VERY weird if you don't init it
- * first. i.e. it will not correctly report devsel. If for
- * some reason the sdram is in a wrote-protected state the
- * device will DEVSEL when it is written to causing problems
- * with the oldproc.c driver in
- * some kernels (2.2.*)
- */
- if((length = fixup_pmc551(PCI_Device)) <= 0) {
- printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n");
- break;
- }
- if(msize) {
- length = msize;
- printk(KERN_NOTICE "pmc551: Using specified memory size 0x%x\n", length);
- }
-
- mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
- if (!mtd) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
- break;
- }
-
- memset(mtd, 0, sizeof(struct mtd_info));
-
- priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL);
- if (!priv) {
- printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
- kfree(mtd);
- break;
- }
- memset(priv, 0, sizeof(*priv));
- mtd->priv = priv;
-
- priv->dev = PCI_Device;
- if(asize) {
- if(asize > length) {
- asize=length;
- printk(KERN_NOTICE "pmc551: reducing aperture size to fit memory [0x%x]\n",asize);
- } else {
- printk(KERN_NOTICE "pmc551: Using specified aperture size 0x%x\n", asize);
- }
- priv->aperture_size = asize;
- } else {
- priv->aperture_size = length;
- }
- priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device)
- & PCI_BASE_ADDRESS_MEM_MASK),
- priv->aperture_size);
-
- /*
- * Due to the dynamic nature of the code, we need to figure
- * this out in order to stuff the register to set the proper
- * aperture size. If you know of an easier way to do this then
- * PLEASE help yourself.
- *
- * Not with bloody floating point, you don't. Consider yourself
- * duly LARTed. dwmw2.
- */
- {
- u32 size;
- u16 bits;
- size = priv->aperture_size>>20;
- for(bits=0;!(size&0x01)&&size>0;bits++,size=size>>1);
- //size=((u32)((log10(priv->aperture_size)/.30103)-19)<<4);
- priv->mem_map0_base_val = (PMC551_PCI_MEM_MAP_REG_EN
- | PMC551_PCI_MEM_MAP_ENABLE
- | size);
-#ifdef CONFIG_MTD_PMC551_DEBUG
- printk(KERN_NOTICE "pmc551: aperture set to %d[%d]\n",
- size, size>>4);
-#endif
- }
- priv->curr_mem_map0_val = priv->mem_map0_base_val;
-
- pci_write_config_dword ( priv->dev,
- PMC551_PCI_MEM_MAP0,
- priv->curr_mem_map0_val);
-
- mtd->size = length;
- mtd->flags = (MTD_CLEAR_BITS
- | MTD_SET_BITS
- | MTD_WRITEB_WRITEABLE
- | MTD_VOLATILE);
- mtd->erase = pmc551_erase;
- mtd->point = NULL;
- mtd->unpoint = pmc551_unpoint;
- mtd->read = pmc551_read;
- mtd->write = pmc551_write;
- mtd->module = THIS_MODULE;
- mtd->type = MTD_RAM;
- mtd->name = "PMC551 RAM board";
- mtd->erasesize = 0x10000;
-
- if (add_mtd_device(mtd)) {
- printk(KERN_NOTICE "pmc551: Failed to register new device\n");
- iounmap(priv->start);
- kfree(mtd->priv);
- kfree(mtd);
- break;
- }
- printk(KERN_NOTICE "Registered pmc551 memory device.\n");
- printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
- priv->aperture_size/1024/1024,
- priv->start,
- priv->start + priv->aperture_size);
- printk(KERN_NOTICE "Total memory is %d%c\n",
- (length<1024)?length:
- (length<1048576)?length/1024:length/1024/1024,
- (length<1024)?'B':(length<1048576)?'K':'M');
- priv->nextpmc551 = pmc551list;
- pmc551list = mtd;
- found++;
- }
-
- if( !pmc551list ) {
- printk(KERN_NOTICE "pmc551: not detected,\n");
- return -ENODEV;
- } else {
- printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found);
- return 0;
- }
-}
-
-/*
- * PMC551 Card Cleanup
- */
-static void __exit cleanup_pmc551(void)
-{
- int found=0;
- struct mtd_info *mtd;
- struct mypriv *priv;
-
- while((mtd=pmc551list)) {
- priv = (struct mypriv *)mtd->priv;
- pmc551list = priv->nextpmc551;
-
- if(priv->start)
- iounmap(((struct mypriv *)mtd->priv)->start);
-
- kfree (mtd->priv);
- del_mtd_device(mtd);
- kfree(mtd);
- found++;
- }
-
- printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found);
-}
-
-#if LINUX_VERSION_CODE >= 0x20211
-module_init(init_pmc551);
-module_exit(cleanup_pmc551);
-#endif
+++ /dev/null
-/*
- * pnc2000.c - mapper for Photron PNC-2000 board.
- *
- * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
- *
- * This code is GPL
- *
- * $Id: pnc2000.c,v 1.4 2000/11/27 08:50:22 dwmw2 Exp $
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-
-#define WINDOW_ADDR 0xbf000000
-#define WINDOW_SIZE 0x00400000
-
-/*
- * MAP DRIVER STUFF
- */
-
-__u8 pnc_read8(struct map_info *map, unsigned long ofs)
-{
- return *(__u8 *)(WINDOW_ADDR + ofs);
-}
-
-__u16 pnc_read16(struct map_info *map, unsigned long ofs)
-{
- return *(__u16 *)(WINDOW_ADDR + ofs);
-}
-
-__u32 pnc_read32(struct map_info *map, unsigned long ofs)
-{
- return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
-}
-
-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
- memcpy(to, (void *)(WINDOW_ADDR + from), len);
-}
-
-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- *(__u8 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- *(__u16 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- *(__u32 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
- memcpy((void *)(WINDOW_ADDR + to), from, len);
-}
-
-struct map_info pnc_map = {
- name: "PNC-2000",
- size: WINDOW_SIZE,
- buswidth: 4,
- read8: pnc_read8,
- read16: pnc_read16,
- read32: pnc_read32,
- copy_from: pnc_copy_from,
- write8: pnc_write8,
- write16: pnc_write16,
- write32: pnc_write32,
- copy_to: pnc_copy_to
-};
-
-
-/*
- * MTD 'PARTITIONING' STUFF
- */
-static struct mtd_partition pnc_partitions[3] = {
- {
- name: "PNC-2000 boot firmware",
- size: 0x20000,
- offset: 0
- },
- {
- name: "PNC-2000 kernel",
- size: 0x1a0000,
- offset: 0x20000
- },
- {
- name: "PNC-2000 filesystem",
- size: 0x240000,
- offset: 0x1c0000
- }
-};
-
-/*
- * This is the master MTD device for which all the others are just
- * auto-relocating aliases.
- */
-static struct mtd_info *mymtd;
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_pnc init_module
-#define cleanup_pnc cleanup_module
-#endif
-
-int __init init_pnc(void)
-{
- printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
-
- mymtd = do_cfi_probe(&pnc_map);
- if (mymtd) {
- mymtd->module = THIS_MODULE;
- return add_mtd_partitions(mymtd, pnc_partitions, 3);
- }
-
- return -ENXIO;
-}
-
-static void __exit cleanup_pnc(void)
-{
- if (mymtd) {
- del_mtd_partitions(mymtd);
- map_destroy(mymtd);
- }
-}
-
-module_init(init_pnc);
-module_exit(cleanup_pnc);
--- /dev/null
+/*
+ * $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 <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+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 ( ; i<nrparts; i++) {
+ parts[i].size = fl->img->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);
+++ /dev/null
-/*
- * $Id: rpxlite.c,v 1.8 2000/12/09 22:00:31 dwmw2 Exp $
- *
- * Handle mapping of the flash on the RPX Lite and CLLF boards
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-
-
-#define WINDOW_ADDR 0xfe000000
-#define WINDOW_SIZE 0x800000
-
-static struct mtd_info *mymtd;
-
-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
-{
- return readb(map->map_priv_1 * ofs);
-}
-
-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
-{
- return readw(map->map_priv_1 + ofs);
-}
-
-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
-{
- return readl(map->map_priv_1 + ofs);
-}
-
-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- writeb(d, map->map_priv_1 + adr);
-}
-
-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- writew(d, map->map_priv_1 + adr);
-}
-
-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- writel(d, map->map_priv_1 + adr);
-}
-
-void rpxlite_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);
-}
-
-struct map_info rpxlite_map = {
- name: "RPX",
- size: WINDOW_SIZE,
- buswidth: 4,
- read8: rpxlite_read8,
- read16: rpxlite_read16,
- read32: rpxlite_read32,
- copy_from: rpxlite_copy_from,
- write8: rpxlite_write8,
- write16: rpxlite_write16,
- write32: rpxlite_write32,
- copy_to: rpxlite_copy_to
-};
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_rpxlite init_module
-#define cleanup_rpxlite cleanup_module
-#endif
-
-int __init init_rpxlite(void)
-{
- printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
-
- if (!rpxlite_map.map_priv_1) {
- printk("Failed to ioremap\n");
- return -EIO;
- }
- mymtd = do_cfi_probe(&rpxlite_map);
- if (mymtd) {
-#ifdef MODULE
- mymtd->module = &__this_module;
-#endif
- add_mtd_device(mymtd);
- return 0;
- }
-
- iounmap((void *)rpxlite_map.map_priv_1);
- return -ENXIO;
-}
-
-static void __exit cleanup_rpxlite(void)
-{
- if (mymtd) {
- del_mtd_device(mymtd);
- map_destroy(mymtd);
- }
- if (rpxlite_map.map_priv_1) {
- iounmap((void *)rpxlite_map.map_priv_1);
- rpxlite_map.map_priv_1 = 0;
- }
-}
-
-module_init(init_rpxlite);
-module_exit(cleanup_rpxlite);
+++ /dev/null
-/*======================================================================
-
- $Id: slram.c,v 1.10 2000/07/03 10:01:38 dwmw2 Exp $
-
-======================================================================*/
-
-
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/major.h>
-#include <linux/fs.h>
-#include <linux/ioctl.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <stdarg.h>
-
-#include <linux/mtd/mtd.h>
-
-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
+++ /dev/null
-// $Id: vmax301.c,v 1.15 2000/11/27 08:50:22 dwmw2 Exp $
-/* ######################################################################
-
- Tempustech VMAX SBC301 MTD Driver.
-
- The VMAx 301 is a SBC based on . It
- comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
- more flash. Each unit has it's own 8k mapping into a settable region
- (0xD8000). There are two 8k mappings for each MTD, the first is always set
- to the lower 8k of the device the second is paged. Writing a 16 bit page
- value to anywhere in the first 8k will cause the second 8k to page around.
-
- To boot the device a bios extension must be installed into the first 8k
- of flash that is smart enough to copy itself down, page in the rest of
- itself and begin executing.
-
- ##################################################################### */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <asm/spinlock.h>
-#include <asm/io.h>
-
-#include <linux/mtd/map.h>
-
-
-#define WINDOW_START 0xd8000
-#define WINDOW_LENGTH 0x2000
-#define WINDOW_SHIFT 25
-#define WINDOW_MASK 0x1FFF
-
-/* Actually we could use two spinlocks, but we'd have to have
- more private space in the struct map_info. We lose a little
- performance like this, but we'd probably lose more by having
- the extra indirection from having one of the map->map_priv
- fields pointing to yet another private struct.
-*/
-static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED;
-
-static void __vmax301_page(struct map_info *map, unsigned long page)
-{
- writew(page, map->map_priv_2 - WINDOW_LENGTH);
- map->map_priv_1 = page;
-}
-
-static inline void vmax301_page(struct map_info *map,
- unsigned long ofs)
-{
- unsigned long page = (ofs >> WINDOW_SHIFT);
- if (map->map_priv_1 != page)
- __vmax301_page(map, page);
-}
-
-static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
-{
- __u8 ret;
- spin_lock(&vmax301_spin);
- vmax301_page(map, ofs);
- ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
- return ret;
-}
-
-static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&vmax301_spin);
- vmax301_page(map, ofs);
- ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
- return ret;
-}
-
-static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&vmax301_spin);
- vmax301_page(map, ofs);
- ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
- return ret;
-}
-
-static void vmax301_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(&vmax301_spin);
- vmax301_page(map, from);
- memcpy_fromio(to, map->map_priv_2 + from, thislen);
- spin_unlock(&vmax301_spin);
- to += thislen;
- from += thislen;
- len -= thislen;
- }
-}
-
-static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- spin_lock(&vmax301_spin);
- vmax301_page(map, adr);
- writeb(d, map->map_priv_2 + (adr & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
-}
-
-static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- spin_lock(&vmax301_spin);
- vmax301_page(map, adr);
- writew(d, map->map_priv_2 + (adr & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
-}
-
-static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
- spin_lock(&vmax301_spin);
- vmax301_page(map, adr);
- writel(d, map->map_priv_2 + (adr & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
-}
-
-static void vmax301_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(&vmax301_spin);
- vmax301_page(map, to);
- memcpy_toio(map->map_priv_2 + to, from, thislen);
- spin_unlock(&vmax301_spin);
- to += thislen;
- from += thislen;
- len -= thislen;
- }
-}
-
-static struct map_info vmax_map[2] = {
- {
- name: "VMAX301 Internal Flash",
- size: 3*2*1024*1024,
- buswidth: 1,
- read8: vmax301_read8,
- read16: vmax301_read16,
- read32: vmax301_read32,
- copy_from: vmax301_copy_from,
- write8: vmax301_write8,
- write16: vmax301_write16,
- write32: vmax301_write32,
- copy_to: vmax301_copy_to,
- map_priv_1: WINDOW_START + WINDOW_LENGTH,
- map_priv_2: 0xFFFFFFFF
- },
- {
- name: "VMAX301 Socket",
- size: 0,
- buswidth: 1,
- read8: vmax301_read8,
- read16: vmax301_read16,
- read32: vmax301_read32,
- copy_from: vmax301_copy_from,
- write8: vmax301_write8,
- write16: vmax301_write16,
- write32: vmax301_write32,
- copy_to: vmax301_copy_to,
- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
- map_priv_2: 0xFFFFFFFF
- }
-};
-
-static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
-
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define init_vmax301 init_module
-#define cleanup_vmax301 cleanup_module
-#endif
-
-static void __exit cleanup_vmax301(void)
-{
- int i;
-
- for (i=0; i<2; i++) {
- if (vmax_mtd[i]) {
- del_mtd_device(vmax_mtd[i]);
- map_destroy(vmax_mtd[i]);
- }
- }
- iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
-}
-
-int __init init_vmax301(void)
-{
- int i;
- unsigned long iomapadr;
- // Print out our little header..
- printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
- WINDOW_START+4*WINDOW_LENGTH);
-
- iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
- if (!iomapadr) {
- printk("Failed to ioremap memory region\n");
- return -EIO;
- }
- /* Put the address in the map's private data area.
- We store the actual MTD IO address rather than the
- address of the first half, because it's used more
- often.
- */
- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
- 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]);
- if (!vmax_mtd[i])
- vmax_mtd[i] = do_jedec_probe(&vmax_map[i]);
- if (!vmax_mtd[i])
- vmax_mtd[i] = do_ram_probe(&vmax_map[i]);
- if (!vmax_mtd[i])
- vmax_mtd[i] = do_rom_probe(&vmax_map[i]);
- if (vmax_mtd[i]) {
- vmax_mtd[i]->module = THIS_MODULE;
- add_mtd_device(vmax_mtd[i]);
- }
- }
-
- if (!vmax_mtd[1] && !vmax_mtd[2]) {
- iounmap(iomapadr);
- return -ENXIO;
- }
-
- return 0;
-}
-
-module_init(init_vmax301);
-module_exit(cleanup_vmax301);
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);
}
\f
#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;
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;
sp->rx_mode = -1;
sp->flow_ctrl = sp->partner = 0;
set_rx_mode(dev);
+ return 0;
}
#endif /* CONFIG_EEPRO100_PM */
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 */
}
break;
- default: /* to be defined */
+ default:
+ /* to be defined */
+ break;
}
dev_kfree_skb(skb);
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;
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;
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
#
}
/**
- * 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;
}
/**
}
}
+/**
+ * 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)
{
* 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;
}
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;
}
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
__setup("pci=", pci_setup);
-
EXPORT_SYMBOL(pci_read_config_byte);
EXPORT_SYMBOL(pci_read_config_word);
EXPORT_SYMBOL(pci_read_config_dword);
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);
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);
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;
}
(void *)0xc8000
};
#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
-#endif USE_BIOS
+#endif /* USE_BIOS */
/* possible i/o port addresses */
static unsigned short ports[] =
{ "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 */
/* ============================================================ */
return tmp;
}
-#endif USE_DMA
+#endif /* USE_DMA */
#if USE_PIO
static __inline__ int NCR53c406a_pio_read(unsigned char *request,
}
return 0;
}
-#endif USE_PIO
+#endif /* USE_PIO */
int __init
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 */
}
}
}
-#endif PORT_BASE
+#endif /* PORT_BASE */
if(!port_base){ /* no ports found */
printk("NCR53c406a: no available ports found\n");
#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"));
}
DEB(printk("Allocated DMA channel %d\n", dma_chan));
-#endif USE_DMA
+#endif /* USE_DMA */
tpnt->present = 1;
tpnt->proc_name = "NCR53c406a";
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);
current_SC->scsi_done(current_SC);
return;
}
-#endif USE_PIO
+#endif /* USE_PIO */
if(status & 0x20) { /* Parity error */
printk("NCR53c406a: Warning: parity error!\n");
#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 */
}
}
REG0;
-#endif USE_PIO
+#endif /* USE_PIO */
}
break;
#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 */
}
}
REG0;
-#endif USE_PIO
+#endif /* USE_PIO */
}
break;
return irq;
}
-#endif IRQ_LEV
+#endif /* IRQ_LEV */
static void chip_init()
{
} /* while(pdev=....) */
} /* for PCI_DEVICES */
} /* PCI BIOS present */
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
#if defined(__i386__) || defined(__alpha__)
/*
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;
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)
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;
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');
}
/* The rest of these are not yet implemented. */
case MODULE_SCSI_CONST:
case MODULE_SCSI_IOCTL:
- break;
default:
+ break;
}
return;
}
}
/*
- * 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;
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;
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;
+ }
}
/*
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);
OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack));
return;
out_stuck:
+ return;
}
* 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 <linux/kernel.h>
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
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;
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;
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);
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)
/* 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;
}
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;
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
/* 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;
}
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)"))
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)
#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
#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_ */
break;
default:
+ break;
}
}
break;
default:
+ break;
}
restore_flags(flags);
}
break;
default:
+ break;
}
status = gus_look8(0x49); /*
* Get Sampling IRQ Status
break;
default:
+ break;
}
return 0;
}
break;
default:
+ break;
}
} else {
mi->m_prev_status = midic;
* for instance we get SNDCTL_TMR_CONTINUE here.
* XXX Is there sound_generic_ioctl() around?
*/
+ break;
}
return -ENOTTY;
}
return -1;
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
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;
}
/*
* bluetooth.c Version 0.10
*
- * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2000, 2001 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
*
* USB Bluetooth driver, based on the Bluetooth Spec version 1.0B
* - 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.
*
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__);
}
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
}
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,
return -1;
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
dbg("dabusb_init: driver registered");
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
{
if (usb_register (&camera_driver) < 0)
return -1;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
warn("couldn't register video device");
return -EINVAL;
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
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;
}
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);
}
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;
MTS_DEBUG("driver registered.\n");
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
}
#ifdef VERBOSE
dbg ("no read resubmitted");
-#endif VERBOSE
+#endif /* VERBOSE */
}
/*-------------------------------------------------------------------------*/
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;
}
if (usb_register(&ov511_driver) < 0)
return -1;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
int __init pegasus_init(void)
{
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return usb_register( &pegasus_driver );
}
dbg("plusb_init: driver registered");
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
{
if (usb_register(&usblp_driver))
return -1;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
if (usb_register(&rio_driver) < 0)
return -1;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
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;
}
{
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;
}
}
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
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;
}
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;
}
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;
}
{
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;
}
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;
}
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;
}
return -1;
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
}
}
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
{
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;
}
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";
/* 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;
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 */
}
#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
if (retval)
goto init_failed;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
#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;
}
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)
{
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)
}
#endif
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return retval;
}
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
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;
}
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;
}
if (usb_register(&uss720_driver) < 0)
return -1;
- info(DRIVER_VERSION " " DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
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;
}
-/* $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)
#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
{ 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 }
};
/* 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);
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);
/* 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
Simplified interface to <devfs_find_handle>.
Work sponsored by SGI.
v0.102
+ 20010519 Richard Gooch <rgooch@atnf.csiro.au>
+ Ensure <devfs_generate_path> terminates string for root entry.
+ Exported <devfs_get_name> to modules.
+ 20010520 Richard Gooch <rgooch@atnf.csiro.au>
+ Make <devfs_mk_symlink> send events to devfsd.
+ Cleaned up option processing in <devfs_setup>.
+ 20010521 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bugs in handling symlinks: could leak or cause Oops.
+ 20010522 Richard Gooch <rgooch@atnf.csiro.au>
+ Cleaned up directory handling by separating fops.
+ v0.103
+ 20010601 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed handling of inverted options in <devfs_setup>.
+ v0.104
+ 20010604 Richard Gooch <rgooch@atnf.csiro.au>
+ Adjusted <try_modload> to account for <devfs_generate_path> fix.
+ v0.105
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/bitops.h>
#include <asm/atomic.h>
-#define DEVFS_VERSION "0.102 (20000622)"
+#define DEVFS_VERSION "0.105 (20010604)"
#define DEVFS_NAME "devfs"
#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); \
#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 */
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) )
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;
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);
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;
}
}
/* 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;
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 */
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;
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;
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);
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,
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)
}
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;
/* 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;
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;
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,
};
/* 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) ?
}
#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 */
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 <devfs_mk_symlink>: %d\n",
+ printk ("%s: symlink(): errcode from <devfs_do_symlink>: %d\n",
DEVFS_NAME, err);
#endif
if (err < 0) return err;
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;
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;
}
#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;
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,
};
{
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);
/* 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
20000622 Richard Gooch <rgooch@atnf.csiro.au>
Took account of interface change to <devfs_mk_symlink>.
Took account of interface change to <devfs_mk_dir>.
+ 20010519 Richard Gooch <rgooch@atnf.csiro.au>
+ Documentation cleanup.
*/
#include <linux/module.h>
#include <linux/init.h>
/**
* 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.
* @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,
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;
{
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)
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)) {
}
/* 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)
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 */
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 */
*/
#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/proc_fs.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
#include <linux/quotaops.h>
#include <linux/pagemap.h>
-#include <linux/dcache.h>
#include <linux/dnotify.h>
-
-#include <asm/uaccess.h>
-#include <asm/unaligned.h>
-#include <asm/semaphore.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
+#include <linux/smp_lock.h>
#include <asm/namei.h>
+#include <asm/uaccess.h>
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
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;
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)
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.
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;
schedule();
}
}
- kunmap(page);
dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
return status;
}
{
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);
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;
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 (;;) {
if (res != -EAGAIN)
break;
/* Align to beginning of next page */
- desc->page_offset = 0;
desc->page_index ++;
if (loop_count++ > 200) {
loop_count = 0;
{
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);
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;
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;
}
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);
break;
}
}
- if (desc->page)
- page_cache_release(desc->page);
if (desc->error < 0)
return desc->error;
if (res < 0)
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;
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);
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;
}
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)
) {
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
* 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);
}
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)
* 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
*/
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)) {
/* 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;
|| !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))
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.
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;
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
__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;
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);
/*
++*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);
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) {
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;
}
{
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);
}
}
#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);
}
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;
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
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 {
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 */
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
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 ;
}
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;
}
}
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;
}
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 */
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;
}
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
}
#ifdef CONFIG_SMP
+#include <linux/sched.h> /* 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)
outb(mode, MCA_DMA_REG_EXE);
}
-#endif MCA_DMA_H
+#endif /* MCA_DMA_H */
#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 <linux/linux_logo.h>
-#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
/*
- * 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
* 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;
/*
- * 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
} \
} 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 */
/* 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
-/* $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.
*
#include <asm/hardirq.h>
#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<<nr))
+#define raise_softirq(nr) \
+do { unsigned long flags; \
+ local_irq_save(flags); \
+ __cpu_raise_softirq(smp_processor_id(), nr); \
+ local_irq_restore(flags); \
+} while (0)
#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
#endif /* __SPARC_SOFTIRQ_H */
/* 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
-/* $Id: linux_logo.h,v 1.6 1998/07/30 16:30:48 jj Exp $
+/* $Id: linux_logo.h,v 1.7 2001/06/08 23:01:58 davem Exp $
* include/asm-sparc64/linux_logo.h: This is a linux logo
* to be displayed on boot.
*
#include <asm/system.h> /* 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<<nr))
+#define raise_softirq(nr) \
+do { unsigned long flags; \
+ local_irq_save(flags); \
+ __cpu_raise_softirq(smp_processor_id(), nr); \
+ local_irq_restore(flags); \
+} while (0)
#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
#endif /* !(__SPARC64_SOFTIRQ_H) */
-/* $Id: string.h,v 1.18 2000/10/30 21:01:41 davem Exp $
+/* $Id: string.h,v 1.19 2001/06/05 20:56:33 davem Exp $
* string.h: External definitions for optimized assembly string
* routines for the Linux Kernel.
*
})
#define __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *,const void *,__kernel_size_t);
/* Now the str*() stuff... */
#define __HAVE_ARCH_STRLEN
/* for downcalls and attributes and lookups */
void coda_flag_inode_children(struct inode *inode, int flag);
-#endif _CFSNC_HEADER_
+#endif /* _CFSNC_HEADER_ */
{
return -ENOSYS;
}
-static inline void *devfs_get_info (devfs_handle_t de, unsigned long size)
+static inline void *devfs_get_info (devfs_handle_t de)
{
return NULL;
}
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
+
+ /* Following are for knfsd to interact with "interesting" filesystems
+ * Currently just reiserfs, but possibly FAT and others later
+ *
+ * fh_to_dentry is given a filehandle fragement with length, and a type flag
+ * and must return a dentry for the referenced object or, if "parent" is
+ * set, a dentry for the parent of the object.
+ * If a dentry cannot be found, a "root" dentry should be created and
+ * flaged as DCACHE_NFSD_DISCONNECTED. nfsd_iget is an example implementation.
+ *
+ * dentry_to_fh is given a dentry and must generate the filesys specific
+ * part of the file handle. Available length is passed in *lenp and used
+ * length should be returned therein.
+ * If need_parent is set, then dentry_to_fh should encode sufficient information
+ * to find the (current) parent.
+ * dentry_to_fh should return a 1byte "type" which will be passed back in
+ * the fhtype arguement to fh_to_dentry. Type of 0 is reserved.
+ * If filesystem was exportable before the introduction of fh_to_dentry,
+ * types 1 and 2 should be used is that same way as the generic code.
+ * Type 255 means error.
+ *
+ * Lengths are in units of 4bytes, not bytes.
+ */
+ struct dentry * (*fh_to_dentry)(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent);
+ int (*dentry_to_fh)(struct dentry *, __u32 *fh, int *lenp, int need_parent);
};
/* Inode state bits.. */
extern struct file_system_type *get_fs_type(const char *name);
extern struct super_block *get_super(kdev_t);
-extern void put_super(kdev_t);
static inline int is_mounted(kdev_t dev)
{
struct super_block *sb = get_super(dev);
unsigned long totalswap; /* Total swap space size */
unsigned long freeswap; /* swap space still available */
unsigned short procs; /* Number of current processes */
+ unsigned short pad; /* explicit padding for m68k */
unsigned long totalhigh; /* Total high memory size */
unsigned long freehigh; /* Available high memory size */
unsigned int mem_unit; /* Memory unit size in bytes */
/* Common Flash Interface structures
* See http://support.intel.com/design/flash/technote/index.htm
- * $Id: cfi.h,v 1.6 2000/07/03 13:29:16 dwmw2 Exp $
+ * $Id: cfi.h,v 1.21 2001/06/03 01:32:57 nico Exp $
*/
#ifndef __MTD_CFI_H__
#define __MTD_CFI_H__
+#include <linux/config.h>
+#include <linux/delay.h>
#include <linux/types.h>
#include <linux/mtd/flashchip.h>
+#include <linux/mtd/cfi_endian.h>
+
+/*
+ * 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.
__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 */
#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__ */
--- /dev/null
+/*
+ * $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 <asm/byteorder.h>
+
+#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 <J.A.K.Mouw@its.tudelft.nl>
+*/
+/* 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
/* Linux driver for Disk-On-Chip 2000 */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $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__
* 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__)
*
* (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 $
*
*/
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;
/* 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__
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.
{
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);
}
+++ /dev/null
-// -*- 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 <linux/types.h>
-#include <linux/mtd/mtd.h>
-
-// 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__ */
-/* $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__
#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;
};
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)
#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 */
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;
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);
--- /dev/null
+/*
+ * 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);
/* Defines for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $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__
#define MAX_NFTLS 16
#define MAX_SECTORS_PER_UNIT 32
+#define NFTL_PARTN_BITS 4
#endif /* __KERNEL__ */
*
* 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
*
* 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
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 *);
#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.
*/
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];
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 */
};
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);
#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
#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
*/
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);
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) ;
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 \
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 */
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);
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;
}
{
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);
}
return !count;
}
-#define SWAP_MM_SHIFT 2
+#define SWAP_MM_SHIFT 4
#define SWAP_SHIFT 5
#define SWAP_MIN 8
/**
* 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
#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");
/* 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);
/*
* 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;
}
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;
}
writepage(page);
page_cache_release(page);
+ maxlaunder--;
+
/* And re-start the thing.. */
spin_lock(&pagemap_lru_lock);
continue;
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 */
/* 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) {
UnlockPage(page);
}
}
+ /* Remove our marker. */
+ list_del(marker_lru);
spin_unlock(&pagemap_lru_lock);
/*
*/
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;
}
* 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);
}
char value[32-sizeof(void*)]; /* fill 1 cache-line */
};
-#endif
\ No newline at end of file
+#endif