# This is a cumulative patch consisting of: # # mtd-cvs.patch # linux-vtcomparison.patch # linux-mkdep.patch # linux-iw241_we16-6.patch # arm-noshortloads.patch # pxa-pcmcia.patch # pxa-smc91x.patch # pxa-usb.patch # pxa-usbeth.patch # pxa-irda.patch # pxa-ac97.patch # pxa-timerint.patch # fb-buffered.patch # fb-turn180.patch # i2c-ds1337.patch # keyb-input.patch # keyb-module.patch # logo-noscrollregion.patch # net-dhcp-timeout.patch # pm.patch # swap-performance.patch # small-nocramdisk.patch # smc91x-ethtool.patch # ucb1x00.patch # vmalloc.patch # usb-sl811.patch # orinoco013e.patch # bluetooth.patch # ramses.patch # ramses-ac97.patch # ramses-keyb.patch # ramses-mtd.patch # ramses-orinoco-ignorecis.patch # ramses-pcmcia.patch # ramses-serial.patch # ramses-smc91x.patch # ramses-sysctl.patch # ramses-ucb1x00-dejitter.patch # ramses-lcd.patch # ramses-usb.patch # ramses-corevolt.patch # wedge.patch # usb-sonycamera.patch # ramses-ucb1x00-rotate.patch # vt-noblank.patch # # Patch managed by http://www.holgerschurig.de/patcher.html # --- linux-2.4.21/CREDITS~bluetooth +++ linux-2.4.21/CREDITS @@ -1348,7 +1348,11 @@ N: Marcel Holtmann E: marcel@holtmann.org W: http://www.holtmann.org -D: Author of the Linux Bluetooth Subsystem PC Card drivers +D: Maintainer of the Linux Bluetooth Subsystem +D: Author and maintainer of the various Bluetooth HCI drivers +D: Author and maintainer of the CAPI message transport protocol driver +D: Author and maintainer of the Bluetooth HID protocol driver +D: Various other Bluetooth related patches, cleanups and fixes S: Germany N: Rob W. W. Hooft @@ -2624,6 +2628,7 @@ N: Aristeu Sergio Rozanski Filho E: aris@conectiva.com.br D: Support for EtherExpress 10 ISA (i82595) in eepro driver +D: User level driver support for input S: Conectiva S.A. S: R. Tocantins, 89 - Cristo Rei S: 80050-430 - Curitiba - Paraná --- linux-2.4.21/Documentation/Configure.help~bluetooth +++ linux-2.4.21/Documentation/Configure.help @@ -14071,6 +14071,15 @@ accessible under char device 13:64+ - /dev/input/eventX in a generic way. This is the future ... +CONFIG_INPUT_UINPUT + Say Y here if you want to support user level drivers for input + subsystem accessible under char device 10:223 - /dev/input/uinput. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called uinput.o. If you want to compile it as a + module, say M here and read . + USB Scanner support CONFIG_USB_SCANNER Say Y here if you want to connect a USB scanner to your computer's @@ -21670,21 +21679,21 @@ Linux Bluetooth subsystem consist of several layers: BlueZ Core (HCI device and connection manager, scheduler) - HCI Device drivers (interface to the hardware) - L2CAP Module (L2CAP protocol) - SCO Module (SCO links) - RFCOMM Module (RFCOMM protocol) - BNEP Module (BNEP protocol) + HCI Device drivers (Interface to the hardware) + SCO Module (SCO audio links) + L2CAP Module (Logical Link Control and Adaptation Protocol) + RFCOMM Module (RFCOMM Protocol) + BNEP Module (Bluetooth Network Encapsulation Protocol) + CMTP Module (CAPI Message Transport Protocol) + HIDP Module (Human Interface Device Protocol) - Say Y here to enable Linux Bluetooth support and to build BlueZ Core - layer. + Say Y here to compile Bluetooth support into the kernel or say M to + compile it as module (bluez.o). To use Linux Bluetooth subsystem, you will need several user-space utilities like hciconfig and hcid. These utilities and updates to Bluetooth kernel modules are provided in the BlueZ package. - For more information, see . - - If you want to compile BlueZ Core as module (bluez.o) say M here. + For more information, see . L2CAP protocol support CONFIG_BLUEZ_L2CAP @@ -21697,7 +21706,7 @@ SCO links support CONFIG_BLUEZ_SCO - SCO link provides voice transport over Bluetooth. SCO support is + SCO link provides voice transport over Bluetooth. SCO support is required for voice applications like Headset and Audio. Say Y here to compile SCO support into the kernel or say M to @@ -21705,7 +21714,7 @@ RFCOMM protocol support CONFIG_BLUEZ_RFCOMM - RFCOMM provides connection oriented stream transport. RFCOMM + RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth applications. @@ -21719,12 +21728,8 @@ BNEP protocol support CONFIG_BLUEZ_BNEP BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet - emulation layer on top of Bluetooth. BNEP is required for Bluetooth - PAN (Personal Area Network). - - To use BNEP, you will need user-space utilities provided in the - BlueZ-PAN package. - For more information, see . + emulation layer on top of Bluetooth. BNEP is required for + Bluetooth PAN (Personal Area Network). Say Y here to compile BNEP support into the kernel or say M to compile it as module (bnep.o). @@ -21737,6 +21742,24 @@ CONFIG_BLUEZ_BNEP_PROTO_FILTER This option enables the protocol filter support for BNEP. +CMTP protocol support +CONFIG_BLUEZ_CMTP + CMTP (CAPI Message Transport Protocol) is a transport layer + for CAPI messages. CMTP is required for the Bluetooth Common + ISDN Access Profile. + + Say Y here to compile CMTP support into the kernel or say M to + compile it as module (cmtp.o). + +HIDP protocol support +CONFIG_BLUEZ_HIDP + HIDP (Human Interface Device Protocol) is a transport layer + for HID reports. HIDP is required for the Bluetooth Human + Interface Device Profile. + + Say Y here to compile HIDP support into the kernel or say M to + compile it as module (hidp.o). + HCI UART driver CONFIG_BLUEZ_HCIUART Bluetooth HCI UART driver. @@ -21781,7 +21804,7 @@ kernel or say M to compile it as module (hci_usb.o). HCI USB SCO (voice) support -CONFIG_BLUEZ_USB_SCO +CONFIG_BLUEZ_HCIUSB_SCO This option enables the SCO support in the HCI USB driver. You need this to transmit voice data with your Bluetooth USB device. And your device must also support sending SCO data over the HCI layer, because some of @@ -21789,14 +21812,6 @@ Say Y here to compile support for HCI SCO data. -HCI USB zero packet support -CONFIG_BLUEZ_USB_ZERO_PACKET - This option is provided only as a work around for buggy Bluetooth USB - devices. Do NOT enable it unless you know for sure that your device - requires zero packets. - - Most people should say N here. - HCI VHCI Virtual HCI device driver CONFIG_BLUEZ_HCIVHCI Bluetooth Virtual HCI device driver. @@ -21805,6 +21820,16 @@ Say Y here to compile support for virtual HCI devices into the kernel or say M to compile it as module (hci_vhci.o). +HCI BFUSB device driver +CONFIG_BLUEZ_HCIBFUSB + Bluetooth HCI BlueFRITZ! USB driver. + This driver provides support for Bluetooth USB devices with AVM + interface: + AVM BlueFRITZ! USB + + Say Y here to compile support for HCI BFUSB devices into the + kernel or say M to compile it as module (bfusb.o). + HCI DTL1 (PC Card) device driver CONFIG_BLUEZ_HCIDTL1 Bluetooth HCI DTL1 (PC Card) driver. @@ -21824,9 +21849,6 @@ 3Com Bluetooth Card (3CRWB6096) HP Bluetooth Card - The HCI BT3C driver uses external firmware loader program provided in - the BlueFW package. For more information, see . - Say Y here to compile support for HCI BT3C devices into the kernel or say M to compile it as module (bt3c_cs.o). @@ -26815,6 +26837,12 @@ If unsure, say N. +Hotplug firmware loading support (EXPERIMENTAL) +CONFIG_FW_LOADER + This option is provided for the case where no in-kernel-tree modules require + hotplug firmware loading support, but a module built outside the kernel tree + does. + NatSemi SCx200 support CONFIG_SCx200 This provides basic support for the National Semiconductor SCx200 --- /dev/null +++ linux-2.4.21/Documentation/DocBook/librs.tmpl @@ -0,0 +1,287 @@ + + + + + Reed-Solomon Library Programming Interface + + + + Thomas + Gleixner + +
+ tglx@linutronix.de +
+
+
+
+ + + 2004 + Thomas Gleixner + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + The generic Reed-Solomon Library provides encoding, decoding + and error correction functions. + + + Reed-Solomon codes are used in communication and storage + applications to ensure data integrity. + + + This documentation is provided for developers who want to utilize + the functions provided by the library. + + + + + Known Bugs And Assumptions + + None. + + + + + Usage + + This chapter provides examples how to use the library. + + + Initializing + + The init function init_rs returns a pointer to a + rs decoder structure, which holds the necessary + information for encoding, decoding and error correction + with the given polynomial. It either uses an existing + matching decoder or creates a new one. On creation all + the lookup tables for fast en/decoding are created. + The function may take a while, so make sure not to + call it in critical code paths. + + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree (number of roots) = 6 + */ +rs_decoder = init_rs (10, 0x409, 0, 1, 6); + + + + Encoding + + The encoder calculates the Reed-Solomon code over + the given data length and stores the result in + the parity buffer. Note that the parity buffer must + be initialized before calling the encoder. + + + The expanded data can be inverted on the fly by + providing a non zero inversion mask. The expanded data is + XOR'ed with the mask. This is used e.g. for FLASH + ECC, where the all 0xFF is inverted to an all 0x00. + The Reed-Solomon code for all 0x00 is all 0x00. The + code is inverted before storing to FLASH so it is 0xFF + too. This prevent's that reading from an erased FLASH + results in ECC errors. + + + The databytes are expanded to the given symbol size + on the fly. There is no support for encoding continuous + bitstreams with a symbol size != 8 at the moment. If + it is necessary it should be not a big deal to implement + such functionality. + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6]; +/* Initialize the parity buffer */ +memset(par, 0, sizeof(par)); +/* Encode 512 byte in data8. Store parity in buffer par */ +encode_rs8 (rs_decoder, data8, 512, par, 0); + + + + Decoding + + The decoder calculates the syndrome over + the given data length and the received parity symbols + and corrects errors in the data. + + + If a syndrome is available from a hardware decoder + then the syndrome calculation is skipped. + + + The correction of the data buffer can be suppressed + by providing a correction pattern buffer and an error + location buffer to the decoder. The decoder stores the + calculated error location and the correction bitmask + in the given buffers. This is useful for hardware + decoders which use a weird bit ordering scheme. + + + The databytes are expanded to the given symbol size + on the fly. There is no support for decoding continuous + bitstreams with a symbolsize != 8 at the moment. If + it is necessary it should be not a big deal to implement + such functionality. + + + + + Decoding with syndrome calculation, direct data correction + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6]; +uint8_t data[512]; +int numerr; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, data8, par, 512, NULL, 0, NULL, 0, NULL); + + + + + + Decoding with syndrome given by hardware decoder, direct data correction + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6], syn[6]; +uint8_t data[512]; +int numerr; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Get syndrome from hardware decoder */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, data8, par, 512, syn, 0, NULL, 0, NULL); + + + + + + Decoding with syndrome given by hardware decoder, no direct data correction. + + + Note: It's not necessary to give data and received parity to the decoder. + + +/* Parity buffer. Size = number of roots */ +uint16_t par[6], syn[6], corr[8]; +uint8_t data[512]; +int numerr, errpos[8]; +/* Receive data */ +..... +/* Receive parity */ +..... +/* Get syndrome from hardware decoder */ +..... +/* Decode 512 byte in data8.*/ +numerr = decode_rs8 (rs_decoder, NULL, NULL, 512, syn, 0, errpos, 0, corr); +for (i = 0; i < numerr; i++) { + do_error_correction_in_your_buffer(errpos[i], corr[i]); +} + + + + + Cleanup + + The function free_rs frees the allocated resources, + if the caller is the last user of the decoder. + + +/* Release resources */ +free_rs(rs_decoder); + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the Reed-Solomon Library and are relevant for a developer. + +!Iinclude/linux/rslib.h + + + + Public Functions Provided + + This chapter contains the autogenerated documentation of the Reed-Solomon functions + which are exported. + +!Elib/reed_solomon/reed_solomon.c + + + + Credits + + The library code for encoding and decoding was written by Phil Karn. + + + Copyright 2002, Phil Karn, KA9Q + May be used under the terms of the GNU General Public License (GPL) + + + The wrapper functions and interfaces are written by Thomas Gleixner + + + Many users have provided bugfixes, improvements and helping hands for testing. + Thanks a lot. + + + The following people have contributed to this document: + + + Thomas Gleixnertglx@linutronix.de + + +
--- /dev/null +++ linux-2.4.21/Documentation/DocBook/mtdnand.tmpl @@ -0,0 +1,1318 @@ + + + + + MTD NAND Driver Programming Interface + + + + Thomas + Gleixner + +
+ tglx@linutronix.de +
+
+
+
+ + + 2004 + Thomas Gleixner + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + The generic NAND driver supports almost all NAND and AG-AND based + chips and connects them to the Memory Technology Devices (MTD) + subsystem of the Linux Kernel. + + + This documentation is provided for developers who want to implement + board drivers or filesystem drivers suitable for NAND devices. + + + + + Known Bugs And Assumptions + + None. + + + + + Documentation hints + + The function and structure docs are autogenerated. Each function and + struct member has a short description which is marked with an [XXX] identifier. + The following chapters explain the meaning of those identifiers. + + + Function identifiers [XXX] + + The functions are marked with [XXX] identifiers in the short + comment. The identifiers explain the usage and scope of the + functions. Following identifiers are used: + + + + [MTD Interface] + These functions provide the interface to the MTD kernel API. + They are not replacable and provide functionality + which is complete hardware independent. + + + [NAND Interface] + These functions are exported and provide the interface to the NAND kernel API. + + + [GENERIC] + Generic functions are not replacable and provide functionality + which is complete hardware independent. + + + [DEFAULT] + Default functions provide hardware related functionality which is suitable + for most of the implementations. These functions can be replaced by the + board driver if neccecary. Those functions are called via pointers in the + NAND chip description structure. The board driver can set the functions which + should be replaced by board dependend functions before calling nand_scan(). + If the function pointer is NULL on entry to nand_scan() then the pointer + is set to the default function which is suitable for the detected chip type. + + + + + Struct member identifiers [XXX] + + The struct members are marked with [XXX] identifiers in the + comment. The identifiers explain the usage and scope of the + members. Following identifiers are used: + + + + [INTERN] + These members are for NAND driver internal use only and must not be + modified. Most of these values are calculated from the chip geometry + information which is evaluated during nand_scan(). + + + [REPLACEABLE] + Replaceable members hold hardware related functions which can be + provided by the board driver. The board driver can set the functions which + should be replaced by board dependend functions before calling nand_scan(). + If the function pointer is NULL on entry to nand_scan() then the pointer + is set to the default function which is suitable for the detected chip type. + + + [BOARDSPECIFIC] + Board specific members hold hardware related information which must + be provided by the board driver. The board driver must set the function + pointers and datafields before calling nand_scan(). + + + [OPTIONAL] + Optional members can hold information relevant for the board driver. The + generic NAND driver code does not use this information. + + + + + + + Basic board driver + + For most boards it will be sufficient to provide just the + basic functions and fill out some really board dependend + members in the nand chip description structure. + See drivers/mtd/nand/skeleton for reference. + + + Basic defines + + At least you have to provide a mtd structure and + a storage for the ioremap'ed chip address. + You can allocate the mtd structure using kmalloc + or you can allocate it statically. + In case of static allocation you have to allocate + a nand_chip structure too. + + + Kmalloc based example + + +static struct mtd_info *board_mtd; +static unsigned long baseaddr; + + + Static example + + +static struct mtd_info board_mtd; +static struct nand_chip board_chip; +static unsigned long baseaddr; + + + + Partition defines + + If you want to divide your device into parititions, then + enable the configuration switch CONFIG_MTD_PARITIONS and define + a paritioning scheme suitable to your board. + + +#define NUM_PARTITIONS 2 +static struct mtd_partition partition_info[] = { + { .name = "Flash partition 1", + .offset = 0, + .size = 8 * 1024 * 1024 }, + { .name = "Flash partition 2", + .offset = MTDPART_OFS_NEXT, + .size = MTDPART_SIZ_FULL }, +}; + + + + Hardware control function + + The hardware control function provides access to the + control pins of the NAND chip(s). + The access can be done by GPIO pins or by address lines. + If you use address lines, make sure that the timing + requirements are met. + + + GPIO based example + + +static void board_hwcontrol(struct mtd_info *mtd, int cmd) +{ + switch(cmd){ + case NAND_CTL_SETCLE: /* Set CLE pin high */ break; + case NAND_CTL_CLRCLE: /* Set CLE pin low */ break; + case NAND_CTL_SETALE: /* Set ALE pin high */ break; + case NAND_CTL_CLRALE: /* Set ALE pin low */ break; + case NAND_CTL_SETNCE: /* Set nCE pin low */ break; + case NAND_CTL_CLRNCE: /* Set nCE pin high */ break; + } +} + + + Address lines based example. It's assumed that the + nCE pin is driven by a chip select decoder. + + +static void board_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = (struct nand_chip *) mtd->priv; + switch(cmd){ + case NAND_CTL_SETCLE: this->IO_ADDR_W |= CLE_ADRR_BIT; break; + case NAND_CTL_CLRCLE: this->IO_ADDR_W &= ~CLE_ADRR_BIT; break; + case NAND_CTL_SETALE: this->IO_ADDR_W |= ALE_ADRR_BIT; break; + case NAND_CTL_CLRALE: this->IO_ADDR_W &= ~ALE_ADRR_BIT; break; + } +} + + + + Device ready function + + If the hardware interface has the ready busy pin of the NAND chip connected to a + GPIO or other accesible I/O pin, this function is used to read back the state of the + pin. The function has no arguments and should return 0, if the device is busy (R/B pin + is low) and 1, if the device is ready (R/B pin is high). + If the hardware interface does not give access to the ready busy pin, then + the function must not be defined and the function pointer this->dev_ready is set to NULL. + + + + Init function + + The init function allocates memory and sets up all the board + specific parameters and function pointers. When everything + is set up nand_scan() is called. This function tries to + detect and identify then chip. If a chip is found all the + internal data fields are initialized accordingly. + The structure(s) have to be zeroed out first and then filled with the neccecary + information about the device. + + +int __init board_init (void) +{ + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); + if (!board_mtd) { + printk ("Unable to allocate NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + /* Initialize structures */ + memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); + + /* map physical adress */ + baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); + if(!baseaddr){ + printk("Ioremap to access NAND chip failed\n"); + err = -EIO; + goto out_mtd; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (); + /* Link the private data with the MTD structure */ + board_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = baseaddr; + this->IO_ADDR_W = baseaddr; + /* Reference hardware control function */ + this->hwcontrol = board_hwcontrol; + /* Set command delay time, see datasheet for correct value */ + this->chip_delay = CHIP_DEPENDEND_COMMAND_DELAY; + /* Assign the device ready function, if available */ + this->dev_ready = board_dev_ready; + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ + if (nand_scan (board_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + + add_mtd_partitions(board_mtd, partition_info, NUM_PARTITIONS); + goto out; + +out_ior: + iounmap((void *)baseaddr); +out_mtd: + kfree (board_mtd); +out: + return err; +} +module_init(board_init); + + + + Exit function + + The exit function is only neccecary if the driver is + compiled as a module. It releases all resources which + are held by the chip driver and unregisters the partitions + in the MTD layer. + + +#ifdef MODULE +static void __exit board_cleanup (void) +{ + /* Release resources, unregister device */ + nand_release (board_mtd); + + /* unmap physical adress */ + iounmap((void *)baseaddr); + + /* Free the MTD device structure */ + kfree (board_mtd); +} +module_exit(board_cleanup); +#endif + + + + + + Advanced board driver functions + + This chapter describes the advanced functionality of the NAND + driver. For a list of functions which can be overridden by the board + driver see the documentation of the nand_chip structure. + + + Multiple chip control + + The nand driver can control chip arrays. Therefor the + board driver must provide an own select_chip function. This + function must (de)select the requested chip. + The function pointer in the nand_chip structure must + be set before calling nand_scan(). The maxchip parameter + of nand_scan() defines the maximum number of chips to + scan for. Make sure that the select_chip function can + handle the requested number of chips. + + + The nand driver concatenates the chips to one virtual + chip and provides this virtual chip to the MTD layer. + + + Note: The driver can only handle linear chip arrays + of equally sized chips. There is no support for + parallel arrays which extend the buswidth. + + + GPIO based example + + +static void board_select_chip (struct mtd_info *mtd, int chip) +{ + /* Deselect all chips, set all nCE pins high */ + GPIO(BOARD_NAND_NCE) |= 0xff; + if (chip >= 0) + GPIO(BOARD_NAND_NCE) &= ~ (1 << chip); +} + + + Address lines based example. + Its assumed that the nCE pins are connected to an + address decoder. + + +static void board_select_chip (struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = (struct nand_chip *) mtd->priv; + + /* Deselect all chips */ + this->IO_ADDR_R &= ~BOARD_NAND_ADDR_MASK; + this->IO_ADDR_W &= ~BOARD_NAND_ADDR_MASK; + switch (chip) { + case 0: + this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIP0; + this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIP0; + break; + .... + case n: + this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIPn; + this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIPn; + break; + } +} + + + + Hardware ECC support + + Functions and constants + + The nand driver supports three different types of + hardware ECC. + + NAND_ECC_HW3_256 + Hardware ECC generator providing 3 bytes ECC per + 256 byte. + + NAND_ECC_HW3_512 + Hardware ECC generator providing 3 bytes ECC per + 512 byte. + + NAND_ECC_HW6_512 + Hardware ECC generator providing 6 bytes ECC per + 512 byte. + + NAND_ECC_HW8_512 + Hardware ECC generator providing 6 bytes ECC per + 512 byte. + + + If your hardware generator has a different functionality + add it at the appropriate place in nand_base.c + + + The board driver must provide following functions: + + enable_hwecc + This function is called before reading / writing to + the chip. Reset or initialize the hardware generator + in this function. The function is called with an + argument which let you distinguish between read + and write operations. + + calculate_ecc + This function is called after read / write from / to + the chip. Transfer the ECC from the hardware to + the buffer. If the option NAND_HWECC_SYNDROME is set + then the function is only called on write. See below. + + correct_data + In case of an ECC error this function is called for + error detection and correction. Return 1 respectively 2 + in case the error can be corrected. If the error is + not correctable return -1. If your hardware generator + matches the default algorithm of the nand_ecc software + generator then use the correction function provided + by nand_ecc instead of implementing duplicated code. + + + + + + Hardware ECC with syndrome calculation + + Many hardware ECC implementations provide Reed-Solomon + codes and calculate an error syndrome on read. The syndrome + must be converted to a standard Reed-Solomon syndrome + before calling the error correction code in the generic + Reed-Solomon library. + + + The ECC bytes must be placed immidiately after the data + bytes in order to make the syndrome generator work. This + is contrary to the usual layout used by software ECC. The + seperation of data and out of band area is not longer + possible. The nand driver code handles this layout and + the remaining free bytes in the oob area are managed by + the autoplacement code. Provide a matching oob-layout + in this case. See rts_from4.c and diskonchip.c for + implementation reference. In those cases we must also + use bad block tables on FLASH, because the ECC layout is + interferring with the bad block marker positions. + See bad block table support for details. + + + + + Bad block table support + + Most NAND chips mark the bad blocks at a defined + position in the spare area. Those blocks must + not be erased under any circumstances as the bad + block information would be lost. + It is possible to check the bad block mark each + time when the blocks are accessed by reading the + spare area of the first page in the block. This + is time consuming so a bad block table is used. + + + The nand driver supports various types of bad block + tables. + + Per device + The bad block table contains all bad block information + of the device which can consist of multiple chips. + + Per chip + A bad block table is used per chip and contains the + bad block information for this particular chip. + + Fixed offset + The bad block table is located at a fixed offset + in the chip (device). This applies to various + DiskOnChip devices. + + Automatic placed + The bad block table is automatically placed and + detected either at the end or at the beginning + of a chip (device) + + Mirrored tables + The bad block table is mirrored on the chip (device) to + allow updates of the bad block table without data loss. + + + + + nand_scan() calls the function nand_default_bbt(). + nand_default_bbt() selects appropriate default + bad block table desriptors depending on the chip information + which was retrieved by nand_scan(). + + + The standard policy is scanning the device for bad + blocks and build a ram based bad block table which + allows faster access than always checking the + bad block information on the flash chip itself. + + + Flash based tables + + It may be desired or neccecary to keep a bad block table in FLASH. + For AG-AND chips this is mandatory, as they have no factory marked + bad blocks. They have factory marked good blocks. The marker pattern + is erased when the block is erased to be reused. So in case of + powerloss before writing the pattern back to the chip this block + would be lost and added to the bad blocks. Therefor we scan the + chip(s) when we detect them the first time for good blocks and + store this information in a bad block table before erasing any + of the blocks. + + + The blocks in which the tables are stored are procteted against + accidental access by marking them bad in the memory bad block + table. The bad block table managment functions are allowed + to circumvernt this protection. + + + The simplest way to activate the FLASH based bad block table support + is to set the option NAND_USE_FLASH_BBT in the option field of + the nand chip structure before calling nand_scan(). For AG-AND + chips is this done by default. + This activates the default FLASH based bad block table functionality + of the NAND driver. The default bad block table options are + + Store bad block table per chip + Use 2 bits per block + Automatic placement at the end of the chip + Use mirrored tables with version numbers + Reserve 4 blocks at the end of the chip + + + + + User defined tables + + User defined tables are created by filling out a + nand_bbt_descr structure and storing the pointer in the + nand_chip structure member bbt_td before calling nand_scan(). + If a mirror table is neccecary a second structure must be + created and a pointer to this structure must be stored + in bbt_md inside the nand_chip structure. If the bbt_md + member is set to NULL then only the main table is used + and no scan for the mirrored table is performed. + + + The most important field in the nand_bbt_descr structure + is the options field. The options define most of the + table properties. Use the predefined constants from + nand.h to define the options. + + Number of bits per block + The supported number of bits is 1, 2, 4, 8. + Table per chip + Setting the constant NAND_BBT_PERCHIP selects that + a bad block table is managed for each chip in a chip array. + If this option is not set then a per device bad block table + is used. + Table location is absolute + Use the option constant NAND_BBT_ABSPAGE and + define the absolute page number where the bad block + table starts in the field pages. If you have selected bad block + tables per chip and you have a multi chip array then the start page + must be given for each chip in the chip array. Note: there is no scan + for a table ident pattern performed, so the fields + pattern, veroffs, offs, len can be left uninitialized + Table location is automatically detected + The table can either be located in the first or the last good + blocks of the chip (device). Set NAND_BBT_LASTBLOCK to place + the bad block table at the end of the chip (device). The + bad block tables are marked and identified by a pattern which + is stored in the spare area of the first page in the block which + holds the bad block table. Store a pointer to the pattern + in the pattern field. Further the length of the pattern has to be + stored in len and the offset in the spare area must be given + in the offs member of the nand_bbt_descr stucture. For mirrored + bad block tables different patterns are mandatory. + Table creation + Set the option NAND_BBT_CREATE to enable the table creation + if no table can be found during the scan. Usually this is done only + once if a new chip is found. + Table write support + Set the option NAND_BBT_WRITE to enable the table write support. + This allows the update of the bad block table(s) in case a block has + to be marked bad due to wear. The MTD interface function block_markbad + is calling the update function of the bad block table. If the write + support is enabled then the table is updated on FLASH. + + Note: Write support should only be enabled for mirrored tables with + version control. + + Table version control + Set the option NAND_BBT_VERSION to enable the table version control. + It's highly recommended to enable this for mirrored tables with write + support. It makes sure that the risk of loosing the bad block + table information is reduced to the loss of the information about the + one worn out block which should be marked bad. The version is stored in + 4 consecutive bytes in the spare area of the device. The position of + the version number is defined by the member veroffs in the bad block table + descriptor. + Save block contents on write + + In case that the block which holds the bad block table does contain + other useful information, set the option NAND_BBT_SAVECONTENT. When + the bad block table is written then the whole block is read the bad + block table is updated and the block is erased and everything is + written back. If this option is not set only the bad block table + is written and everything else in the block is ignored and erased. + + Number of reserved blocks + + For automatic placement some blocks must be reserved for + bad block table storage. The number of reserved blocks is defined + in the maxblocks member of the babd block table description structure. + Reserving 4 blocks for mirrored tables should be a reasonable number. + This also limits the number of blocks which are scanned for the bad + block table ident pattern. + + + + + + + Spare area (auto)placement + + The nand driver implements different possibilities for + placement of filesystem data in the spare area, + + Placement defined by fs driver + Automatic placement + + The default placement function is automatic placement. The + nand driver has built in default placement schemes for the + various chiptypes. If due to hardware ECC functionality the + default placement does not fit then the board driver can + provide a own placement scheme. + + + File system drivers can provide a own placement scheme which + is used instead of the default placement scheme. + + + Placement schemes are defined by a nand_oobinfo structure + +struct nand_oobinfo { + int useecc; + int eccbytes; + int eccpos[24]; + int oobfree[8][2]; +}; + + + useecc + The useecc member controls the ecc and placement function. The header + file include/mtd/mtd-abi.h contains constants to select ecc and + placement. MTD_NANDECC_OFF switches off the ecc complete. This is + not recommended and available for testing and diagnosis only. + MTD_NANDECC_PLACE selects caller defined placement, MTD_NANDECC_AUTOPLACE + selects automatic placement. + + eccbytes + The eccbytes member defines the number of ecc bytes per page. + + eccpos + The eccpos array holds the byte offsets in the spare area where + the ecc codes are placed. + + oobfree + The oobfree array defines the areas in the spare area which can be + used for automatic placement. The information is given in the format + {offset, size}. offset defines the start of the usable area, size the + length in bytes. More than one area can be defined. The list is terminated + by an {0, 0} entry. + + + + + Placement defined by fs driver + + The calling function provides a pointer to a nand_oobinfo + structure which defines the ecc placement. For writes the + caller must provide a spare area buffer along with the + data buffer. The spare area buffer size is (number of pages) * + (size of spare area). For reads the buffer size is + (number of pages) * ((size of spare area) + (number of ecc + steps per page) * sizeof (int)). The driver stores the + result of the ecc check for each tuple in the spare buffer. + The storage sequence is + + + <spare data page 0><ecc result 0>...<ecc result n> + + + ... + + + <spare data page n><ecc result 0>...<ecc result n> + + + This is a legacy mode used by YAFFS1. + + + If the spare area buffer is NULL then only the ECC placement is + done according to the given scheme in the nand_oobinfo structure. + + + + Automatic placement + + Automatic placement uses the built in defaults to place the + ecc bytes in the spare area. If filesystem data have to be stored / + read into the spare area then the calling function must provide a + buffer. The buffer size per page is determined by the oobfree array in + the nand_oobinfo structure. + + + If the spare area buffer is NULL then only the ECC placement is + done according to the default builtin scheme. + + + + User space placement selection + + All non ecc functions like mtd->read and mtd->write use an internal + structure, which can be set by an ioctl. This structure is preset + to the autoplacement default. + + ioctl (fd, MEMSETOOBSEL, oobsel); + + oobsel is a pointer to a user supplied structure of type + nand_oobconfig. The contents of this structure must match the + criteria of the filesystem, which will be used. See an example in utils/nandwrite.c. + + + + + Spare area autoplacement default schemes + + 256 byte pagesize + + +Offset +Content +Comment + + +0x00 +ECC byte 0 +Error correction code byte 0 + + +0x01 +ECC byte 1 +Error correction code byte 1 + + +0x02 +ECC byte 2 +Error correction code byte 2 + + +0x03 +Autoplace 0 + + + +0x04 +Autoplace 1 + + + +0x05 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x06 +Autoplace 2 + + + +0x07 +Autoplace 3 + + + + + + 512 byte pagesize + + +Offset +Content +Comment + + +0x00 +ECC byte 0 +Error correction code byte 0 of the lower 256 Byte data in +this page + + +0x01 +ECC byte 1 +Error correction code byte 1 of the lower 256 Bytes of data +in this page + + +0x02 +ECC byte 2 +Error correction code byte 2 of the lower 256 Bytes of data +in this page + + +0x03 +ECC byte 3 +Error correction code byte 0 of the upper 256 Bytes of data +in this page + + +0x04 +reserved +reserved + + +0x05 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x06 +ECC byte 4 +Error correction code byte 1 of the upper 256 Bytes of data +in this page + + +0x07 +ECC byte 5 +Error correction code byte 2 of the upper 256 Bytes of data +in this page + + +0x08 - 0x0F +Autoplace 0 - 7 + + + + + + 2048 byte pagesize + + +Offset +Content +Comment + + +0x00 +Bad block marker +If any bit in this byte is zero, then this block is bad. +This applies only to the first page in a block. In the remaining +pages this byte is reserved + + +0x01 +Reserved +Reserved + + +0x02-0x27 +Autoplace 0 - 37 + + + +0x28 +ECC byte 0 +Error correction code byte 0 of the first 256 Byte data in +this page + + +0x29 +ECC byte 1 +Error correction code byte 1 of the first 256 Bytes of data +in this page + + +0x2A +ECC byte 2 +Error correction code byte 2 of the first 256 Bytes data in +this page + + +0x2B +ECC byte 3 +Error correction code byte 0 of the second 256 Bytes of data +in this page + + +0x2C +ECC byte 4 +Error correction code byte 1 of the second 256 Bytes of data +in this page + + +0x2D +ECC byte 5 +Error correction code byte 2 of the second 256 Bytes of data +in this page + + +0x2E +ECC byte 6 +Error correction code byte 0 of the third 256 Bytes of data +in this page + + +0x2F +ECC byte 7 +Error correction code byte 1 of the third 256 Bytes of data +in this page + + +0x30 +ECC byte 8 +Error correction code byte 2 of the third 256 Bytes of data +in this page + + +0x31 +ECC byte 9 +Error correction code byte 0 of the fourth 256 Bytes of data +in this page + + +0x32 +ECC byte 10 +Error correction code byte 1 of the fourth 256 Bytes of data +in this page + + +0x33 +ECC byte 11 +Error correction code byte 2 of the fourth 256 Bytes of data +in this page + + +0x34 +ECC byte 12 +Error correction code byte 0 of the fifth 256 Bytes of data +in this page + + +0x35 +ECC byte 13 +Error correction code byte 1 of the fifth 256 Bytes of data +in this page + + +0x36 +ECC byte 14 +Error correction code byte 2 of the fifth 256 Bytes of data +in this page + + +0x37 +ECC byte 15 +Error correction code byte 0 of the sixt 256 Bytes of data +in this page + + +0x38 +ECC byte 16 +Error correction code byte 1 of the sixt 256 Bytes of data +in this page + + +0x39 +ECC byte 17 +Error correction code byte 2 of the sixt 256 Bytes of data +in this page + + +0x3A +ECC byte 18 +Error correction code byte 0 of the seventh 256 Bytes of +data in this page + + +0x3B +ECC byte 19 +Error correction code byte 1 of the seventh 256 Bytes of +data in this page + + +0x3C +ECC byte 20 +Error correction code byte 2 of the seventh 256 Bytes of +data in this page + + +0x3D +ECC byte 21 +Error correction code byte 0 of the eigth 256 Bytes of data +in this page + + +0x3E +ECC byte 22 +Error correction code byte 1 of the eigth 256 Bytes of data +in this page + + +0x3F +ECC byte 23 +Error correction code byte 2 of the eigth 256 Bytes of data +in this page + + + + + + + + Filesystem support + + The NAND driver provides all neccecary functions for a + filesystem via the MTD interface. + + + Filesystems must be aware of the NAND pecularities and + restrictions. One major restrictions of NAND Flash is, that you cannot + write as often as you want to a page. The consecutive writes to a page, + before erasing it again, are restricted to 1-3 writes, depending on the + manufacturers specifications. This applies similar to the spare area. + + + Therefor NAND aware filesystems must either write in page size chunks + or hold a writebuffer to collect smaller writes until they sum up to + pagesize. Available NAND aware filesystems: JFFS2, YAFFS. + + + The spare area usage to store filesystem data is controlled by + the spare area placement functionality which is described in one + of the earlier chapters. + + + + Tools + + The MTD project provides a couple of helpful tools to handle NAND Flash. + + flasherase, flasheraseall: Erase and format FLASH partitions + nandwrite: write filesystem images to NAND FLASH + nanddump: dump the contents of a NAND FLASH partitions + + + + These tools are aware of the NAND restrictions. Please use those tools + instead of complaining about errors which are caused by non NAND aware + access methods. + + + + + Constants + + This chapter describes the constants which might be relevant for a driver developer. + + + Chip option constants + + Constants for chip id table + + These constants are defined in nand.h. They are ored together to describe + the chip functionality. + +/* Chip can not auto increment pages */ +#define NAND_NO_AUTOINCR 0x00000001 +/* Buswitdh is 16 bit */ +#define NAND_BUSWIDTH_16 0x00000002 +/* Device supports partial programming without padding */ +#define NAND_NO_PADDING 0x00000004 +/* Chip has cache program function */ +#define NAND_CACHEPRG 0x00000008 +/* Chip has copy back function */ +#define NAND_COPYBACK 0x00000010 +/* AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information */ +#define NAND_IS_AND 0x00000020 +/* Chip has a array of 4 pages which can be read without + * additional ready /busy waits */ +#define NAND_4PAGE_ARRAY 0x00000040 + + + + + Constants for runtime options + + These constants are defined in nand.h. They are ored together to describe + the functionality. + +/* Use a flash based bad block table. This option is parsed by the + * default bad block table function (nand_default_bbt). */ +#define NAND_USE_FLASH_BBT 0x00010000 +/* The hw ecc generator provides a syndrome instead a ecc value on read + * This can only work if we have the ecc bytes directly behind the + * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ +#define NAND_HWECC_SYNDROME 0x00020000 + + + + + + + ECC selection constants + + Use these constants to select the ECC algorithm. + +/* No ECC. Usage is not recommended ! */ +#define NAND_ECC_NONE 0 +/* Software ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_SOFT 1 +/* Hardware ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_HW3_256 2 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW3_512 3 +/* Hardware ECC 6 byte ECC per 512 Byte data */ +#define NAND_ECC_HW6_512 4 +/* Hardware ECC 6 byte ECC per 512 Byte data */ +#define NAND_ECC_HW8_512 6 + + + + + + Hardware control related constants + + These constants describe the requested hardware access function when + the boardspecific hardware control function is called + +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 +/* Set write protection by setting WP to high. Not used! */ +#define NAND_CTL_SETWP 7 +/* Clear write protection by setting WP to low. Not used! */ +#define NAND_CTL_CLRWP 8 + + + + + + Bad block table related constants + + These constants describe the options used for bad block + table descriptors. + +/* Options for the bad block table descriptors */ + +/* The number of bits used per block in the bbt on the device */ +#define NAND_BBT_NRBITS_MSK 0x0000000F +#define NAND_BBT_1BIT 0x00000001 +#define NAND_BBT_2BIT 0x00000002 +#define NAND_BBT_4BIT 0x00000004 +#define NAND_BBT_8BIT 0x00000008 +/* The bad block table is in the last good block of the device */ +#define NAND_BBT_LASTBLOCK 0x00000010 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_ABSPAGE 0x00000020 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_SEARCH 0x00000040 +/* bbt is stored per chip on multichip devices */ +#define NAND_BBT_PERCHIP 0x00000080 +/* bbt has a version counter at offset veroffs */ +#define NAND_BBT_VERSION 0x00000100 +/* Create a bbt if none axists */ +#define NAND_BBT_CREATE 0x00000200 +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000400 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00000800 +/* Write bbt if neccecary */ +#define NAND_BBT_WRITE 0x00001000 +/* Read and write back block contents when writing bbt */ +#define NAND_BBT_SAVECONTENT 0x00002000 + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the NAND driver and might be relevant for a driver developer. Each + struct member has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + +!Iinclude/linux/mtd/nand.h + + + + Public Functions Provided + + This chapter contains the autogenerated documentation of the NAND kernel API functions + which are exported. Each function has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + +!Edrivers/mtd/nand/nand_base.c +!Edrivers/mtd/nand/nand_bbt.c +!Edrivers/mtd/nand/nand_ecc.c + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the NAND driver internal functions. + Each function has a short description which is marked with an [XXX] identifier. + See the chapter "Documentation hints" for an explanation. + The functions marked with [DEFAULT] might be relevant for a board driver developer. + +!Idrivers/mtd/nand/nand_base.c +!Idrivers/mtd/nand/nand_bbt.c +!Idrivers/mtd/nand/nand_ecc.c + + + + Credits + + The following people have contributed to the NAND driver: + + Steven J. Hillsjhill@realitydiluted.com + David Woodhousedwmw2@infradead.org + Thomas Gleixnertglx@linutronix.de + + A lot of users have provided bugfixes, improvements and helping hands for testing. + Thanks a lot. + + + The following people have contributed to this document: + + Thomas Gleixnertglx@linutronix.de + + + +
--- linux-2.4.21/Documentation/devices.txt~bluetooth +++ linux-2.4.21/Documentation/devices.txt @@ -419,6 +419,7 @@ 220 = /dev/mptctl Message passing technology (MPT) control 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver 222 = /dev/mvista/hasi Montavista PICMG high availability + 223 = /dev/input/uinput User level driver support for input 240-255 Reserved for local use 11 char Raw keyboard device --- /dev/null +++ linux-2.4.21/Documentation/firmware_class/README @@ -0,0 +1,58 @@ + + request_firmware() hotplug interface: + ------------------------------------ + Copyright (C) 2003 Manuel Estrada Sainz + + Why: + --- + + Today, the most extended way to use firmware in the Linux kernel is linking + it statically in a header file. Which has political and technical issues: + + 1) Some firmware is not legal to redistribute. + 2) The firmware occupies memory permanently, even though it often is just + used once. + 3) Some people, like the Debian crowd, don't consider some firmware free + enough and remove entire drivers (e.g.: keyspan). + + about in-kernel persistence: + --------------------------- + Under some circumstances, as explained below, it would be interesting to keep + firmware images in non-swappable kernel memory or even in the kernel image + (probably within initramfs). + + Note that this functionality has not been implemented. + + - Why OPTIONAL in-kernel persistence may be a good idea sometimes: + + - If the device that needs the firmware is needed to access the + filesystem. When upon some error the device has to be reset and the + firmware reloaded, it won't be possible to get it from userspace. + e.g.: + - A diskless client with a network card that needs firmware. + - The filesystem is stored in a disk behind an scsi device + that needs firmware. + - Replacing buggy DSDT/SSDT ACPI tables on boot. + Note: this would require the persistent objects to be included + within the kernel image, probably within initramfs. + + And the same device can be needed to access the filesystem or not depending + on the setup, so I think that the choice on what firmware to make + persistent should be left to userspace. + + - Why register_firmware()+__init can be useful: + - For boot devices needing firmware. + - To make the transition easier: + The firmware can be declared __init and register_firmware() + called on module_init. Then the firmware is warranted to be + there even if "firmware hotplug userspace" is not there yet or + it doesn't yet provide the needed firmware. + Once the firmware is widely available in userspace, it can be + removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE). + + In either case, if firmware hotplug support is there, it can move the + firmware out of kernel memory into the real filesystem for later + usage. + + Note: If persistence is implemented on top of initramfs, + register_firmware() may not be appropriate. --- /dev/null +++ linux-2.4.21/Documentation/firmware_class/firmware_sample_driver.c @@ -0,0 +1,121 @@ +/* + * firmware_sample_driver.c - + * + * Copyright (c) 2003 Manuel Estrada Sainz + * + * Sample code on how to use request_firmware() from drivers. + * + * Note that register_firmware() is currently useless. + * + */ + +#include +#include +#include +#include + +#include "linux/firmware.h" + +#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE +#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE +char __init inkernel_firmware[] = "let's say that this is firmware\n"; +#endif + +static char ghost_device[] = "ghost0"; + +static void sample_firmware_load(char *firmware, int size) +{ + u8 buf[size+1]; + memcpy(buf, firmware, size); + buf[size] = '\0'; + printk("firmware_sample_driver: firmware: %s\n", buf); +} + +static void sample_probe_default(void) +{ + /* uses the default method to get the firmware */ + const struct firmware *fw_entry; + printk("firmware_sample_driver: a ghost device got inserted :)\n"); + + if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0) + { + printk(KERN_ERR + "firmware_sample_driver: Firmware not available\n"); + return; + } + + sample_firmware_load(fw_entry->data, fw_entry->size); + + release_firmware(fw_entry); + + /* finish setting up the device */ +} +static void sample_probe_specific(void) +{ + /* Uses some specific hotplug support to get the firmware from + * userspace directly into the hardware, or via some sysfs file */ + + /* NOTE: This currently doesn't work */ + + printk("firmware_sample_driver: a ghost device got inserted :)\n"); + + if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0) + { + printk(KERN_ERR + "firmware_sample_driver: Firmware load failed\n"); + return; + } + + /* request_firmware blocks until userspace finished, so at + * this point the firmware should be already in the device */ + + /* finish setting up the device */ +} +static void sample_probe_async_cont(const struct firmware *fw, void *context) +{ + if(!fw){ + printk(KERN_ERR + "firmware_sample_driver: firmware load failed\n"); + return; + } + + printk("firmware_sample_driver: device pointer \"%s\"\n", + (char *)context); + sample_firmware_load(fw->data, fw->size); +} +static void sample_probe_async(void) +{ + /* Let's say that I can't sleep */ + int error; + error = request_firmware_nowait (THIS_MODULE, + "sample_driver_fw", ghost_device, + "my device pointer", + sample_probe_async_cont); + if(error){ + printk(KERN_ERR + "firmware_sample_driver:" + " request_firmware_nowait failed\n"); + } +} + +static int sample_init(void) +{ +#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE + register_firmware("sample_driver_fw", inkernel_firmware, + sizeof(inkernel_firmware)); +#endif + /* since there is no real hardware insertion I just call the + * sample probe functions here */ + sample_probe_specific(); + sample_probe_default(); + sample_probe_async(); + return 0; +} +static void __exit sample_exit(void) +{ +} + +module_init (sample_init); +module_exit (sample_exit); + +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.21/Documentation/firmware_class/hotplug-script @@ -0,0 +1,16 @@ +#!/bin/sh + +# Simple hotplug script sample: +# +# Both $DEVPATH and $FIRMWARE are already provided in the environment. + +HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/ + +echo 1 > /sysfs/$DEVPATH/loading +cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data +echo 0 > /sysfs/$DEVPATH/loading + +# To cancel the load in case of error: +# +# echo -1 > /sysfs/$DEVPATH/loading +# --- linux-2.4.21/MAINTAINERS~bluetooth +++ linux-2.4.21/MAINTAINERS @@ -302,16 +302,88 @@ L: linux-kernel@vger.kernel.org S: Maintained -BLUETOOTH SUBSYSTEM (BlueZ) +BLUETOOTH SUBSYSTEM +P: Marcel Holtmann +M: marcel@holtmann.org P: Maxim Krasnyansky M: maxk@qualcomm.com +L: bluez-devel@lists.sf.net W: http://bluez.sf.net +W: http://www.bluez.org +W: http://www.holtmann.org/linux/bluetooth/ S: Maintained -BLUETOOTH SUBSYSTEM (PC Card Drivers) +BLUETOOTH RFCOMM LAYER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ +P: Maxim Krasnyansky +M: maxk@qualcomm.com +S: Maintained + +BLUETOOTH BNEP LAYER +P: Marcel Holtmann +M: marcel@holtmann.org +P: Maxim Krasnyansky +M: maxk@qualcomm.com +S: Maintained + +BLUETOOTH CMTP LAYER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HIDP LAYER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI UART DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +P: Maxim Krasnyansky +M: maxk@qualcomm.com +S: Maintained + +BLUETOOTH HCI USB DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +P: Maxim Krasnyansky +M: maxk@qualcomm.com +S: Maintained + +BLUETOOTH HCI BCM203X DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI BFUSB DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI DTL1 DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI BLUECARD DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI BT3C DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI BTUART DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +S: Maintained + +BLUETOOTH HCI VHCI DRIVER +P: Maxim Krasnyansky +M: maxk@qualcomm.com S: Maintained BONDING DRIVER --- linux-2.4.21/Makefile~linux-mkdep +++ linux-2.4.21/Makefile @@ -14,10 +14,11 @@ else echo sh; fi ; fi) TOPDIR := $(shell /bin/pwd) +PATH := /usr/local/arm/3.3/bin:$(PATH) HPATH = $(TOPDIR)/include FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net $(HPATH)/math-emu -HOSTCC = gcc +HOSTCC = ccache gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer CROSS_COMPILE = arm-linux- @@ -28,7 +29,7 @@ AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld -CC = $(CROSS_COMPILE)gcc +CC = ccache $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm @@ -80,6 +81,7 @@ # makefile but the arguement can be passed to make if needed. # +export INSTALL_MOD_PATH = /tftpboot/ramses2 MODLIB := $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) export MODLIB @@ -140,7 +142,6 @@ DRIVERS-y += drivers/serial/serial.o \ drivers/char/char.o \ drivers/block/block.o \ - drivers/misc/misc.o \ drivers/net/net.o DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o @@ -197,6 +198,7 @@ DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o DRIVERS-$(CONFIG_ARCH_AT91RM9200) += drivers/at91/at91drv.o +DRIVERS-y += drivers/misc/misc.o DRIVERS := $(DRIVERS-y) @@ -416,7 +418,7 @@ endif .PHONY: _modinst_post _modinst_post: _modinst_post_pcmcia - if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi +# if [ -r System.map ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi # Backwards compatibilty symlinks for people still using old versions # of pcmcia-cs with hard coded pathnames on insmod. Remove @@ -495,7 +497,7 @@ ifdef CONFIG_MODVERSIONS $(MAKE) update-modverfile endif - scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend + $(foreach, dir, $(FINDHPATH), scripts/mkdep -- `find $(dir) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` >> .hdepend) scripts/mkdep -- init/*.c > .depend ifdef CONFIG_MODVERSIONS @@ -574,3 +576,14 @@ . scripts/mkversion > .version ; \ rpm -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ; \ rm $(TOPDIR)/../$(KERNELPATH).tar.gz + + + +# +# Burn Linux Image for ArmBoot using the bdi2000 +# +burn burn_zImage: + ../hwtester/burner.py arch/arm/boot/zImage 0x40000 + +publish: arch/arm/boot/zImage + cp arch/arm/boot/zImage /tftpboot/bdi/zImage.testing --- linux-2.4.21/arch/arm/Makefile~arm-noshortloads +++ linux-2.4.21/arch/arm/Makefile @@ -55,8 +55,8 @@ #tune-$(CONFIG_CPU_XSCALE) :=-mtune=xscale tune-$(CONFIG_CPU_XSCALE) :=-mtune=strongarm -CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm -CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm +CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm +CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm AFLAGS +=$(apcs-y) $(arch-y) -msoft-float ifeq ($(CONFIG_CPU_26),y) @@ -289,7 +289,7 @@ arch/arm/kernel arch/arm/mm arch/arm/lib: dummy $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@) -bzImage zImage zinstall Image xipImage bootpImage install: vmlinux +bzImage zImage zinstall Image xipImage bootpImage: vmlinux @$(MAKEBOOT) $@ CLEAN_FILES += \ --- linux-2.4.21/arch/arm/config.in~pm +++ linux-2.4.21/arch/arm/config.in @@ -152,6 +152,7 @@ dep_bool ' Intel DBPXA250 Development Platform' CONFIG_ARCH_LUBBOCK $CONFIG_ARCH_PXA dep_bool ' Accelent Xscale IDP' CONFIG_ARCH_PXA_IDP $CONFIG_ARCH_PXA dep_bool ' Intrinsyc CerfBoard' CONFIG_ARCH_PXA_CERF $CONFIG_ARCH_PXA +dep_bool ' M und N Ramses' CONFIG_ARCH_RAMSES $CONFIG_ARCH_PXA dep_bool ' Trizeps-II MT6N' CONFIG_ARCH_TRIZEPS2 $CONFIG_ARCH_PXA if [ "$CONFIG_ARCH_PXA_CERF" = "y" ]; then @@ -586,6 +587,7 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC dep_bool 'Power Management support (experimental)' CONFIG_PM $CONFIG_EXPERIMENTAL +dep_bool 'Advanced power management emulation support' CONFIG_APM $CONFIG_PM dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32 string 'Default kernel command string' CONFIG_CMDLINE "" --- /dev/null +++ linux-2.4.21/arch/arm/def-configs/ramses @@ -0,0 +1,1177 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_ARM=y +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_GENERIC_BUST_SPINLOCK is not set +# CONFIG_GENERIC_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_OBSOLETE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_OMAHA is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_MX1ADS is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_RISCSTATION is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_AT91RM9200 is not set + +# +# Archimedes/A5000 Implementations +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set + +# +# Footbridge Implementations +# +# CONFIG_ARCH_CATS is not set +# CONFIG_ARCH_PERSONAL_SERVER is not set +# CONFIG_ARCH_EBSA285_ADDIN is not set +# CONFIG_ARCH_EBSA285_HOST is not set +# CONFIG_ARCH_NETWINDER is not set + +# +# SA11x0 Implementations +# +# CONFIG_SA1100_ACCELENT is not set +# CONFIG_SA1100_ASSABET is not set +# CONFIG_ASSABET_NEPONSET is not set +# CONFIG_SA1100_ADSAGC is not set +# CONFIG_SA1100_ADSBITSY is not set +# CONFIG_SA1100_ADSBITSYPLUS is not set +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_CEP is not set +# CONFIG_SA1100_CERF is not set +# CONFIG_SA1100_H3100 is not set +# CONFIG_SA1100_H3600 is not set +# CONFIG_SA1100_H3800 is not set +# CONFIG_SA1100_H3XXX is not set +# CONFIG_H3600_SLEEVE is not set +# CONFIG_SA1100_EXTENEX1 is not set +# CONFIG_SA1100_FLEXANET is not set +# CONFIG_SA1100_FREEBIRD is not set +# CONFIG_SA1100_FRODO is not set +# CONFIG_SA1100_GRAPHICSCLIENT is not set +# CONFIG_SA1100_GRAPHICSMASTER is not set +# CONFIG_SA1100_HACKKIT is not set +# CONFIG_SA1100_BADGE4 is not set +# CONFIG_SA1100_JORNADA720 is not set +# CONFIG_SA1100_HUW_WEBPANEL is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_NANOENGINE is not set +# CONFIG_SA1100_OMNIMETER is not set +# CONFIG_SA1100_PANGOLIN is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_PT_SYSTEM3 is not set +# CONFIG_SA1100_SHANNON is not set +# CONFIG_SA1100_SHERMAN is not set +# CONFIG_SA1100_SIMPAD is not set +# CONFIG_SA1100_SIMPUTER is not set +# CONFIG_SA1100_PFS168 is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_XP860 is not set +# CONFIG_SA1100_YOPY is not set +# CONFIG_SA1100_USB is not set +# CONFIG_SA1100_USB_NETLINK is not set +# CONFIG_SA1100_USB_CHAR is not set +# CONFIG_SA1100_SSP is not set + +# +# AT91RM9200 Implementations +# +# CONFIG_ARCH_AT91RM9200DK is not set + +# +# Intel PXA250/210 Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_ARCH_PXA_IDP is not set +# CONFIG_ARCH_PXA_CERF is not set +CONFIG_ARCH_RAMSES=y +# CONFIG_ARCH_TRIZEPS2 is not set +CONFIG_PXA_USB=m +CONFIG_PXA_USB_NETLINK=m +CONFIG_PXA_USB_CHAR=m + +# +# CLPS711X/EP721X Implementations +# +# CONFIG_ARCH_AUTCPU12 is not set +# CONFIG_ARCH_CDB89712 is not set +# CONFIG_ARCH_CLEP7312 is not set +# CONFIG_ARCH_EDB7211 is not set +# CONFIG_ARCH_FORTUNET is not set +# CONFIG_ARCH_GUIDEA07 is not set +# CONFIG_ARCH_P720T is not set +# CONFIG_ARCH_EP7211 is not set +# CONFIG_ARCH_EP7212 is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_PLD is not set +# CONFIG_FOOTBRIDGE is not set +# CONFIG_FOOTBRIDGE_HOST is not set +# CONFIG_FOOTBRIDGE_ADDIN is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_ARM610 is not set +# CONFIG_CPU_ARM710 is not set +# CONFIG_CPU_ARM720T is not set +# CONFIG_CPU_ARM920T is not set +# CONFIG_CPU_ARM922T is not set +# CONFIG_CPU_ARM926T is not set +# CONFIG_CPU_ARM1020 is not set +# CONFIG_CPU_ARM1020E is not set +# CONFIG_CPU_ARM1022 is not set +# CONFIG_CPU_ARM1026 is not set +# CONFIG_CPU_SA110 is not set +# CONFIG_CPU_SA1100 is not set +CONFIG_CPU_32v5=y +CONFIG_CPU_XSCALE=y +# CONFIG_XSCALE_CACHE_ERRATA is not set +# CONFIG_CPU_32v3 is not set +# CONFIG_CPU_32v4 is not set +# CONFIG_DISCONTIGMEM is not set + +# +# General setup +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set +# CONFIG_ISA_DMA is not set +CONFIG_ZBOOT_ROM=y +CONFIG_ZBOOT_ROM_TEXT=00040000 +CONFIG_ZBOOT_ROM_BSS=a00c0000 +CONFIG_CPU_FREQ=y +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_TCIC is not set +# CONFIG_PCMCIA_CLPS6700 is not set +# CONFIG_PCMCIA_SA1100 is not set +CONFIG_PCMCIA_PXA=y + +# +# MMC device drivers +# +CONFIG_MMC=m +CONFIG_MMC_PXA=m +CONFIG_MMC_BLOCK=m +CONFIG_MMC_PARTITIONS=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_XIP_KERNEL is not set +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_PM=y +CONFIG_APM=y +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="debug" +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +# CONFIG_MTD_CFI_I1 is not set +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set +# CONFIG_MTD_AMDSTD is not set +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_JEDEC is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_CDB89712 is not set +# CONFIG_MTD_SA1100 is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_IQ80310 is not set +# CONFIG_MTD_LUBBOCK is not set +CONFIG_MTD_RAMSES=y +# CONFIG_MTD_IXP425 is not set +# CONFIG_MTD_EPXA10DB is not set +# CONFIG_MTD_FORTUNET is not set +# CONFIG_MTD_AUTCPU12 is not set +# CONFIG_MTD_EDB7312 is not set +# CONFIG_MTD_H720X is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_CEIVA is not set +# CONFIG_MTD_NOR_TOTO is not set +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PCMCIA 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 +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_DOCPROBE is not set +# CONFIG_MTD_DOCECC is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_SPIA is not set +# CONFIG_MTD_NAND_TOTO is not set +# CONFIG_MTD_NAND_AUTCPU12 is not set +# CONFIG_MTD_NAND_EDB7312 is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_BLK_STATS=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK_DEV=m +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +CONFIG_VLAN_8021Q=m +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +CONFIG_BRIDGE=m +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set +# CONFIG_ARM_CIRRUS is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +CONFIG_NET_VENDOR_SMC=y +# CONFIG_WD80x3 is not set +# CONFIG_ULTRAMCA is not set +# CONFIG_ULTRA is not set +# CONFIG_ULTRA32 is not set +# CONFIG_SMC9194 is not set +CONFIG_SMC91X=y +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y +# CONFIG_STRIP is not set +# CONFIG_WAVELAN is not set +# CONFIG_ARLAN is not set +# CONFIG_AIRONET4500 is not set +# CONFIG_AIRONET4500_NONCS is not set +# CONFIG_AIRONET4500_PROC is not set +CONFIG_HERMES=m +CONFIG_PCMCIA_HERMES=m +# CONFIG_AIRO_CS is not set +CONFIG_NET_WIRELESS=y + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_ARCNET_COM20020_CS is not set +# CONFIG_PCMCIA_IBMTR is not set +# CONFIG_NET_PCMCIA_RADIO is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +CONFIG_IRDA=m +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +CONFIG_IRDA_ULTRA=y +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +CONFIG_IRDA_DEBUG=y + +# +# Infrared-port device drivers +# +CONFIG_IRTTY_SIR=m +CONFIG_IRPORT_SIR=m +# CONFIG_DONGLE is not set +# CONFIG_USB_IRDA is not set +# CONFIG_NSC_FIR is not set +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_OLD is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +CONFIG_PXA_FIR=m + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=m +CONFIG_BLK_DEV_SD=m +CONFIG_SD_EXTRA_DEVS=4 +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_DEBUG_QUEUES is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_SCSI_PCMCIA is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input core support +# +CONFIG_INPUT=y +CONFIG_INPUT_KEYBDEV=y +CONFIG_INPUT_RAMSES_KEYB=y +CONFIG_INPUT_RAMSES_WEDGE=y +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_UINPUT=m +# CONFIG_INPUT_MX1TS is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_ANAKIN is not set +# CONFIG_SERIAL_ANAKIN_CONSOLE is not set +# CONFIG_SERIAL_AMBA is not set +# CONFIG_SERIAL_AMBA_CONSOLE is not set +# CONFIG_SERIAL_CLPS711X is not set +# CONFIG_SERIAL_CLPS711X_CONSOLE is not set +# CONFIG_SERIAL_21285 is not set +# CONFIG_SERIAL_21285_OLD is not set +# CONFIG_SERIAL_21285_CONSOLE is not set +# CONFIG_SERIAL_UART00 is not set +# CONFIG_SERIAL_UART00_CONSOLE is not set +# CONFIG_SERIAL_SA1100 is not set +# CONFIG_SERIAL_SA1100_CONSOLE is not set +# CONFIG_SERIAL_OMAHA is not set +# CONFIG_SERIAL_OMAHA_CONSOLE is not set +# CONFIG_SERIAL_AT91 is not set +# CONFIG_SERIAL_AT91_CONSOLE is not set +# CONFIG_SERIAL_8250 is not set +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_HUB6 is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_PXA_ALGO=y +CONFIG_I2C_PXA_ADAP=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_PROC=m +# CONFIG_I2C_DS1307 is not set +CONFIG_I2C_DS1337=y + +# +# L3 serial bus support +# +# CONFIG_L3 is not set +# CONFIG_L3_ALGOBIT is not set +# CONFIG_L3_BIT_SA1100_GPIO is not set +# CONFIG_L3_SA1111 is not set +# CONFIG_BIT_SA1100_GPIO is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_NS558 is not set +# CONFIG_INPUT_LIGHTNING is not set +# CONFIG_INPUT_PCIGAME is not set +# CONFIG_INPUT_CS461X is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_INPUT_SERIO is not set +# CONFIG_INPUT_SERPORT is not set +# CONFIG_INPUT_ANALOG is not set +# CONFIG_INPUT_A3D is not set +# CONFIG_INPUT_ADI is not set +# CONFIG_INPUT_COBRA is not set +# CONFIG_INPUT_GF2K is not set +# CONFIG_INPUT_GRIP is not set +# CONFIG_INPUT_INTERACT is not set +# CONFIG_INPUT_TMDC is not set +# CONFIG_INPUT_SIDEWINDER is not set +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_INPUT_IFORCE_232 is not set +# CONFIG_INPUT_WARRIOR is not set +# CONFIG_INPUT_MAGELLAN is not set +# CONFIG_INPUT_SPACEORB is not set +# CONFIG_INPUT_SPACEBALL is not set +# CONFIG_INPUT_STINGER is not set +# CONFIG_INPUT_DB9 is not set +# CONFIG_INPUT_GAMECON is not set +# CONFIG_INPUT_TURBOGRAFX is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMI_PANIC_EVENT is not set +# CONFIG_IPMI_DEVICE_INTERFACE is not set +# CONFIG_IPMI_KCS is not set +# CONFIG_IPMI_WATCHDOG is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_AMD_PM768 is not set +# CONFIG_NVRAM is not set +CONFIG_RTC=m +CONFIG_PXA_RTC=m +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +CONFIG_PCMCIA_SERIAL_CS=m +# CONFIG_SYNCLINK_CS is not set + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# +CONFIG_VIDEO_PROC_FS=y +# CONFIG_I2C_PARPORT is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_TUNER_3036 is not set +# CONFIG_VIDEO_STRADIS is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIDEO_ZORAN_BUZ is not set +# CONFIG_VIDEO_ZORAN_DC10 is not set +# CONFIG_VIDEO_ZORAN_LML33 is not set +# CONFIG_VIDEO_ZR36120 is not set +# CONFIG_VIDEO_MEYE is not set +# CONFIG_VIDEO_CYBERPRO is not set + +# +# Radio Adapters +# +# CONFIG_RADIO_GEMTEK_PCI is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_MAESTRO is not set +# CONFIG_RADIO_MIROPCM20 is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +# CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_LZO is not set +# CONFIG_JFFS2_LZARI is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_PROC=y +CONFIG_CRAMFS=m +# CONFIG_CRAMFS_LINEAR is not set +# CONFIG_CRAMFS_LINEAR_XIP is not set +# CONFIG_ROOT_CRAMFS_LINEAR is not set +CONFIG_TMPFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=m +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Console drivers +# +CONFIG_PC_KEYMAP=y +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +CONFIG_FB=y +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_ACORN is not set +# CONFIG_FB_ANAKIN is not set +# CONFIG_FB_CLPS711X is not set +# CONFIG_FB_SA1100 is not set +# CONFIG_FB_DBMX1 is not set +CONFIG_FB_PXA=y +# CONFIG_FB_PXA_8BPP is not set +CONFIG_FB_PXA_16BPP=y +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_FBCON_ADVANCED=y +# CONFIG_FBCON_MFB is not set +# CONFIG_FBCON_CFB2 is not set +# CONFIG_FBCON_CFB4 is not set +# CONFIG_FBCON_CFB8 is not set +CONFIG_FBCON_CFB16=y +# CONFIG_FBCON_CFB24 is not set +# CONFIG_FBCON_CFB32 is not set +# CONFIG_FBCON_AFB is not set +# CONFIG_FBCON_ILBM is not set +# CONFIG_FBCON_IPLAN2P2 is not set +# CONFIG_FBCON_IPLAN2P4 is not set +# CONFIG_FBCON_IPLAN2P8 is not set +# CONFIG_FBCON_MAC is not set +# CONFIG_FBCON_VGA_PLANES is not set +# CONFIG_FBCON_VGA is not set +# CONFIG_FBCON_HGA is not set +# CONFIG_FBCON_FONTWIDTH8_ONLY is not set +# CONFIG_FBCON_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Sound +# +CONFIG_SOUND=y +# CONFIG_SOUND_ALI5455 is not set +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_CMPCI is not set +# CONFIG_SOUND_EMU10K1 is not set +# CONFIG_MIDI_EMU10K1 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_FORTE is not set +# CONFIG_SOUND_ICH is not set +# CONFIG_SOUND_RME96XX is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_MIDI_VIA82CXXX is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_VIDC is not set +# CONFIG_SOUND_WAVEARTIST is not set +CONFIG_SOUND_PXA_AC97=y +# CONFIG_SOUND_TVMIXER is not set + +# +# Multimedia Capabilities Port drivers +# +CONFIG_MCP=y +# CONFIG_MCP_SA1100 is not set +# CONFIG_MCP_UCB1200 is not set +# CONFIG_MCP_UCB1200_AUDIO is not set +# CONFIG_MCP_UCB1200_TS is not set +CONFIG_MCP_UCB1400_TS=y + +# +# USB support +# +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_OHCI_SA1111 is not set +CONFIG_USB_SL811HS_ALT=m +CONFIG_USB_AUDIO=m +CONFIG_USB_EMI26=m +CONFIG_USB_MIDI=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +# CONFIG_USB_STORAGE_ISD200 is not set +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=m +CONFIG_USB_HID=m +CONFIG_USB_HIDINPUT=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_KBD=m +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +CONFIG_USB_DC2XX=m +CONFIG_USB_MDC800=m +CONFIG_USB_SCANNER=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_OV511=m +CONFIG_USB_PWC=m +CONFIG_USB_SE401=m +CONFIG_USB_STV680=m +CONFIG_USB_VICAM=m +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_BRLVGER is not set +# CONFIG_USB_LCD is not set + +# +# Bluetooth support +# +CONFIG_BLUEZ=m +CONFIG_BLUEZ_L2CAP=m +CONFIG_BLUEZ_SCO=m +CONFIG_BLUEZ_RFCOMM=m +CONFIG_BLUEZ_RFCOMM_TTY=y +CONFIG_BLUEZ_BNEP=m +CONFIG_BLUEZ_BNEP_MC_FILTER=y +CONFIG_BLUEZ_BNEP_PROTO_FILTER=y +CONFIG_BLUEZ_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BLUEZ_HCIUSB=m +CONFIG_BLUEZ_HCIUSB_SCO=y +CONFIG_BLUEZ_HCIUART=m +CONFIG_BLUEZ_HCIUART_H4=y +CONFIG_BLUEZ_HCIUART_BCSP=y +# CONFIG_BLUEZ_HCIUART_BCSP_TXCRC is not set +CONFIG_BLUEZ_HCIBFUSB=m +CONFIG_BLUEZ_HCIDTL1=m +CONFIG_BLUEZ_HCIBT3C=m +CONFIG_BLUEZ_HCIBLUECARD=m +CONFIG_BLUEZ_HCIBTUART=m +CONFIG_BLUEZ_HCIVHCI=m + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_NO_PGT_CACHE is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_DC21285_PORT is not set +# CONFIG_DEBUG_CLPS711X_UART2 is not set + +# +# Library routines +# +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +# CONFIG_REED_SOLOMON is not set +CONFIG_FW_LOADER=m --- linux-2.4.21/arch/arm/mach-pxa/Makefile~pm +++ linux-2.4.21/arch/arm/mach-pxa/Makefile @@ -14,8 +14,11 @@ obj-n := obj- := -export-objs := generic.o irq.o dma.o sa1111.o \ - usb_ctl.o usb_recv.o usb_send.o +export-objs := apm.o generic.o irq.o dma.o sa1111.o \ + usb_ctl.o usb_recv.o usb_send.o pm.o + + +export-objs += ramses.o # Common support (must be linked before board specific support) obj-y += generic.o irq.o dma.o @@ -27,6 +30,7 @@ obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o obj-$(CONFIG_ARCH_PXA_CERF) += cerf.o obj-$(CONFIG_ARCH_PXA_IDP) += idp.o +obj-$(CONFIG_ARCH_RAMSES) += ramses.o obj-$(CONFIG_ARCH_TRIZEPS2) += trizeps2.o # Support for blinky lights @@ -48,6 +52,7 @@ # Misc features obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o include $(TOPDIR)/Rules.make --- /dev/null +++ linux-2.4.21/arch/arm/mach-pxa/apm.c @@ -0,0 +1,491 @@ +/* + * bios-less APM driver for ARM Linux + * Jamey Hicks + * adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com) + * + * APM 1.2 Reference: + * Intel Corporation, Microsoft Corporation. Advanced Power Management + * (APM) BIOS Interface Specification, Revision 1.2, February 1996. + * + * [This document is available from Microsoft at: + * http://www.microsoft.com/hwdev/busbios/amp_12.htm] + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_SA1100_H3XXX +#include +#endif + +#include "pm-common.c" + +struct apm_bios_info apm_bios_info = { + /* this driver simulates APM version 1.2 */ + version: 0x102, + flags: APM_32_BIT_SUPPORT +}; + +/* + * The apm_bios device is one of the misc char devices. + * This is its minor number. + */ +#define APM_MINOR_DEV 134 + +/* + * See Documentation/Config.help for the configuration options. + * + * Various options can be changed at boot time as follows: + * (We allow underscores for compatibility with the modules code) + * apm=on/off enable/disable APM + * [no-]power[-_]off power off on shutdown + */ + +/* + * Maximum number of events stored + */ +#define APM_MAX_EVENTS 10 + +/* + * The per-file APM data + */ +struct apm_user { + int magic; + struct apm_user * next; + int suser: 1; + int suspend_wait: 1; + int suspend_result; + int suspends_pending; + int standbys_pending; + int suspends_read; + int standbys_read; + int event_head; + int event_tail; + apm_event_t events[APM_MAX_EVENTS]; +}; + +/* + * The magic number in apm_user + */ +#define APM_BIOS_MAGIC 0x4101 + +/* + * Local variables + */ + +#ifdef CONFIG_APM_RTC_IS_GMT +#define clock_cmos_diff 0 +#define got_clock_diff 1 +#endif +static int apm_disabled; +#ifdef CONFIG_SMP +static int power_off; +#else +static int power_off = 1; +#endif +static int exit_kapmd; +static int kapmd_running; + +static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); +static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); +static struct apm_user * user_list = NULL; + +static char driver_version[] = "1.13"; /* no spaces */ + +typedef struct lookup_t { + int key; + char * msg; +} lookup_t; + +static const lookup_t error_table[] = { +/* N/A { APM_SUCCESS, "Operation succeeded" }, */ + { APM_DISABLED, "Power management disabled" }, + { APM_CONNECTED, "Real mode interface already connected" }, + { APM_NOT_CONNECTED, "Interface not connected" }, + { APM_16_CONNECTED, "16 bit interface already connected" }, +/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ + { APM_32_CONNECTED, "32 bit interface already connected" }, + { APM_32_UNSUPPORTED, "32 bit interface not supported" }, + { APM_BAD_DEVICE, "Unrecognized device ID" }, + { APM_BAD_PARAM, "Parameter out of range" }, + { APM_NOT_ENGAGED, "Interface not engaged" }, + { APM_BAD_FUNCTION, "Function not supported" }, + { APM_RESUME_DISABLED, "Resume timer disabled" }, + { APM_BAD_STATE, "Unable to enter requested state" }, +/* N/A { APM_NO_EVENTS, "No events pending" }, */ + { APM_NO_ERROR, "BIOS did not set a return code" }, + { APM_NOT_PRESENT, "No APM present" } +}; +#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) + +static int (*apm_get_power_status)(u_char *ac_line_status, + u_char *battery_status, + u_char *battery_flag, + u_char *battery_percentage, + u_short *battery_life) = 0; + +void apm_register_get_power_status( int (*fn)(u_char *ac_line_status, + u_char *battery_status, + u_char *battery_flag, + u_char *battery_percentage, + u_short *battery_life)) +{ + apm_get_power_status = fn; +} + +static int queue_empty(struct apm_user *as) +{ + return as->event_head == as->event_tail; +} + +static apm_event_t get_queued_event(struct apm_user *as) +{ + as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; + return as->events[as->event_tail]; +} + +static int check_apm_user(struct apm_user *as, const char *func) +{ + if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { + printk(KERN_ERR "apm: %s passed bad filp\n", func); + return 1; + } + return 0; +} + +static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) +{ + struct apm_user * as; + int i; + apm_event_t event; + DECLARE_WAITQUEUE(wait, current); + + as = fp->private_data; + if (check_apm_user(as, "read")) + return -EIO; + if (count < sizeof(apm_event_t)) + return -EINVAL; + if (queue_empty(as)) { + if (fp->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&apm_waitqueue, &wait); + printk("do_read: waiting\n"); +repeat: + set_current_state(TASK_INTERRUPTIBLE); + if (queue_empty(as) && !signal_pending(current)) { + schedule(); + goto repeat; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&apm_waitqueue, &wait); + } + i = count; + while ((i >= sizeof(event)) && !queue_empty(as)) { + event = get_queued_event(as); + printk(" do_read: event=%d\n", event); + if (copy_to_user(buf, &event, sizeof(event))) { + if (i < count) + break; + return -EFAULT; + } + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + as->suspends_read++; + break; + + case APM_SYS_STANDBY: + case APM_USER_STANDBY: + as->standbys_read++; + break; + } + buf += sizeof(event); + i -= sizeof(event); + } + if (i < count) + return count - i; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static unsigned int do_poll(struct file *fp, poll_table * wait) +{ + struct apm_user * as; + + as = fp->private_data; + if (check_apm_user(as, "poll")) + return 0; + poll_wait(fp, &apm_waitqueue, wait); + if (!queue_empty(as)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int do_ioctl(struct inode * inode, struct file *filp, + u_int cmd, u_long arg) +{ + struct apm_user * as; + + as = filp->private_data; + if (check_apm_user(as, "ioctl")) + return -EIO; + if (!as->suser) + return -EPERM; + switch (cmd) { + case APM_IOC_SUSPEND: + pm_suggest_suspend(); + break; + default: + printk("//hs %x\n", cmd); + return -EINVAL; + } + return 0; +} + +static int do_release(struct inode * inode, struct file * filp) +{ + struct apm_user * as; + + as = filp->private_data; + if (check_apm_user(as, "release")) + return 0; + filp->private_data = NULL; + lock_kernel(); + unlock_kernel(); + kfree(as); + return 0; +} + +static int do_open(struct inode * inode, struct file * filp) +{ + struct apm_user * as; + + as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n", + sizeof(*as)); + return -ENOMEM; + } + as->magic = APM_BIOS_MAGIC; + as->event_tail = as->event_head = 0; + as->suspends_pending = as->standbys_pending = 0; + as->suspends_read = as->standbys_read = 0; + /* + * XXX - this is a tiny bit broken, when we consider BSD + * process accounting. If the device is opened by root, we + * instantly flag that we used superuser privs. Who knows, + * we might close the device immediately without doing a + * privileged operation -- cevans + */ + as->suser = capable(CAP_SYS_ADMIN); + as->next = user_list; + user_list = as; + filp->private_data = as; + return 0; +} + +static int apm_get_info(char *buf, char **start, off_t fpos, int length) +{ + char * p; + unsigned short dx; + unsigned short error; + unsigned char ac_line_status = 0xff; + unsigned char battery_status = 0xff; + unsigned char battery_flag = 0xff; + unsigned char percentage = 0xff; + int time_units = -1; + char *units = "?"; + + p = buf; + + if ( (smp_num_cpus == 1) && + apm_get_power_status && + !(error = apm_get_power_status(&ac_line_status, + &battery_status, &battery_flag, &percentage, &dx))) { + if (apm_bios_info.version > 0x100) { + if (dx != 0xffff) { + units = (dx & 0x8000) ? "min" : "sec"; + time_units = dx & 0x7fff; + } + } + } + /* Arguments, with symbols from linux/apm_bios.h. Information is + from the Get Power Status (0x0a) call unless otherwise noted. + + 0) Linux driver version (this will change if format changes) + 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. + 2) APM flags from APM Installation Check (0x00): + bit 0: APM_16_BIT_SUPPORT + bit 1: APM_32_BIT_SUPPORT + bit 2: APM_IDLE_SLOWS_CLOCK + bit 3: APM_BIOS_DISABLED + bit 4: APM_BIOS_DISENGAGED + 3) AC line status + 0x00: Off-line + 0x01: On-line + 0x02: On backup power (BIOS >= 1.1 only) + 0xff: Unknown + 4) Battery status + 0x00: High + 0x01: Low + 0x02: Critical + 0x03: Charging + 0x04: Selected battery not present (BIOS >= 1.2 only) + 0xff: Unknown + 5) Battery flag + bit 0: High + bit 1: Low + bit 2: Critical + bit 3: Charging + bit 7: No system battery + 0xff: Unknown + 6) Remaining battery life (percentage of charge): + 0-100: valid + -1: Unknown + 7) Remaining battery life (time units): + Number of remaining minutes or seconds + -1: Unknown + 8) min = minutes; sec = seconds */ + + p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", + driver_version, + (apm_bios_info.version >> 8) & 0xff, + apm_bios_info.version & 0xff, + apm_bios_info.flags, + ac_line_status, + battery_status, + battery_flag, + percentage, + time_units, + units); + + return p - buf; +} + +#ifndef MODULE +static int __init apm_setup(char *str) +{ + int invert; + +printk("//hs apm_setup\n"); + while ((str != NULL) && (*str != '\0')) { + if (strncmp(str, "off", 3) == 0) + apm_disabled = 1; + if (strncmp(str, "on", 2) == 0) + apm_disabled = 0; + invert = (strncmp(str, "no-", 3) == 0); + if (invert) + str += 3; + if ((strncmp(str, "power-off", 9) == 0) || + (strncmp(str, "power_off", 9) == 0)) + power_off = !invert; + str = strchr(str, ','); + if (str != NULL) + str += strspn(str, ", \t"); + } + return 1; +} + +__setup("apm=", apm_setup); +#endif + +static struct file_operations apm_bios_fops = { + owner: THIS_MODULE, + read: do_read, + poll: do_poll, + ioctl: do_ioctl, + open: do_open, + release: do_release, +}; + +static struct miscdevice apm_device = { + APM_MINOR_DEV, + "apm_bios", + &apm_bios_fops +}; + +#define APM_INIT_ERROR_RETURN return -1 + +/* + * Just start the APM thread. We do NOT want to do APM BIOS + * calls from anything but the APM thread, if for no other reason + * than the fact that we don't trust the APM BIOS. This way, + * most common APM BIOS problems that lead to protection errors + * etc will have at least some level of being contained... + * + * In short, if something bad happens, at least we have a choice + * of just killing the apm thread.. + */ +static int __init apm_init(void) +{ + if (apm_bios_info.version == 0) { + printk(KERN_INFO "apm: BIOS not found.\n"); + APM_INIT_ERROR_RETURN; + } + printk(KERN_INFO + "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n", + ((apm_bios_info.version >> 8) & 0xff), + (apm_bios_info.version & 0xff), + apm_bios_info.flags, + driver_version); + + if (apm_disabled) { + printk(KERN_NOTICE "apm: disabled on user request.\n"); + APM_INIT_ERROR_RETURN; + } + + if (PM_IS_ACTIVE()) { + printk(KERN_NOTICE "apm: overridden by ACPI.\n"); + APM_INIT_ERROR_RETURN; + } + pm_active = 1; + + create_proc_info_entry("apm", 0, NULL, apm_get_info); + + misc_register(&apm_device); + + return 0; +} + +module_init(apm_init); + +#ifdef MODULE +static void __exit apm_exit(void) +{ + misc_deregister(&apm_device); + remove_proc_entry("apm", NULL); + if (power_off) + pm_power_off = NULL; + exit_kapmd = 1; + while (kapmd_running) + schedule(); + pm_active = 0; +} + +module_exit(apm_exit); + +MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell"); +MODULE_DESCRIPTION("A minimal emulation of APM"); +MODULE_PARM(power_off, "i"); +MODULE_PARM_DESC(power_off, "Enable power off"); +#endif --- linux-2.4.21/arch/arm/mach-pxa/cpu-pxa.c~ramses-corevolt +++ linux-2.4.21/arch/arm/mach-pxa/cpu-pxa.c @@ -39,7 +39,7 @@ #include -#define DEBUGGING 1 +#define DEBUGGING 0 #if DEBUGGING static unsigned int freq_debug = DEBUGGING; @@ -52,6 +52,7 @@ unsigned int khz; unsigned int cccr; unsigned int pxbus; + unsigned int corevolt; } pxa_freqs_t; #define CCLKCFG_TURBO 0x1 @@ -79,23 +80,23 @@ static pxa_freqs_t pxa250_valid_freqs[] = { - {199100, 0x141, 99}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ - {298600, 0x1c1, 99}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ - {398100, 0x241, 99}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ + {199100, 0x141, 99, 115}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ + {298600, 0x1c1, 99, 125}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ + {398100, 0x241, 99, 135}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ {0,0} }; static pxa_freqs_t pxa255_valid_freqs[] = { - { 99000, 0x121, 50}, /* mem= 99, run= 99, turbo= 99, PXbus= 50 */ -OC( {118000, 0x122, 59},)/* mem=118, run=118, turbo=118, PXbus= 59 OC'd mem */ - {199100, 0x141, 99}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ -OC( {236000, 0x142,118},)/* mem=118, run=236, turbo=236, PXbus=118 OC'd mem */ - {298600, 0x1c1, 99}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ -OC( {354000, 0x1c2,118},)/* mem=118, run=236, turbo=354, PXbus=118 OC'd mem */ - {398099, 0x241, 99}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ - {398100, 0x161,196}, /* mem= 99, run=398, turbo=398, PXbus=196 */ -OC( {471000, 0x162,236},)/* mem=118, run=471, turbo=471, PXbus=236 OC'd mem/core/bus */ + { 99000, 0x121, 50, 105}, /* mem= 99, run= 99, turbo= 99, PXbus= 50 */ +OC( {118000, 0x122, 59, 115},)/* mem=118, run=118, turbo=118, PXbus= 59 OC'd mem */ + {199100, 0x141, 99, 115}, /* mem= 99, run=199, turbo=199, PXbus= 99 */ +OC( {236000, 0x142,118, 125},)/* mem=118, run=236, turbo=236, PXbus=118 OC'd mem */ + {298600, 0x1c1, 99, 125}, /* mem= 99, run=199, turbo=298, PXbus= 99 */ +OC( {354000, 0x1c2,118, 135},)/* mem=118, run=236, turbo=354, PXbus=118 OC'd mem */ + {398099, 0x241, 99, 135}, /* mem= 99, run=199, turbo=398, PXbus= 99 */ + {398100, 0x161,196, 135}, /* mem= 99, run=398, turbo=398, PXbus=196 */ +OC( {471000, 0x162,236, 150},)/* mem=118, run=471, turbo=471, PXbus=236 OC'd mem/core/bus */ {0,0} }; @@ -109,7 +110,7 @@ int i=0; while( pxa_valid_freqs[i].khz) { - if( pxa_valid_freqs[i].khz == khz) + if (pxa_valid_freqs[i].khz == khz) return &pxa_valid_freqs[i]; i++; } @@ -141,14 +142,17 @@ void *ramstart = phys_to_virt(0xa0000000); pxa_freqs_t *freq_info; - if( ! supported) return; + if (! supported) return; freq_info = pxa_get_freq_info( khz); - if( ! freq_info) return; + if (! freq_info) return; + + if (freq_info->corevolt > ramses_corevolt_shadow) + ramses_set_corevolt(freq_info->corevolt); CCCR = freq_info->cccr; - if( freq_debug) + if (freq_debug) printk(KERN_INFO "Changing CPU frequency to %d Mhz (PXbus=%dMhz).\n", khz/1000, freq_info->pxbus); @@ -184,6 +188,9 @@ : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart) : "r4", "r5"); local_irq_restore(flags); + + if (freq_info->corevolt < ramses_corevolt_shadow) + ramses_set_corevolt(freq_info->corevolt); } static int pxa_init_freqs( void) @@ -191,19 +198,19 @@ int cpu_ver; asm("mrc%? p15, 0, %0, c0, c0" : "=r" (cpu_ver)); - if( (cpu_ver & 0xf) <= PXA250_REV_A1) + if ((cpu_ver & 0xf) <= PXA250_REV_A1) { return 0; } - if( (cpu_ver & 0xf) <= PXA250_REV_B2) + if ((cpu_ver & 0xf) <= PXA250_REV_B2) { - if( freq_debug) printk(KERN_INFO "Using PXA250 frequency points.\n"); + if (freq_debug) printk(KERN_INFO "Using PXA250 frequency points.\n"); pxa_valid_freqs = pxa250_valid_freqs; } else /* C0 and above */ { - if( freq_debug) printk(KERN_INFO "Using PXA255 frequency points.\n"); + if (freq_debug) printk(KERN_INFO "Using PXA255 frequency points.\n"); pxa_valid_freqs = pxa255_valid_freqs; } @@ -212,24 +219,23 @@ static int __init pxa_clk_init(void) { - if( pxa_init_freqs()) + if (pxa_init_freqs()) { - if( freq_debug) printk(KERN_INFO "Registering CPU frequency change support.\n"); + if (freq_debug) printk(KERN_INFO "Registering CPU frequency change support.\n"); supported = 1; cpufreq_init( get_clk_frequency_khz(0), PXA25x_MIN_FREQ, PXA25x_MAX_FREQ); - cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); } else { - if( freq_debug) printk(KERN_INFO "Disabling CPU frequency change support.\n"); + if (freq_debug) printk(KERN_INFO "Disabling CPU frequency change support.\n"); /* Note that we have to initialize the generic code in order to * release a lock (cpufreq_sem). Any registration for freq changes * (e.g. lcd driver) will get blocked otherwise. */ cpufreq_init( 0, 0, 0); - cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); } + cpufreq_setfunctions(pxa_validate_speed, pxa_setspeed); return 0; } --- linux-2.4.21/arch/arm/mach-pxa/generic.c~pm +++ linux-2.4.21/arch/arm/mach-pxa/generic.c @@ -28,6 +28,11 @@ #include #include +#ifdef CONFIG_PXA_RTC_HACK +#include +#include +#endif + #include "generic.h" /* @@ -139,4 +144,41 @@ { iotable_init(standard_io_desc); get_clk_frequency_khz( 1); +#ifdef CONFIG_PXA_RTC_HACK + pxa_rtc_hack_init(); +#endif +} + + + +#ifdef CONFIG_PXA_RTC_HACK +unsigned long *save_RCNR = 0; + +void pxa_rtc_hack_init(void) +{ + /* + This has to be here since I guess the bootmem API is the + right choice to allocate the memory during boot + place. And we are sure that timer iqr is not already + running. + - Christian Pellegin + */ + unsigned long pxa_rtc_hack = 0; + + pxa_rtc_hack = meminfo.bank[meminfo.nr_banks-1].start + + meminfo.bank[meminfo.nr_banks-1].size - + PAGE_SIZE; + reserve_bootmem(pxa_rtc_hack, PAGE_SIZE); + printk("Reserved %ld bytes at %lx for RTC hack\n", + PAGE_SIZE, pxa_rtc_hack); + save_RCNR = (unsigned long *) phys_to_virt(pxa_rtc_hack); + if ( (save_RCNR[0] ^ save_RCNR[1]) == 0xffffffff ) { + printk("Restoring saved RCNR value to %ld (from %lx)\n", + save_RCNR[0], (unsigned long) save_RCNR); + RCNR = save_RCNR[0]; + } + else { + printk("No valid saved RCNR value found at %lx\n", (unsigned long) save_RCNR); + } } +#endif /* CONFIG_PXA_RTC_HACK */ --- linux-2.4.21/arch/arm/mach-pxa/generic.h~pm +++ linux-2.4.21/arch/arm/mach-pxa/generic.h @@ -17,3 +17,7 @@ mi->bank[__nr].size = (__size), \ mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27) +#ifdef CONFIG_PXA_RTC_HACK +void pxa_rtc_hack_init(void); +extern unsigned long *save_RCNR; +#endif --- /dev/null +++ linux-2.4.21/arch/arm/mach-pxa/pm-common.c @@ -0,0 +1,285 @@ +/* + * SA1100 Power Management Routines + * + * Copyright (c) 2001 Cliff Brake + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2001-02-06: Cliff Brake Initial code + * + * 2001-02-25: Sukjae Cho & + * Chester Kuo + * Save more value for the resume function! Support + * Bitsy/Assabet/Freebird board + * + * 2001-08-29: Nicolas Pitre + * Cleaned up, pushed platform dependent stuff + * in the platform specific files. + * + * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array. + * Storage is local on the stack now. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#ifdef CONFIG_IPAQ_HANDHELD +#include +#endif + +#define __KERNEL_SYSCALLS__ +#include + + + +static char pm_helper_path[128] = "/etc/apm/apmd_proxy"; +extern int exec_usermodehelper(char *path, char **argv, char **envp); +int debug_pm = 0; +static int pm_helper_veto = 0; + +static int +run_sbin_pm_helper( pm_request_t action ) +{ + int i; + char *argv[3], *envp[8]; + + if (!pm_helper_path[0]) + return 2; + + if ( action != PM_SUSPEND && action != PM_RESUME ) + return 1; + + /* Be root */ + current->uid = current->gid = 0; + + i = 0; + argv[i++] = pm_helper_path; + argv[i++] = (action == PM_RESUME ? "resume" : "suspend"); + + if (action == PM_RESUME) + argv[i++]="suspend"; + + argv[i] = 0; + + i = 0; + /* minimal command environment */ + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[i] = 0; + + /* other stuff we want to pass to /sbin/pm_helper */ + return exec_usermodehelper (argv [0], argv, envp); +} + +/* + * If pm_suggest_suspend_hook is non-NULL, it is called by pm_suggest_suspend. + */ +int (*pm_suggest_suspend_hook)(int state); +EXPORT_SYMBOL(pm_suggest_suspend_hook); + +/* + * If pm_use_sbin_pm_helper is nonzero, then run_sbin_pm_helper is called before suspend and after resume + */ +int pm_use_sbin_pm_helper = 1; +EXPORT_SYMBOL(pm_use_sbin_pm_helper); + +/* + * If sysctl_pm_do_suspend_hook is non-NULL, it is called by sysctl_pm_do_suspend. + * If it returns a true value, then pm_suspend is not called. + * Use this to hook in apmd, for now. + */ +int (*pm_sysctl_suspend_hook)(int state); +EXPORT_SYMBOL(pm_sysctl_suspend_hook); + +int pm_suspend(void); + +int pm_suggest_suspend(void) +{ + int retval; + + if (pm_suggest_suspend_hook) { + if (pm_suggest_suspend_hook(PM_SUSPEND)) + return 0; + } + + if (pm_use_sbin_pm_helper) { + pid_t pid; + int res; + int status = 0; + unsigned int old_fs; + + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_SUSPEND, 0 ); + if ( pid < 0 ) + return pid; + + if (debug_pm) + printk(KERN_CRIT "%s:%d got pid=%d\n", __FUNCTION__, __LINE__, pid); + + old_fs = get_fs (); + set_fs (get_ds ()); + res = waitpid(pid, &status, __WCLONE); + set_fs (old_fs); + + if ( pid != res ) { + if (debug_pm) + printk(KERN_CRIT ": waitpid returned %d (exit_code=%d); not suspending\n", res, status ); + + return -1; + } + + /*if ( WIFEXITED(status) && ( WIFEXITSTATUS(status) != 0 )) {*/ + if (( status & 0xff7f ) != 0 ) { + if (pm_helper_veto) { + if (debug_pm) + printk(KERN_CRIT "%s: SUSPEND WAS CANCELLED BY pm_helper (exit status %d)\n", __FUNCTION__, status >> 8); + return -1; + } else { + if (debug_pm) + printk(KERN_CRIT "%s: pm_helper returned %d, but going ahead anyway\n", __FUNCTION__, status >> 8); + } + } + } + + if (debug_pm) + printk(KERN_CRIT "%s: REALLY SUSPENDING NOW\n", __FUNCTION__ ); + + if (pm_sysctl_suspend_hook) { + if (pm_sysctl_suspend_hook(PM_SUSPEND)) + return 0; + } + + retval = pm_suspend(); + if (retval) { + if (debug_pm) + printk(KERN_CRIT "pm_suspend returned %d\n", retval); + return retval; + } + + if (pm_use_sbin_pm_helper) { + pid_t pid; + + if (debug_pm) + printk(KERN_CRIT "%s: running pm_helper for wakeup\n", __FUNCTION__); + + pid = kernel_thread ((int (*) (void *)) run_sbin_pm_helper, (void *) PM_RESUME, 0 ); + if ( pid < 0 ) + return pid; + + if ( pid != waitpid ( pid, NULL, __WCLONE )) + return -1; + } + + return 0; +} + +EXPORT_SYMBOL(pm_suggest_suspend); + + +/* + * Send us to sleep. + */ +int pm_suspend(void) +{ + int retval; + + retval = pm_send_all(PM_SUSPEND, (void *)3); + if ( retval ) + return retval; + +#ifdef CONFIG_IPAQ_HANDHELD + retval = h3600_power_management(PM_SUSPEND); + if (retval) { + pm_send_all(PM_RESUME, (void *)0); + return retval; + } +#endif + + retval = pm_do_suspend(); + +#ifdef CONFIG_IPAQ_HANDHELD + /* Allow the power management routines to override resuming */ + while ( h3600_power_management(PM_RESUME) ) + retval = pm_do_suspend(); +#endif + + pm_send_all(PM_RESUME, (void *)0); + + return retval; +} +EXPORT_SYMBOL(pm_suspend); + +#ifdef CONFIG_SYSCTL +/* + * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than + * linux/sysctl.h. + * + * This means our interface here won't survive long - it needs a new + * interface. Quick hack to get this working - use sysctl id 9999. + */ +#warning ACPI broke the kernel, this interface needs to be fixed up. +#define CTL_ACPI 9999 +#define ACPI_S1_SLP_TYP 19 + +/* + * Send us to sleep. + */ +static int sysctl_pm_do_suspend(void) +{ + int retval; + + retval = pm_send_all(PM_SUSPEND, (void *)3); + + if (retval == 0) { + retval = pm_do_suspend(); + + pm_send_all(PM_RESUME, (void *)0); + } + + return retval; +} + +static struct ctl_table pm_table[] = +{ + {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_do_suspend}, + {2, "helper", pm_helper_path, sizeof(pm_helper_path), 0644, NULL, (proc_handler *)&proc_dostring}, + {3, "debug", &debug_pm, sizeof(debug_pm), 0644, NULL, (proc_handler *)&proc_dointvec}, + {4, "helper_veto", &pm_helper_veto, sizeof(pm_helper_veto), 0644, NULL, (proc_handler *)&proc_dointvec}, + {0} +}; + +static struct ctl_table pm_dir_table[] = +{ + {CTL_ACPI, "pm", NULL, 0, 0555, pm_table}, + {0} +}; + +/* + * Initialize power interface + */ +static int __init pm_init(void) +{ + register_sysctl_table(pm_dir_table, 1); + return 0; +} + +__initcall(pm_init); + +#endif + --- linux-2.4.21/arch/arm/mach-pxa/pm.c~pm +++ linux-2.4.21/arch/arm/mach-pxa/pm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -82,7 +83,7 @@ /* * Temporary solution. This won't be necessary once - * we move pxa support into the serial/* driver + * we move pxa support into the serial driver * Save the FF UART */ SAVE(FFIER); @@ -176,7 +177,7 @@ /* * Temporary solution. This won't be necessary once - * we move pxa support into the serial/* driver. + * we move pxa support into the serial driver. * Restore the FF UART. */ RESTORE(FFMCR); @@ -209,6 +210,12 @@ return virt_to_phys(sp); } +#ifndef CONFIG_APM +/* + * This code is only needed if we don't compile in APM support. + * If we compile APM support in, then this code is in pm-common.c + */ + #ifdef CONFIG_SYSCTL /* * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than @@ -263,3 +270,6 @@ __initcall(pm_init); #endif +#endif + +EXPORT_SYMBOL(pm_do_suspend); --- linux-2.4.21/arch/arm/mach-pxa/pxa_usb.h~pxa-usb +++ linux-2.4.21/arch/arm/mach-pxa/pxa_usb.h @@ -39,6 +39,7 @@ int pxa_usb_xmitter_avail( void ); int pxa_usb_send(char *buf, int len, usb_callback_t callback); void sa110a_usb_send_reset(void); +void pxa_usb_send_reset(void); /* in usb_recev.c */ int pxa_usb_recv(char *buf, int len, usb_callback_t callback); --- /dev/null +++ linux-2.4.21/arch/arm/mach-pxa/ramses.c @@ -0,0 +1,844 @@ +/* + * linux/arch/arm/mach-pxa/ramses.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Copyright (c) 2002,2003,2004 by M&N Logistik-Lösungen Online GmbH + * written by Holger Schurig + * + * 2001-09-13: Cliff Brake + * Initial code + * + * 2002-10-09: adaptions to ramses + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_APM +#include +#endif +#define USE_UCB +//#define PFI_LED +#define PFI_TURNOFF + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef USE_UCB +#include "../drivers/misc/ucb1x00.h" +#endif + +#include "generic.h" + + +/* shadow registers for write only registers */ +u16 ramses_control_shadow = + RAMSES_CONTROL_LED_BLUE_ + + RAMSES_CONTROL_LED_ORANGE_ + + RAMSES_CONTROL_SCANNER_WAKE_ + + RAMSES_CONTROL_SCANNER_TRIG_; + +/* various flags the change the behavior of the kernel */ +unsigned int ramses_flags = + RAMSES_FLAGS_KEY_SCAN + + RAMSES_FLAGS_KEY_SUSPEND + + RAMSES_FLAGS_KEY_OFF; + + +/******************************************************************/ +/* Corevoltage settings */ +/******************************************************************/ + +int ramses_corevolt_shadow = 150; + +void ramses_set_corevolt(int volt) +{ + int val = 0; + switch (volt) { + case 150: + val = 5; + break; + case 135: + val = 8; + break; + case 125: + val = 10; + break; + case 115: + val = 12; + break; + case 105: + val = 14; + break; + } + if (val) { + ramses_corevolt_shadow = volt; + RAMSES_COREVOLT = val; + RAMSES_CPLD_PERIPH_PWR |= CORE_VAR_EN; + } +} + + +/******************************************************************/ +/* LCD stuff */ +/******************************************************************/ + +u16 ramses_lcd_type; + +void ramses_lcd_power_on(void) +{ + //printk("--> ramses_lcd_power_on\n"); + + /* 1. VCC */ + RAMSES_CPLD_LCD |= RAMSES_LCD_VCC; + + /* 2. Signal */ + + /* 3. the PWM */ + CKEN |= (CKEN0_PWM0 | CKEN1_PWM1); + + /* 4. nDISPOFF (done at backlight_on time) */ + RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; +} + + +void ramses_lcd_power_off(void) +{ + //printk("--> ramses_lcd_power_off\n"); + if (RAMSES_CPLD_LCD & RAMSES_LCD_DISPOFF) { + //printk("--> turn bl off first\n"); + ramses_lcd_backlight_off(); + } + + /* 1. nDISPOFF (just to be sure) */ + RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; + + // for Torisan: wait until all has been sent out + if (ramses_lcd_type == 2) { + int i; + for (i=0; i<33; i++) + udelay(1500); + } + + /* 2. disable the PWM */ + set_GPIO_mode(GPIO16_PWM0 | GPIO_OUT); + set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); + CKEN &= ~(CKEN0_PWM0 | CKEN1_PWM1); + + /* 3. SIGNAL */ + + /* 4. VCC */ + RAMSES_CPLD_LCD &= ~RAMSES_LCD_VCC; +} + + +void ramses_lcd_backlight_on(void) +{ + int i; + + //printk("--> ramses_lcd_backlight_on\n"); + if ((ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) != 0) + return; + //printk(" state: %d\n", ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT); + + set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); + + /* 4. nDISPOFF */ + RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; + + /* Backlight can be turned on at any time */ + RAMSES_LCD_BLIGHT_ON(); + + for (i=0; i<33; i++) + udelay(1500); + set_GPIO_mode(GPIO16_PWM0_MD); + set_GPIO_mode(GPIO17_PWM1_MD); +} + + +void ramses_lcd_backlight_off(void) +{ + int i; + + //printk("--> ramses_lcd_backlight_off\n"); + if ((ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) == 0) + return; + //printk(" state: %d\n", ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT); + + set_GPIO_mode(GPIO17_PWM1 | GPIO_OUT); + for (i=0; i<100; i++) + udelay(1500); + + /* Backlight can be turned off at any time */ + RAMSES_LCD_BLIGHT_OFF(); + udelay(1500); + + /* 1. nDISPOFF */ + if (ramses_lcd_type != 2) + RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; + + //set_GPIO_mode(GPIO16_PWM0 | GPIO_IN); + //set_GPIO_mode(GPIO17_PWM1 | GPIO_IN); +} + + +void ramses_lcd_set_brightness(int b) +{ + if (b > 255) b = 255; + if (b < 0) b = 0; + PWM_PWDUTY1 = 510-(b<<1); +} + + +int ramses_lcd_get_brightness(void) +{ + return 255-(PWM_PWDUTY1 >> 1); +} + + +void ramses_lcd_set_contrast(int c) +{ + if (c > 255) c = 255; + if (c < 0) c = 0; + PWM_PWDUTY0 = 542-c; +} + + +int ramses_lcd_get_contrast(void) +{ + return 542-PWM_PWDUTY0; +} + + +void ramses_lcd_set_intensity(int i) +{ + //printk("--> ramses_lcd_set_intensity(%d)\n", i); + if (i) { + ramses_lcd_backlight_on(); + } else { + ramses_lcd_backlight_off(); + } +} + + +int ramses_lcd_get_intensity(void) +{ + return ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT; +} + + + + + +/******************************************************************/ +/* HDQ communication */ +/******************************************************************/ + +#define GPIO_HDQ 2 + +#define HDQ_LO GPCR(GPIO_HDQ) = GPIO_bit(GPIO_HDQ) +#define HDQ_HI GPSR(GPIO_HDQ) = GPIO_bit(GPIO_HDQ) +#define HDQ_GET (GPLR(GPIO_HDQ) & (1 << GPIO_HDQ)) + +#define MAXLOOPS 800 +#define MAXTRIES 3 + + +static void hdq_break(void) +{ + HDQ_LO; + set_GPIO_mode(GPIO_HDQ | GPIO_OUT); + udelay(220); + HDQ_HI; + udelay(50); +} + + +/** + * Send data on the 1-bit wire. + * + * LSB first. Depending on the bit, do the low phase short or + * small. The used timings in usec's are made so that our send + * stuff has exactly the timing that the BQ2050 battery sends + * us back. +*/ +static void hdq_put_data(unsigned char cmd) +{ + unsigned char mask = 1; + + HDQ_HI; + set_GPIO_mode(GPIO_HDQ | GPIO_OUT); + + while (1) { + HDQ_LO; + udelay(cmd & mask ? 37 : 115); + HDQ_HI; + udelay(cmd & mask ? 163 : 85); + if (mask == 0x80) break; + mask = mask << 1; + } + set_GPIO_mode(GPIO_HDQ | GPIO_IN); +} + + +/** + * Receive data on the 1-bit wire. + * + * Little state-machine with two states (yuck) that measures the time + * in the low-state. If it exceeds some value, then the bit was a 0, + * otherwise it's a 1. +*/ +static int hdq_get_data(void) +{ + enum { ST_WAITLOW, ST_LOW }; + + int i; + int lastlow = 0; + int state = ST_WAITLOW; + unsigned char mask = 1; + unsigned char d = 0; + + for (i=0; i %08x\n", old_shadow, ramses_control_shadow); + + RAMSES_CPLD_PERIPH_PWR &= ~PER_PWR_EN; + break; + + case PM_RESUME: + RAMSES_CPLD_PERIPH_PWR |= PER_PWR_EN; + ramses_control_shadow = old_shadow; + PWM_CTRL0 = old_ctrl0; + PWM_CTRL1 = old_ctrl1; + PWM_PERVAL0 = old_perval0; + PWM_PERVAL1 = old_perval1; + PWM_PWDUTY0 = old_duty0; + PWM_PWDUTY1 = old_duty1; + + break; + } + return 0; +} + +#endif + + + +static void pf_interrupt(int irq, void *dummy, struct pt_regs *fp) +{ +#ifdef PFI_LED + RAMSES_LED_BLUE_ON(); + RAMSES_LED_ORANGE_ON(); +#endif +#ifdef PFI_TURNOFF + // Make sure we can't be turned on by setting low onto the CPLD's columns + RAMSES_CPLD_KB_COL_LOW = 0; + RAMSES_CPLD_KB_COL_HIGH = 0; + + // turn power off + RAMSES_POWER_OFF(); + + // wait until VCC fades + while (1) { } +#endif +} + + +void ramses_shut_off(void) +{ + // Make sure we can't be turned on by setting low onto the CPLD's columns + RAMSES_CPLD_KB_COL_LOW = 0; + RAMSES_CPLD_KB_COL_HIGH = 0; + //printk("--> ramses_shut_off calling ramses_lcd_backlight_off\n"); + ramses_lcd_backlight_off(); + //printk("--> ramses_shut_off calling ramses_lcd_power_off\n"); + ramses_lcd_power_off(); + + // turn power off + RAMSES_POWER_OFF(); + + // wait until voltage fades + while (1) {} +} + + + + +#ifdef CONFIG_APM +static int ramses_get_power_status(u_char *ac_line_status, + u_char *battery_status, + u_char *battery_flag, + u_char *battery_percentage, + u_short *battery_life) +{ +#ifdef USE_UCB + int adc3; + struct ucb1x00 *ucb = ucb1x00_get(); + + ucb1x00_adc_enable(ucb); + adc3 = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD3, 0); + ucb1x00_adc_disable(ucb); + + /* + * when charged: 0..430 + * when discharging: 0..340 + */ + +#define CHG_LO 165 +#define CHG_HI 420 +#define OFF_LO 60 +#define OFF_HI 350 + if ((RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0) { + // in Docking-Station + if (adc3 > CHG_HI) adc3 = CHG_HI; + if (adc3 < CHG_LO) adc3 = CHG_LO; + adc3 -= CHG_LO; + *battery_percentage = adc3 * 100 / (CHG_HI-CHG_LO); + *ac_line_status = 0x01; + } else { + // offline + if (adc3 > OFF_HI) adc3 = OFF_HI; + if (adc3 < OFF_LO) adc3 = OFF_LO; + adc3 -= OFF_LO; + *battery_percentage = adc3 * 100 / (OFF_HI-OFF_LO); + *ac_line_status = 0x00; + } + + if (*battery_percentage > 100) + *battery_percentage = 100; + + if (*ac_line_status) { + *battery_status = 3; // charging + *battery_flag = 1<<3; + } else + if (*battery_percentage >= 30) { + *battery_status = 0; // high + *battery_flag = 1<<0; + } else + if (*battery_percentage >= 15) { + *battery_status = 1; // low + *battery_flag = 1<<1; + } else { + *battery_status = 2; // critical + *battery_flag = 1<<2; + } + + // assume 5.5 hours operation, 330 minutes + *battery_life = (*battery_percentage * 330 / 100) | 0x8000; +#endif + + +#ifdef USE_HDQ +#error HDQ + // SAE is something like mAh * 10 + int sae, sael; + + sael = ramses_hdq_get_reg(HDQ_SAEL); + sae = ramses_hdq_get_reg(HDQ_SAEH); + + if (sae == -1 || sael == -1) { + //printk("ramses: could not read HDQ_SAE\n"); + *ac_line_status = 0xff; + *battery_status = 0xff; + *battery_flag = 0xff; + *battery_percentage = 0xff; + *battery_life = -1; + return 0; + } + + sae = (sae << 16) + sael; + if (sae > 27000) { + printk("ramses: capped HDQ_SAE from %d to 27000\n", sae); + sae = 27000; + } + + if (sae < 4000) { + *battery_status = 2; // critical + *battery_flag = 1<<2; + } else + if (sae < 10000) { + *battery_status = 1; // low + *battery_flag = 1<<1; + } else { + *battery_status = 0; // high + *battery_flag = 1<<0; + } + + if ((RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0) { + *battery_status = 3; // charging + *battery_flag = 1<<3; + *ac_line_status = 0x01; // online + } else { + *ac_line_status = 0x00; // offline + } + + *battery_percentage = sae / 270; + *battery_life = (sae / 56) | 0x8000; +#endif + + +#if !defined(USE_UCB) && !defined(USE_HDQ) +#error NONE + *ac_line_status = 0xff; + *battery_status = 0xff; + *battery_flag = 0xff; + *battery_percentage = 0xff; + *battery_life = -1; +#endif + + return 0; +} +#endif + + + + +/******************************************************************/ +/* Initialisation */ +/******************************************************************/ + +static struct map_desc ramses_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { RAMSES_IDE_BASE, RAMSES_IDE_PHYS, RAMSES_IDE_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + { RAMSES_ETH_BASE, RAMSES_ETH_PHYS, RAMSES_ETH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + { RAMSES_COREVOLT_BASE, RAMSES_COREVOLT_PHYS, RAMSES_COREVOLT_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + { RAMSES_CPLD_BASE, RAMSES_CPLD_PHYS, RAMSES_CPLD_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + { RAMSES_CONTROL_BASE, RAMSES_CONTROL_PHYS, RAMSES_CONTROL_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + LAST_DESC +}; + + + + + +/* + * Uncompressing Linux...... done, booting the kernel. + * Linux version 2.4.19-rmk4-pxa1-mn ... + * CPU: Intel XScale-PXA250 revision 4 + * Machine: Ramses + * fixup_ramses() + */ + +static void __init +fixup_ramses(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + SET_BANK (0, 0xa0000000,128*1024*1024); + mi->nr_banks = 1; +} + + + + +/* + * fixup_ramses() + * ramses_map_io() + */ + +static void __init ramses_map_io(void) +{ + u16 smc_eeprom_read(long ioaddr, u16 location); + + pxa_map_io(); + iotable_init(ramses_io_desc); + +#ifdef IPAQ + // set power managament stuff + PGSR0 = GPSRx_SleepValue; + PGSR1 = GPSRy_SleepValue; + PGSR2 = GPSRz_SleepValue; + PWER = PWER_GPIO0 | PWER_RTC; + PFER = PWER_GPIO0 | PWER_RTC; + PRER = 0; +#endif + + ramses_lcd_type = smc_eeprom_read(RAMSES_ETH_BASE+0x300, RAMSES_LCD_TYPE_OFFSET); + // here we could make a special case about ramses_lcd_type == 0xffff + ramses_flags |= (ramses_lcd_type & RAMSES_FLAGS_LCD_FBTURN); +} + + + + +/* + * ramses_map_io() + * Memory clock: 99.53MHz (*27) + * Run Mode clock: 199.07MHz (*2) + * Turbo Mode clock: 398.13MHz (*2.0, active) * On node 0 totalpages: 16384 + * zone(0): 32768 pages. + * zone(1): 0 pages. + * zone(2): 0 pages. + * Kernel command line: root=/dev/nfsroot ... + * ramses_init_irq() + */ + +static void __init ramses_init_irq(void) +{ + set_GPIO_IRQ_edge(21, GPIO_FALLING_EDGE); // UCB 1400 + + RAMSES_SCANNER_OFF(); + RAMSES_GSM_OFF(); + RAMSES_GSM_RESET_OFF(); + RAMSES_GSM_BOOT_OFF(); + pxa_init_irq(); +} + + + + +/* + * ramses_init_irq() + * Console: colour dummy device 80x30 + * serial_console_init + * serial_console_setup + * Calibrating delay loop... 397.31 BogoMIPS + * Memory: 128MB = 128MB total + * Memory: 127872KB available (1355K code, 272K data, 112K init) + * Dentry cache hash table entries: 16384 (order: 5, 131072 bytes) + * Inode cache hash table entries: 8192 (order: 4, 65536 bytes) + * Mount-cache hash table entries: 2048 (order: 2, 16384 bytes) + * Buffer-cache hash table entries: 8192 (order: 3, 32768 bytes) + * Page-cache hash table entries: 32768 (order: 5, 131072 bytes) + * POSIX conformance testing by UNIFIX + * Linux NET4.0 for Linux 2.4 + * Based upon Swansea University Computer Society NET3.039 + * Initializing RT netlink socket + * ramses_init() + */ + +static int __init ramses_init(void) +{ + unsigned int irq_gpio_pin; + + // Set IRQ for Touchpanel (via UCB 1400) + irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ); + set_GPIO_IRQ_edge(irq_gpio_pin, TOUCH_PANEL_IRQ_EDGE); + + // Set IRQ for Power Fail Interrupt + set_GPIO_IRQ_edge(1, GPIO_FALLING_EDGE); + request_irq(IRQ_GPIO(1), pf_interrupt, 0, "PWR FAIL", NULL); + + // Setup IRQ edge for Ethernet + //set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(27), GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(ETHERNET_IRQ), ETHERNET_IRQ_EDGE); + + // Configure PWMs for LCD + PWM_CTRL0 = 0; + PWM_PERVAL0 = 512; + PWM_PWDUTY0 = 440; + PWM_CTRL1 = 0; + PWM_PERVAL1 = 512; + PWM_PWDUTY1 = 450; + + // Request Memory Regions of core components + request_mem_region(RAMSES_CONTROL_BASE, RAMSES_CONTROL_SIZE, "Ramses Control"); + request_mem_region(RAMSES_CPLD_BASE, RAMSES_CPLD_SIZE, "Ramses CPLD"); + request_mem_region(RAMSES_COREVOLT_BASE, RAMSES_COREVOLT_SIZE, "Ramses Corevolt"); + +#ifdef CONFIG_PM +#ifdef PM_DEBUG + pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ramses_pm_callback, "ramses"); +#else + pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ramses_pm_callback); +#endif + //TODO PWER (PXA255: 3-25) + //TODO PRER (PXA255: 3-26) + //TODO PFER (PXA255: 3-27) + //TODO PSSR (PXA255: 3-29) + //TODO PGSR0..PGSR2 (PXA255: 3-32) +#endif + +#ifdef CONFIG_APM + apm_register_get_power_status(ramses_get_power_status); +#endif + + PCFR = PCFR_OPDE | PCFR_FS | PCFR_FP; // PXA255: 3-24 + + pm_power_off = ramses_shut_off; + + return 0; +} + +__initcall(ramses_init); + + + +/* + * ramses_init() + * Using PXA255 frequency points. + * Registering CPU frequency change support. + * CPU clock: 398.131 MHz (99.000-400.000 MHz) + * Starting kswapd + * devfs: v1.12a (20020514) Richard Gooch (rgooch@atnf.csiro.au) + * devfs: boot_options: 0x1 + * pty: 256 Unix98 ptys configured + * pxa & ti16c754b serial driver + * tts/0 at irq 14 is a PXA UART + * tts/1 at irq 13 is a PXA UART + * tts/2 at irq 12 is a PXA UART + * tts/3 at irq 45 is a TI16750 + * tts/4 at irq 46 is a TI16750 + * tts/5 at irq 47 is a TI16750 + * tts/6 at irq 48 is a TI16750 + * LAN91C111: You shouldn't use auto-probing with insmod! + * SMSC LAN91C111 Driver (v2.2), (Linux Kernel 2.4 + Support for Odd Byte) ... + * eth0: SMC91C11xFD(rev:1) at 0xf0100300 IRQ:26 MEMSIZE ... + * ac97_codec: AC97 Audio codec, id: 0x5053:0x4304 (Philips UCB1400) + * NET4: Linux TCP/IP 1.0 for NET4.0 + * IP Protocols: ICMP, UDP, TCP + * IP: routing cache hash table of 512 buckets, 4Kbytes + * TCP: Hash tables configured (established 4096 bind 4096) + * IP-Config: ... + * NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. + * NetWinder Floating Point Emulator V0.95 (c) 1998-1999 Rebel.com + * Looking up port of RPC 100003/2 on 192.168.233.66 + * Looking up port of RPC 100005/1 on 192.168.233.66 + * VFS: Mounted root (nfs filesystem). + * Mounted devfs on /dev + * Freeing init memory: 68K + * INIT: version 2.84 booting + */ + + + +MACHINE_START(RAMSES, "Ramses") + MAINTAINER("M&N Logistik-Lösungen Online GmbH") + BOOT_MEM(0xa0000000, 0x40000000, 0xfc000000) + BOOT_PARAMS(0xa0000100) + FIXUP(fixup_ramses) + MAPIO(ramses_map_io) + INITIRQ(ramses_init_irq) +MACHINE_END + +EXPORT_SYMBOL(ramses_lcd_type); +EXPORT_SYMBOL(ramses_lcd_power_on); +EXPORT_SYMBOL(ramses_lcd_power_off); +EXPORT_SYMBOL(ramses_lcd_backlight_on); +EXPORT_SYMBOL(ramses_lcd_backlight_off); +EXPORT_SYMBOL(ramses_lcd_set_intensity); +EXPORT_SYMBOL(ramses_lcd_set_brightness); +EXPORT_SYMBOL(ramses_lcd_set_contrast); +EXPORT_SYMBOL(ramses_lcd_get_intensity); +EXPORT_SYMBOL(ramses_lcd_get_brightness); +EXPORT_SYMBOL(ramses_lcd_get_contrast); +EXPORT_SYMBOL(ramses_hdq_get_reg); +EXPORT_SYMBOL(ramses_set_corevolt); +EXPORT_SYMBOL(ramses_corevolt_shadow); --- linux-2.4.21/arch/arm/mach-pxa/usb-char.c~pxa-usb +++ linux-2.4.21/arch/arm/mach-pxa/usb-char.c @@ -211,7 +211,6 @@ static void twiddle_descriptors( void ) { desc_t * pDesc = pxa_usb_get_descriptor_ptr(); - string_desc_t * pString; pDesc->b.ep1.wMaxPacketSize = make_word_c( RX_PACKET_SIZE ); pDesc->b.ep1.bmAttributes = USB_EP_BULK; @@ -220,6 +219,7 @@ if ( machine_is_extenex1() ) { #ifdef CONFIG_SA1100_EXTENEX1 + string_desc_t * pString; pDesc->dev.idVendor = make_word_c( 0xC9F ); pDesc->dev.idProduct = 1; pDesc->dev.bcdDevice = make_word_c( 0x0001 ); --- linux-2.4.21/arch/arm/mach-pxa/usb-eth.c~pxa-usbeth +++ linux-2.4.21/arch/arm/mach-pxa/usb-eth.c @@ -52,6 +52,7 @@ #define ETHERNET_VENDOR_ID 0x49f #define ETHERNET_PRODUCT_ID 0x505A +#define ETHERNET_DEVICE_ID 0x0200 #define MAX_PACKET 32768 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -329,6 +330,7 @@ pd->b.ep2.wMaxPacketSize = make_word( usb_wsize ); pd->dev.idVendor = ETHERNET_VENDOR_ID; pd->dev.idProduct = ETHERNET_PRODUCT_ID; + pd->dev.bcdDevice = ETHERNET_DEVICE_ID; pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" ); if ( pstr ) { pxa_usb_set_string_descriptor( 1, pstr ); --- linux-2.4.21/drivers/bluetooth/Config.in~bluetooth +++ linux-2.4.21/drivers/bluetooth/Config.in @@ -7,8 +7,7 @@ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then - bool ' SCO (voice) support' CONFIG_BLUEZ_USB_SCO - bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET + bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO fi dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ @@ -18,6 +17,8 @@ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP fi +dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB + dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ --- linux-2.4.21/drivers/bluetooth/Makefile~bluetooth +++ linux-2.4.21/drivers/bluetooth/Makefile @@ -14,6 +14,8 @@ uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o +obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o + obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o --- /dev/null +++ linux-2.4.21/drivers/bluetooth/Makefile.lib @@ -0,0 +1,2 @@ +obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o +obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o --- /dev/null +++ linux-2.4.21/drivers/bluetooth/bfusb.c @@ -0,0 +1,782 @@ +/* + * + * AVM BlueFRITZ! USB driver + * + * Copyright (C) 2003 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "1.1" + +static struct usb_device_id bfusb_table[] = { + /* AVM BlueFRITZ! USB */ + { USB_DEVICE(0x057c, 0x2200) }, + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, bfusb_table); + + +#define BFUSB_MAX_BLOCK_SIZE 256 + +#define BFUSB_BLOCK_TIMEOUT (HZ * 3) + +#define BFUSB_TX_PROCESS 1 +#define BFUSB_TX_WAKEUP 2 + +#define BFUSB_MAX_BULK_TX 1 +#define BFUSB_MAX_BULK_RX 1 + +struct bfusb { + struct hci_dev hdev; + + unsigned long state; + + struct usb_device *udev; + + unsigned int bulk_in_ep; + unsigned int bulk_out_ep; + unsigned int bulk_pkt_size; + + rwlock_t lock; + + struct sk_buff_head transmit_q; + + struct sk_buff *reassembly; + + atomic_t pending_tx; + struct sk_buff_head pending_q; + struct sk_buff_head completed_q; +}; + +struct bfusb_scb { + struct urb *urb; +}; + +static void bfusb_tx_complete(struct urb *urb); +static void bfusb_rx_complete(struct urb *urb); + +static struct urb *bfusb_get_completed(struct bfusb *bfusb) +{ + struct sk_buff *skb; + struct urb *urb = NULL; + + BT_DBG("bfusb %p", bfusb); + + skb = skb_dequeue(&bfusb->completed_q); + if (skb) { + urb = ((struct bfusb_scb *) skb->cb)->urb; + kfree_skb(skb); + } + + return urb; +} + +static inline void bfusb_unlink_urbs(struct bfusb *bfusb) +{ + struct sk_buff *skb; + struct urb *urb; + + BT_DBG("bfusb %p", bfusb); + + while ((skb = skb_dequeue(&bfusb->pending_q))) { + urb = ((struct bfusb_scb *) skb->cb)->urb; + usb_unlink_urb(urb); + skb_queue_tail(&bfusb->completed_q, skb); + } + + while ((urb = bfusb_get_completed(bfusb))) + usb_free_urb(urb); +} + + +static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb) +{ + struct bfusb_scb *scb = (void *) skb->cb; + struct urb *urb = bfusb_get_completed(bfusb); + int err, pipe; + + BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len); + + if (!urb && !(urb = usb_alloc_urb(0))) + return -ENOMEM; + + pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); + + FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len, + bfusb_tx_complete, skb); + + urb->transfer_flags = USB_QUEUE_BULK; + + scb->urb = urb; + + skb_queue_tail(&bfusb->pending_q, skb); + + err = usb_submit_urb(urb); + if (err) { + BT_ERR("%s bulk tx submit failed urb %p err %d", + bfusb->hdev.name, urb, err); + skb_unlink(skb); + usb_free_urb(urb); + } else + atomic_inc(&bfusb->pending_tx); + + return err; +} + +static void bfusb_tx_wakeup(struct bfusb *bfusb) +{ + struct sk_buff *skb; + + BT_DBG("bfusb %p", bfusb); + + if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) { + set_bit(BFUSB_TX_WAKEUP, &bfusb->state); + return; + } + + do { + clear_bit(BFUSB_TX_WAKEUP, &bfusb->state); + + while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) && + (skb = skb_dequeue(&bfusb->transmit_q))) { + if (bfusb_send_bulk(bfusb, skb) < 0) { + skb_queue_head(&bfusb->transmit_q, skb); + break; + } + } + + } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state)); + + clear_bit(BFUSB_TX_PROCESS, &bfusb->state); +} + +static void bfusb_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct bfusb *bfusb = (struct bfusb *) skb->dev; + + BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); + + atomic_dec(&bfusb->pending_tx); + + if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) + return; + + if (!urb->status) + bfusb->hdev.stat.byte_tx += skb->len; + else + bfusb->hdev.stat.err_tx++; + + read_lock(&bfusb->lock); + + skb_unlink(skb); + skb_queue_tail(&bfusb->completed_q, skb); + + bfusb_tx_wakeup(bfusb); + + read_unlock(&bfusb->lock); +} + + +static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) +{ + struct bfusb_scb *scb; + struct sk_buff *skb; + int err, pipe, size = HCI_MAX_FRAME_SIZE + 32; + + BT_DBG("bfusb %p urb %p", bfusb, urb); + + if (!urb && !(urb = usb_alloc_urb(0))) + return -ENOMEM; + + if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { + usb_free_urb(urb); + return -ENOMEM; + } + + skb->dev = (void *) bfusb; + + scb = (struct bfusb_scb *) skb->cb; + scb->urb = urb; + + pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep); + + FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size, + bfusb_rx_complete, skb); + + urb->transfer_flags = USB_QUEUE_BULK; + + skb_queue_tail(&bfusb->pending_q, skb); + + err = usb_submit_urb(urb); + if (err) { + BT_ERR("%s bulk rx submit failed urb %p err %d", + bfusb->hdev.name, urb, err); + skb_unlink(skb); + kfree_skb(skb); + usb_free_urb(urb); + } + + return err; +} + +static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len) +{ + BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len); + + if (hdr & 0x10) { + BT_ERR("%s error in block", bfusb->hdev.name); + if (bfusb->reassembly) + kfree_skb(bfusb->reassembly); + bfusb->reassembly = NULL; + return -EIO; + } + + if (hdr & 0x04) { + struct sk_buff *skb; + unsigned char pkt_type; + int pkt_len = 0; + + if (bfusb->reassembly) { + BT_ERR("%s unexpected start block", bfusb->hdev.name); + kfree_skb(bfusb->reassembly); + bfusb->reassembly = NULL; + } + + if (len < 1) { + BT_ERR("%s no packet type found", bfusb->hdev.name); + return -EPROTO; + } + + pkt_type = *data++; len--; + + switch (pkt_type) { + case HCI_EVENT_PKT: + if (len >= HCI_EVENT_HDR_SIZE) { + hci_event_hdr *hdr = (hci_event_hdr *) data; + pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; + } else { + BT_ERR("%s event block is too short", bfusb->hdev.name); + return -EILSEQ; + } + break; + + case HCI_ACLDATA_PKT: + if (len >= HCI_ACL_HDR_SIZE) { + hci_acl_hdr *hdr = (hci_acl_hdr *) data; + pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen); + } else { + BT_ERR("%s data block is too short", bfusb->hdev.name); + return -EILSEQ; + } + break; + + case HCI_SCODATA_PKT: + if (len >= HCI_SCO_HDR_SIZE) { + hci_sco_hdr *hdr = (hci_sco_hdr *) data; + pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen; + } else { + BT_ERR("%s audio block is too short", bfusb->hdev.name); + return -EILSEQ; + } + break; + } + + skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC); + if (!skb) { + BT_ERR("%s no memory for the packet", bfusb->hdev.name); + return -ENOMEM; + } + + skb->dev = (void *) &bfusb->hdev; + skb->pkt_type = pkt_type; + + bfusb->reassembly = skb; + } else { + if (!bfusb->reassembly) { + BT_ERR("%s unexpected continuation block", bfusb->hdev.name); + return -EIO; + } + } + + if (len > 0) + memcpy(skb_put(bfusb->reassembly, len), data, len); + + if (hdr & 0x08) { + hci_recv_frame(bfusb->reassembly); + bfusb->reassembly = NULL; + } + + return 0; +} + +static void bfusb_rx_complete(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct bfusb *bfusb = (struct bfusb *) skb->dev; + unsigned char *buf = urb->transfer_buffer; + int count = urb->actual_length; + int err, hdr, len; + + BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); + + read_lock(&bfusb->lock); + + if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) + goto unlock; + + if (urb->status || !count) + goto resubmit; + + bfusb->hdev.stat.byte_rx += count; + + skb_put(skb, count); + + while (count) { + hdr = buf[0] | (buf[1] << 8); + + if (hdr & 0x4000) { + len = 0; + count -= 2; + buf += 2; + } else { + len = (buf[2] == 0) ? 256 : buf[2]; + count -= 3; + buf += 3; + } + + if (count < len) { + BT_ERR("%s block extends over URB buffer ranges", + bfusb->hdev.name); + } + + if ((hdr & 0xe1) == 0xc1) + bfusb_recv_block(bfusb, hdr, buf, len); + + count -= len; + buf += len; + } + + skb_unlink(skb); + kfree_skb(skb); + + bfusb_rx_submit(bfusb, urb); + + read_unlock(&bfusb->lock); + + return; + +resubmit: + urb->dev = bfusb->udev; + + err = usb_submit_urb(urb); + if (err) { + BT_ERR("%s bulk resubmit failed urb %p err %d", + bfusb->hdev.name, urb, err); + } + +unlock: + read_unlock(&bfusb->lock); +} + + +static int bfusb_open(struct hci_dev *hdev) +{ + struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + unsigned long flags; + int i, err; + + BT_DBG("hdev %p bfusb %p", hdev, bfusb); + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + MOD_INC_USE_COUNT; + + write_lock_irqsave(&bfusb->lock, flags); + + err = bfusb_rx_submit(bfusb, NULL); + if (!err) { + for (i = 1; i < BFUSB_MAX_BULK_RX; i++) + bfusb_rx_submit(bfusb, NULL); + } else { + clear_bit(HCI_RUNNING, &hdev->flags); + MOD_DEC_USE_COUNT; + } + + write_unlock_irqrestore(&bfusb->lock, flags); + + return err; +} + +static int bfusb_flush(struct hci_dev *hdev) +{ + struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + + BT_DBG("hdev %p bfusb %p", hdev, bfusb); + + skb_queue_purge(&bfusb->transmit_q); + + return 0; +} + +static int bfusb_close(struct hci_dev *hdev) +{ + struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + unsigned long flags; + + BT_DBG("hdev %p bfusb %p", hdev, bfusb); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + write_lock_irqsave(&bfusb->lock, flags); + + bfusb_unlink_urbs(bfusb); + bfusb_flush(hdev); + + write_unlock_irqrestore(&bfusb->lock, flags); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int bfusb_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct bfusb *bfusb; + struct sk_buff *nskb; + unsigned char buf[3]; + int sent = 0, size, count; + + BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len); + + if (!hdev) { + BT_ERR("Frame for unknown HCI device (hdev=NULL)"); + return -ENODEV; + } + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + bfusb = (struct bfusb *) hdev->driver_data; + + switch (skb->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + }; + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); + + count = skb->len; + + /* Max HCI frame size seems to be 1511 + 1 */ + if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for new packet"); + return -ENOMEM; + } + + nskb->dev = (void *) bfusb; + + while (count) { + size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE); + + buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0); + buf[1] = 0x00; + buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; + + memcpy(skb_put(nskb, 3), buf, 3); + memcpy(skb_put(nskb, size), skb->data + sent, size); + + sent += size; + count -= size; + } + + /* Don't send frame with multiple size of bulk max packet */ + if ((nskb->len % bfusb->bulk_pkt_size) == 0) { + buf[0] = 0xdd; + buf[1] = 0x00; + memcpy(skb_put(nskb, 2), buf, 2); + } + + read_lock(&bfusb->lock); + + skb_queue_tail(&bfusb->transmit_q, nskb); + bfusb_tx_wakeup(bfusb); + + read_unlock(&bfusb->lock); + + kfree_skb(skb); + + return 0; +} + +static void bfusb_destruct(struct hci_dev *hdev) +{ + struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; + + BT_DBG("hdev %p bfusb %p", hdev, bfusb); + + kfree(bfusb); +} + +static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + + +static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count) +{ + unsigned char *buf; + int err, pipe, len, size, sent = 0; + + BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count); + + BT_INFO("BlueFRITZ! USB loading firmware"); + + if (usb_set_configuration(bfusb->udev, 1) < 0) { + BT_ERR("Can't change to loading configuration"); + return -EBUSY; + } + + buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC); + if (!buf) { + BT_ERR("Can't allocate memory chunk for firmware"); + return -ENOMEM; + } + + pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); + + while (count) { + size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3); + + memcpy(buf, firmware + sent, size); + + err = usb_bulk_msg(bfusb->udev, pipe, buf, size, + &len, BFUSB_BLOCK_TIMEOUT); + + if (err || (len != size)) { + BT_ERR("Error in firmware loading"); + goto error; + } + + sent += size; + count -= size; + } + + if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0, + &len, BFUSB_BLOCK_TIMEOUT)) < 0) { + BT_ERR("Error in null packet request"); + goto error; + } + + if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) { + BT_ERR("Can't change to running configuration"); + goto error; + } + + BT_INFO("BlueFRITZ! USB device ready"); + + kfree(buf); + return 0; + +error: + kfree(buf); + + pipe = usb_sndctrlpipe(bfusb->udev, 0); + + usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, + 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT); + + return err; +} + +static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) +{ + const struct firmware *firmware; + char device[16]; + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *bulk_out_ep; + struct usb_endpoint_descriptor *bulk_in_ep; + struct hci_dev *hdev; + struct bfusb *bfusb; + + BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id); + + /* Check number of endpoints */ + iface = &udev->actconfig->interface[0]; + iface_desc = &iface->altsetting[0]; + + if (iface_desc->bNumEndpoints < 2) + return NULL; + + bulk_out_ep = &iface_desc->endpoint[0]; + bulk_in_ep = &iface_desc->endpoint[1]; + + if (!bulk_out_ep || !bulk_in_ep) { + BT_ERR("Bulk endpoints not found"); + goto done; + } + + /* Initialize control structure and load firmware */ + if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) { + BT_ERR("Can't allocate memory for control structure"); + goto done; + } + + memset(bfusb, 0, sizeof(struct bfusb)); + + bfusb->udev = udev; + bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress; + bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress; + bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize; + + bfusb->lock = RW_LOCK_UNLOCKED; + + bfusb->reassembly = NULL; + + skb_queue_head_init(&bfusb->transmit_q); + skb_queue_head_init(&bfusb->pending_q); + skb_queue_head_init(&bfusb->completed_q); + + snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum); + + if (request_firmware(&firmware, "bfubase.frm", device) < 0) { + BT_ERR("Firmware request failed"); + goto error; + } + + if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) { + BT_ERR("Firmware loading failed"); + goto release; + } + + release_firmware(firmware); + + /* Initialize and register HCI device */ + hdev = &bfusb->hdev; + + hdev->type = HCI_USB; + hdev->driver_data = bfusb; + + hdev->open = bfusb_open; + hdev->close = bfusb_close; + hdev->flush = bfusb_flush; + hdev->send = bfusb_send_frame; + hdev->destruct = bfusb_destruct; + hdev->ioctl = bfusb_ioctl; + + if (hci_register_dev(hdev) < 0) { + BT_ERR("Can't register HCI device"); + goto error; + } + + return bfusb; + +release: + release_firmware(firmware); + +error: + kfree(bfusb); + +done: + return NULL; +} + +static void bfusb_disconnect(struct usb_device *udev, void *ptr) +{ + struct bfusb *bfusb = (struct bfusb *) ptr; + struct hci_dev *hdev = &bfusb->hdev; + + BT_DBG("udev %p ptr %p", udev, ptr); + + if (!hdev) + return; + + bfusb_close(hdev); + + if (hci_unregister_dev(hdev) < 0) + BT_ERR("Can't unregister HCI device %s", hdev->name); +} + +static struct usb_driver bfusb_driver = { + name: "bfusb", + probe: bfusb_probe, + disconnect: bfusb_disconnect, + id_table: bfusb_table, +}; + +static int __init bfusb_init(void) +{ + int err; + + BT_INFO("BlueFRITZ! USB driver ver %s", VERSION); + BT_INFO("Copyright (C) 2003 Marcel Holtmann "); + + if ((err = usb_register(&bfusb_driver)) < 0) + BT_ERR("Failed to register BlueFRITZ! USB driver"); + + return err; +} + +static void __exit bfusb_cleanup(void) +{ + usb_deregister(&bfusb_driver); +} + +module_init(bfusb_init); +module_exit(bfusb_cleanup); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/bluetooth/bluecard_cs.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/bluecard_cs.c @@ -803,6 +803,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + bluecard_hci_close(hdev); clear_bit(CARD_READY, &(info->hw_state)); --- linux-2.4.21/drivers/bluetooth/bt3c_cs.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/bt3c_cs.c @@ -24,8 +24,6 @@ #include #include -#define __KERNEL_SYSCALLS__ - #include #include #include @@ -48,6 +46,8 @@ #include #include +#include + #include #include #include @@ -485,78 +485,101 @@ -/* ======================== User mode firmware loader ======================== */ +/* ======================== Card services HCI interaction ======================== */ -#define FW_LOADER "/sbin/bluefw" -static int errno; +static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count) +{ + char *ptr = (char *) firmware; + char b[9]; + unsigned int iobase, size, addr, fcs, tmp; + int i, err = 0; + iobase = info->link.io.BasePort1; -static int bt3c_fw_loader_exec(void *dev) -{ - char *argv[] = { FW_LOADER, "pccard", dev, NULL }; - char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - int err; + /* Reset */ - err = exec_usermodehelper(FW_LOADER, argv, envp); - if (err) - printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev); + bt3c_io_write(iobase, 0x8040, 0x0404); + bt3c_io_write(iobase, 0x8040, 0x0400); - return err; -} + udelay(1); + bt3c_io_write(iobase, 0x8040, 0x0404); -static int bt3c_firmware_load(bt3c_info_t *info) -{ - sigset_t tmpsig; - char dev[16]; - pid_t pid; - int result; + udelay(17); - /* Check if root fs is mounted */ - if (!current->fs->root) { - printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n"); - return -EPERM; - } + /* Load */ - sprintf(dev, "%04x", info->link.io.BasePort1); + while (count) { + if (ptr[0] != 'S') { + printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n"); + err = -EFAULT; + goto error; + } - pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0); - if (pid < 0) { - printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid); - return pid; - } + memset(b, 0, sizeof(b)); + memcpy(b, ptr + 2, 2); + size = simple_strtol(b, NULL, 16); - /* Block signals, everything but SIGKILL/SIGSTOP */ - spin_lock_irq(¤t->sigmask_lock); - tmpsig = current->blocked; - siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + memset(b, 0, sizeof(b)); + memcpy(b, ptr + 4, 8); + addr = simple_strtol(b, NULL, 16); - result = waitpid(pid, NULL, __WCLONE); + memset(b, 0, sizeof(b)); + memcpy(b, ptr + (size * 2) + 2, 2); + fcs = simple_strtol(b, NULL, 16); - /* Allow signals again */ - spin_lock_irq(¤t->sigmask_lock); - current->blocked = tmpsig; - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + memset(b, 0, sizeof(b)); + for (tmp = 0, i = 0; i < size; i++) { + memcpy(b, ptr + (i * 2) + 2, 2); + tmp += simple_strtol(b, NULL, 16); + } - if (result != pid) { - printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result); - return -result; + if (((tmp + fcs) & 0xff) != 0xff) { + printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n"); + err = -EILSEQ; + goto error; + } + + if (ptr[1] == '3') { + bt3c_address(iobase, addr); + + memset(b, 0, sizeof(b)); + for (i = 0; i < (size - 4) / 2; i++) { + memcpy(b, ptr + (i * 4) + 12, 4); + tmp = simple_strtol(b, NULL, 16); + bt3c_put(iobase, tmp); + } + } + + ptr += (size * 2) + 6; + count -= (size * 2) + 6; } - return 0; -} + udelay(17); + /* Boot */ + bt3c_address(iobase, 0x3000); + outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL); -/* ======================== Card services HCI interaction ======================== */ +error: + udelay(17); + + /* Clear */ + + bt3c_io_write(iobase, 0x7006, 0x0000); + bt3c_io_write(iobase, 0x7005, 0x0000); + bt3c_io_write(iobase, 0x7001, 0x0000); + + return err; +} int bt3c_open(bt3c_info_t *info) { + const struct firmware *firmware; + char device[16]; struct hci_dev *hdev; int err; @@ -570,8 +593,22 @@ /* Load firmware */ - if ((err = bt3c_firmware_load(info)) < 0) + snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1); + + err = request_firmware(&firmware, "BT3CPCC.bin", device); + if (err < 0) { + printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n"); return err; + } + + err = bt3c_load_firmware(info, firmware->data, firmware->size); + + release_firmware(firmware); + + if (err < 0) { + printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n"); + return err; + } /* Timeout before it is safe to send the first HCI packet */ @@ -606,6 +643,9 @@ { struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + bt3c_hci_close(hdev); if (hci_unregister_dev(hdev) < 0) --- linux-2.4.21/drivers/bluetooth/btuart_cs.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/btuart_cs.c @@ -556,6 +556,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + btuart_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); --- linux-2.4.21/drivers/bluetooth/dtl1_cs.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/dtl1_cs.c @@ -535,6 +535,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + dtl1_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); --- linux-2.4.21/drivers/bluetooth/hci_bcsp.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/hci_bcsp.c @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -635,7 +634,8 @@ struct sk_buff *skb; unsigned long flags; - BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen); + BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen); + spin_lock_irqsave(&bcsp->unack.lock, flags); while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) { --- linux-2.4.21/drivers/bluetooth/hci_ldisc.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/hci_ldisc.c @@ -33,7 +33,6 @@ #include #include -#include #include #include #include --- linux-2.4.21/drivers/bluetooth/hci_uart.h~bluetooth +++ linux-2.4.21/drivers/bluetooth/hci_uart.h @@ -35,11 +35,12 @@ #define HCIUARTGETPROTO _IOR('U', 201, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 3 +#define HCI_UART_MAX_PROTO 4 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 -#define HCI_UART_NCSP 2 +#define HCI_UART_3WIRE 2 +#define HCI_UART_H4DS 3 #ifdef __KERNEL__ struct hci_uart; --- linux-2.4.21/drivers/bluetooth/hci_usb.c~bluetooth +++ linux-2.4.21/drivers/bluetooth/hci_usb.c @@ -30,7 +30,7 @@ * * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ */ -#define VERSION "2.4" +#define VERSION "2.7" #include #include @@ -62,7 +62,7 @@ #define BT_DMP( A... ) #endif -#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET +#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET #undef USB_ZERO_PACKET #define USB_ZERO_PACKET 0 #endif @@ -73,20 +73,39 @@ /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) }, - /* Ericsson with non-standard id */ - { USB_DEVICE(0x0bdb, 0x1002) }, + /* AVM BlueFRITZ! USB v2.0 */ + { USB_DEVICE(0x057c, 0x3800) }, /* Bluetooth Ultraport Module from IBM */ { USB_DEVICE(0x04bf, 0x030a) }, + /* ALPS Modules with non-standard id */ + { USB_DEVICE(0x044e, 0x3001) }, + { USB_DEVICE(0x044e, 0x3002) }, + + /* Ericsson with non-standard id */ + { USB_DEVICE(0x0bdb, 0x1002) }, + { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, bluetooth_ids); -static struct usb_device_id ignore_ids[] = { +static struct usb_device_id blacklist_ids[] = { /* Broadcom BCM2033 without firmware */ - { USB_DEVICE(0x0a5c, 0x2033) }, + { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE }, + + /* Broadcom BCM2035 */ + { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET }, + + /* ISSC Bluetooth Adapter v3.1 */ + { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET }, + + /* Digianswer device */ + { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER }, + + /* RTX Telecom based adapter with buggy SCO support */ + { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC }, { } /* Terminating entry */ }; @@ -133,6 +152,7 @@ return _urb_dequeue(__completed_q(husb, type)); } +#ifdef CONFIG_BLUEZ_HCIUSB_SCO static void __fill_isoc_desc(struct urb *urb, int len, int mtu) { int offset = 0, i; @@ -152,6 +172,7 @@ } urb->number_of_packets = i; } +#endif static int hci_usb_intr_rx_submit(struct hci_usb *husb) { @@ -229,7 +250,7 @@ return err; } -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO static int hci_usb_isoc_rx_submit(struct hci_usb *husb) { struct _urb *_urb; @@ -300,8 +321,10 @@ for (i = 0; i < HCI_MAX_BULK_RX; i++) hci_usb_bulk_rx_submit(husb); -#ifdef CONFIG_BLUEZ_USB_SCO - hci_usb_isoc_rx_submit(husb); +#ifdef CONFIG_BLUEZ_HCIUSB_SCO + if (husb->isoc_iface) + for (i = 0; i < HCI_MAX_ISOC_RX; i++) + hci_usb_isoc_rx_submit(husb); #endif } else { clear_bit(HCI_RUNNING, &hdev->flags); @@ -426,7 +449,7 @@ } else dr = (void *) _urb->urb.setup_packet; - dr->bRequestType = HCI_CTRL_REQ; + dr->bRequestType = husb->ctrl_req; dr->bRequest = 0; dr->wIndex = 0; dr->wValue = 0; @@ -467,7 +490,7 @@ return __tx_submit(husb, _urb); } -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb) { struct _urb *_urb = __get_completed(husb, skb->pkt_type); @@ -518,10 +541,10 @@ skb_queue_head(q, skb); } -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO /* Process SCO queue */ q = __transmit_q(husb, HCI_SCODATA_PKT); - if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) && + if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX && (skb = skb_dequeue(q))) { if (hci_usb_send_isoc(husb, skb) < 0) skb_queue_head(q, skb); @@ -577,7 +600,7 @@ hdev->stat.acl_tx++; break; -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; @@ -627,7 +650,7 @@ } else return -EILSEQ; break; -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO case HCI_SCODATA_PKT: if (count >= HCI_SCO_HDR_SIZE) { hci_sco_hdr *h = data; @@ -638,7 +661,7 @@ #endif } BT_DBG("new packet len %d", len); - + skb = bluez_skb_alloc(len, GFP_ATOMIC); if (!skb) { BT_ERR("%s no memory for the packet", husb->hdev.name); @@ -683,16 +706,16 @@ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, _urb->type, urb->status, count, urb->transfer_flags); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return; - read_lock(&husb->completion_lock); + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto unlock; + if (urb->status || !count) goto resubmit; if (_urb->type == HCI_SCODATA_PKT) { -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO int i; for (i=0; i < urb->number_of_packets; i++) { BT_DBG("desc %d status %d offset %d len %d", i, @@ -724,6 +747,8 @@ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, _urb->type, err); } + +unlock: read_unlock(&husb->completion_lock); } @@ -786,8 +811,14 @@ iface = &udev->actconfig->interface[0]; - /* Check our black list */ - if (usb_match_id(udev, iface, ignore_ids)) + if (!id->driver_info) { + const struct usb_device_id *match; + match = usb_match_id(udev, iface, blacklist_ids); + if (match) + id = match; + } + + if (id->driver_info & HCI_IGNORE) return NULL; /* Check number of endpoints */ @@ -827,9 +858,9 @@ bulk_out_ep[i] = ep; break; -#ifdef CONFIG_BLUEZ_USB_SCO +#ifdef CONFIG_BLUEZ_HCIUSB_SCO case USB_ENDPOINT_XFER_ISOC: - if (ep->wMaxPacketSize < size) + if (ep->wMaxPacketSize < size || a > 2) break; size = ep->wMaxPacketSize; @@ -853,8 +884,8 @@ goto done; } -#ifdef CONFIG_BLUEZ_USB_SCO - if (!isoc_in_ep[1] || !isoc_out_ep[1]) { +#ifdef CONFIG_BLUEZ_HCIUSB_SCO + if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) { BT_DBG("Isoc endpoints not found"); isoc_iface = NULL; } @@ -872,7 +903,12 @@ husb->bulk_in_ep = bulk_in_ep[0]; husb->intr_in_ep = intr_in_ep[0]; -#ifdef CONFIG_BLUEZ_USB_SCO + if (id->driver_info & HCI_DIGIANSWER) + husb->ctrl_req = HCI_DIGI_REQ; + else + husb->ctrl_req = HCI_CTRL_REQ; + +#ifdef CONFIG_BLUEZ_HCIUSB_SCO if (isoc_iface) { BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts); if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) { @@ -906,6 +942,9 @@ hdev->send = hci_usb_send_frame; hdev->destruct = hci_usb_destruct; + if (id->driver_info & HCI_RESET) + set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); goto probe_error; @@ -968,6 +1007,6 @@ module_init(hci_usb_init); module_exit(hci_usb_cleanup); -MODULE_AUTHOR("Maxim Krasnyansky "); +MODULE_AUTHOR("Maxim Krasnyansky , Marcel Holtmann "); MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION); MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/bluetooth/hci_usb.h~bluetooth +++ linux-2.4.21/drivers/bluetooth/hci_usb.h @@ -35,12 +35,21 @@ #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */ #define HCI_CTRL_REQ 0x20 +#define HCI_DIGI_REQ 0x40 + +#define HCI_IGNORE 0x01 +#define HCI_RESET 0x02 +#define HCI_DIGIANSWER 0x04 +#define HCI_BROKEN_ISOC 0x08 #define HCI_MAX_IFACE_NUM 3 #define HCI_MAX_BULK_TX 4 #define HCI_MAX_BULK_RX 1 +#define HCI_MAX_ISOC_RX 2 +#define HCI_MAX_ISOC_TX 2 + #define HCI_MAX_ISOC_FRAMES 10 struct _urb_queue { @@ -119,6 +128,8 @@ struct usb_endpoint_descriptor *isoc_out_ep; struct usb_endpoint_descriptor *isoc_in_ep; + __u8 ctrl_req; + struct sk_buff_head transmit_q[4]; struct sk_buff *reassembly[4]; // Reassembly buffers --- linux-2.4.21/drivers/char/Config.in~i2c-ds1337 +++ linux-2.4.21/drivers/char/Config.in @@ -164,6 +164,7 @@ if [ "$CONFIG_I2C" != "n" ]; then dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C + dep_tristate ' DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C fi source drivers/l3/Config.in --- linux-2.4.21/drivers/char/Makefile~i2c-ds1337 +++ linux-2.4.21/drivers/char/Makefile @@ -21,10 +21,11 @@ # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. -export-objs := busmouse.o console.o keyboard.o sysrq.o \ +export-objs := vt.o busmouse.o console.o keyboard.o sysrq.o \ misc.o pty.o random.o selection.o serial.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o \ - au1000_gpio.o hp_psaux.o nvram.o scx200.o + au1000_gpio.o hp_psaux.o nvram.o scx200.o \ + input_keyb.o mod-subdirs := joystick ftape drm drm-4.0 pcmcia @@ -129,6 +130,11 @@ ifeq ($(CONFIG_SA1100_CERF_CPLD),y) KEYBD += cerf_keyb.o endif + ifeq ($(CONFIG_ARCH_RAMSES),y) + KEYMAP = german.o + KEYBD += input_keyb.o + obj-m += sysctl.o + endif ifeq ($(CONFIG_ARCH_FORTUNET),y) KEYMAP := defkeymap.o endif @@ -337,6 +343,7 @@ # I2C char devices obj-$(CONFIG_I2C_DS1307) += ds1307.o +obj-$(CONFIG_I2C_DS1337) += ds1337.o subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) @@ -373,3 +380,6 @@ qtronixmap.c: qtronixmap.map set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + +german.c: german.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ --- linux-2.4.21/drivers/char/console.c~keyb-module +++ linux-2.4.21/drivers/char/console.c @@ -150,7 +150,7 @@ static int con_open(struct tty_struct *, struct file *); static void vc_init(unsigned int console, unsigned int rows, unsigned int cols, int do_clear); -static void blank_screen(unsigned long dummy); +//static void blank_screen(unsigned long dummy); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); static void reset_terminal(int currcons, int do_clear); @@ -158,7 +158,7 @@ static void set_vesa_blanking(unsigned long arg); static void set_cursor(int currcons); static void hide_cursor(int currcons); -static void unblank_screen_t(unsigned long dummy); +//static void unblank_screen_t(unsigned long dummy); static void console_callback(void *ignored); static int printable; /* Is console ready for printing? */ @@ -167,7 +167,7 @@ int console_blanked; static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ -static int blankinterval = 10*60*HZ; +//static int blankinterval = 10*60*HZ; static int vesa_off_interval; static struct tq_struct console_callback_tq = { @@ -209,9 +209,9 @@ * Hook so that the power management routines can (un)blank * the console on our behalf. */ -int (*console_blank_hook)(int); +//int (*console_blank_hook)(int); -static struct timer_list console_timer; +//static struct timer_list console_timer; /* * Low-Level Functions @@ -543,7 +543,7 @@ static void set_cursor(int currcons) { - if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS) + if (!IS_FG || vcmode == KD_GRAPHICS) return; if (deccm) { if (currcons == sel_cons) @@ -1287,7 +1287,7 @@ update_attr(currcons); break; case 9: /* set blanking interval */ - blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; + //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; poke_blanked_console(); break; case 10: /* set bell frequency in Hz */ @@ -2575,11 +2575,11 @@ if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); - init_timer(&console_timer); - console_timer.function = blank_screen; - if (blankinterval) { - mod_timer(&console_timer, jiffies + blankinterval); - } + //init_timer(&console_timer); + //console_timer.function = blank_screen; + //if (blankinterval) { + // mod_timer(&console_timer, jiffies + blankinterval); + //} /* * kmalloc is not running yet - we use the bootmem allocator. @@ -2744,11 +2744,12 @@ */ static void vesa_powerdown_screen(unsigned long dummy) { - console_timer.function = unblank_screen_t; + //console_timer.function = unblank_screen_t; vesa_powerdown(); } +#if 0 static void timer_do_blank_screen(int entering_gfx, int from_timer_handler) { int currcons = fg_console; @@ -2797,12 +2798,14 @@ if (vesa_blank_mode) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } +#endif void do_blank_screen(int entering_gfx) { - timer_do_blank_screen(entering_gfx, 0); + //timer_do_blank_screen(entering_gfx, 0); } +#if 0 /* * This is a timer handler */ @@ -2810,12 +2813,14 @@ { unblank_screen(); } +#endif /* * Called by timer as well as from vt_console_driver */ void unblank_screen(void) { +#if 0 int currcons; if (!console_blanked) @@ -2842,6 +2847,7 @@ /* Low-level driver cannot restore -> do it ourselves */ update_screen(fg_console); set_cursor(fg_console); +#endif } /* @@ -2849,11 +2855,12 @@ */ static void blank_screen(unsigned long dummy) { - timer_do_blank_screen(0, 1); + //timer_do_blank_screen(0, 1); } void poke_blanked_console(void) { +#if 0 del_timer(&console_timer); if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; @@ -2863,6 +2870,7 @@ } else if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); } +#endif } /* @@ -3088,7 +3096,7 @@ unblank_screen(); break; case PM_SUSPEND: - do_blank_screen(0); + //do_blank_screen(0); break; } return 0; @@ -3106,7 +3114,8 @@ EXPORT_SYMBOL(video_scan_lines); EXPORT_SYMBOL(vc_resize); EXPORT_SYMBOL(fg_console); -EXPORT_SYMBOL(console_blank_hook); +//EXPORT_SYMBOL(console_blank_hook); +EXPORT_SYMBOL(console_driver); #ifdef CONFIG_VT EXPORT_SYMBOL(vt_cons); #endif --- /dev/null +++ linux-2.4.21/drivers/char/ds1337.c @@ -0,0 +1,545 @@ +/* + * ds1337.c + * + * Device driver for Dallas Semiconductor's Real Time Controller DS1337. + * + * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.pdf + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ds1337.h" + +//#define DEBUG 1 + +#if DEBUG +static unsigned int rtc_debug = DEBUG; +#else +#define rtc_debug 0 /* gcc will remove all the debug code for us */ +#endif + +static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR; +struct i2c_driver ds1337_driver; +struct i2c_client *ds1337_i2c_client = 0; +static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +static int ds1337_rtc_noop(struct inode *inode, struct file *file); + +static int ds1337_probe(struct i2c_adapter *adap); +static int ds1337_detach(struct i2c_client *client); +static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg); + + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_addr, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = ignore, +}; + +static struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .ioctl = ds1337_rtc_ioctl, + .open = ds1337_rtc_noop, + .release = ds1337_rtc_noop, +}; + +static struct miscdevice ds1337_rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + + +struct i2c_driver ds1337_driver = { + .name = "DS1337", + .id = I2C_DRIVERID_DS1337, + .flags = I2C_DF_NOTIFY, + .attach_adapter = ds1337_probe, + .detach_client = ds1337_detach, + .command = ds1337_command +}; + +#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */ + + +static int ds1337_readram(char *buf, int len) +{ + unsigned long flags; + unsigned char ad[1] = { 0 }; + int ret; + struct i2c_msg msgs[2] = { + {ds1337_i2c_client->addr, 0, 1, ad}, + {ds1337_i2c_client->addr, I2C_M_RD, len, buf} + }; + + spin_lock_irqsave(&ds1337_rtc_lock, flags); + ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2); + spin_unlock_irqrestore(&ds1337_rtc_lock, flags); + + return ret; +} + + +static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val) +{ + unsigned char buf[2]; + buf[0] = reg; + buf[1] = val; + i2c_master_send(c, (char *) buf, 2); +} + +static int ds1337_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct i2c_client *c; + unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 }; + struct i2c_msg msgs[2] = { + {addr, 0, 1, ad}, + {addr, I2C_M_RD, 1, buf} + }; + int ret; + + if (rtc_debug>1) + printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind); + + c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + strcpy(c->name, "DS1337"); + c->id = ds1337_driver.id; + c->flags = 0; + c->addr = addr; + c->adapter = adap; + c->driver = &ds1337_driver; + c->data = NULL; + + ret = i2c_transfer(c->adapter, msgs, 2); + + if (ret == 2) { + DAT(c) = buf[0]; + } else + printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret); + + ds1337_i2c_client = c; + + ds1337_readram(buf, DS1337_MEM_SIZE); + + // set 24 hour mode + ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24); + // INTCN sets INTB to alarm2 (disables SQW) + ds1337_setreg(c, 0x5, buf[5] & 0x7f); // clear century + ds1337_setreg(c, 0x7, 0x00); // clear Alarm 1 seconds + ds1337_setreg(c, 0x8, 0x00); // clear Alarm 1 minutes + ds1337_setreg(c, 0x9, 0x40); // clear Alarm 1 hours, 24 hour on + ds1337_setreg(c, 0xA, 0x00); // clear Alarm 1 date + ds1337_setreg(c, 0xB, 0x00); // clear Alarm 2 minutes + ds1337_setreg(c, 0xC, 0x40); // clear Alarm 2 hours, 24 hour on + ds1337_setreg(c, 0xD, 0x00); // clear Alarm 2 date + ds1337_setreg(c, 0xe, 4); // nEOSC enabled + ds1337_setreg(c, 0xf, 0); // clear OSF, A2F, A1F + + return i2c_attach_client(c); +} + + +static int ds1337_probe(struct i2c_adapter *adap) +{ + if (rtc_debug>1) + printk("%s()\n", __FUNCTION__); + + return i2c_probe(adap, &addr_data, ds1337_attach); +} + + +static int ds1337_detach(struct i2c_client *client) +{ + if (rtc_debug>1) + printk("%s()\n", __FUNCTION__); + + i2c_detach_client(client); + + return 0; +} + + +static void ds1337_convert_to_time(struct rtc_time *dt, char *buf) +{ + if (rtc_debug>1) + printk("%s()\n", __FUNCTION__); + + dt->tm_sec = BCD_TO_BIN(buf[0]); + dt->tm_min = BCD_TO_BIN(buf[1]); + dt->tm_hour = DS1337_HOURS_24(buf[2]); + + dt->tm_mday = BCD_TO_BIN(buf[4]); + /* dt->tm_mon is zero-based */ + dt->tm_mon = BCD_TO_BIN(buf[5]) - 1; + /* year is 1900 + dt->tm_year */ + dt->tm_year = BCD_TO_BIN(buf[6]) + 100; + + if (rtc_debug > 2) { + printk("ds1337_get_datetime: year = %d\n", dt->tm_year); + printk("ds1337_get_datetime: mon = %d\n", dt->tm_mon); + printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday); + printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour); + printk("ds1337_get_datetime: min = %d\n", dt->tm_min); + printk("ds1337_get_datetime: sec = %d\n", dt->tm_sec); + } +} + + +static int ds1337_get_datetime(struct i2c_client *client, + struct rtc_time *dt) +{ + unsigned char buf[7], addr[1] = { 0 }; + struct i2c_msg msgs[2] = { + {client->addr, 0, 1, addr}, + {client->addr, I2C_M_RD, 7, buf} + }; + int ret = -EIO; + + if (rtc_debug) + printk("%s()\n", __FUNCTION__); + + memset(buf, 0, sizeof(buf)); + + ret = i2c_transfer(client->adapter, msgs, 2); + + if (ret == 2) { + ds1337_convert_to_time(dt, buf); + ret = 0; + } else + printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret); + + return ret; +} + + +static int ds1337_set_datetime(struct i2c_client *client, + struct rtc_time *dt, int datetoo) +{ + unsigned char buf[8]; + int ret, len = 4; + + if (rtc_debug) + printk("%s()\n", __FUNCTION__); + + if (rtc_debug > 2) { + printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year); + printk("ds1337_set_datetime: tm_mon = %d\n", dt->tm_mon); + printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday); + printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour); + printk("ds1337_set_datetime: tm_min = %d\n", dt->tm_min); + printk("ds1337_set_datetime: tm_sec = %d\n", dt->tm_sec); + } + + buf[0] = 0; /* register address on DS1337 */ + buf[1] = (BIN_TO_BCD(dt->tm_sec)); + buf[2] = (BIN_TO_BCD(dt->tm_min)); + buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24; + + if (datetoo) { + len = 8; + /* we skip buf[4] as we don't use day-of-week. */ + buf[5] = (BIN_TO_BCD(dt->tm_mday)); + buf[6] = (BIN_TO_BCD(dt->tm_mon + 1)); + /* The year only ranges from 0-99, we are being passed an offset from 1900, + * and the chip calulates leap years based on 2000, thus we adjust by 100. + */ + buf[7] = (BIN_TO_BCD(dt->tm_year - 100)); + } + ret = i2c_master_send(client, (char *) buf, len); + if (ret == len) + ret = 0; + else + printk("ds1337_set_datetime(), i2c_master_send() returned %d\n", + ret); + + + return ret; +} + + +#if 0 +static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl) +{ + *ctrl = DAT(client); + + if (rtc_debug) + printk("%s():%d\n", __FUNCTION__, *ctrl); + + return 0; +} + + +static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo) +{ + unsigned char buf[2]; + int ret; + + if (rtc_debug) + printk("%s(%d)\n", __FUNCTION__, *cinfo); + + buf[0] = 7; /* control register address on DS1337 */ + buf[1] = *cinfo; + /* save the control reg info in the client data field so that get_ctrl + * function doesn't have to do an I2C transfer to get it. + */ + DAT(client) = buf[1]; + + ret = i2c_master_send(client, (char *) buf, 2); + + return ret; +} +#endif + + +static int ds1337_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + if (rtc_debug) + printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd); + + switch (cmd) { + case DS1337_GETDATETIME: + return ds1337_get_datetime(client, arg); + + case DS1337_SETTIME: + return ds1337_set_datetime(client, arg, 0); + + case DS1337_SETDATETIME: + return ds1337_set_datetime(client, arg, 1); + + default: + return -EINVAL; + } +} + + +static int ds1337_rtc_noop(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int ds1337_rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct rtc_time wtime; + int status = 0; + + if (rtc_debug) + printk("%s()\n", __FUNCTION__); + + switch (cmd) { + default: + case RTC_UIE_ON: // mask ints from RTC updates + case RTC_UIE_OFF: + case RTC_PIE_ON: // allow periodic interrupts + case RTC_PIE_OFF: + case RTC_AIE_ON: // mask alarm int enable bit + case RTC_AIE_OFF: + case RTC_ALM_SET: + /* + * This expects a struct rtc_time. Writing 0xff means + * "don't care" or "match all". Only the tm_hour, + * tm_min and tm_sec are used. + */ + case RTC_ALM_READ: + // get_rtc_alm_time(&wtime); + case RTC_IRQP_READ: // Read the periodic IRQ rate + case RTC_IRQP_SET: // Set periodic IRQ rate + case RTC_EPOCH_READ: + // return put_user (epoch, (unsigned long *)arg); + case RTC_EPOCH_SET: + case RTC_WKALM_SET: + case RTC_WKALM_RD: + status = -EINVAL; + break; + + case RTC_RD_TIME: + spin_lock_irqsave(&ds1337_rtc_lock, flags); + ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime); + spin_unlock_irqrestore(&ds1337_rtc_lock, flags); + + if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time))) + status = -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) { + status = -EACCES; + break; + } + + if (copy_from_user + (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) { + status = -EFAULT; + break; + } + + spin_lock_irqsave(&ds1337_rtc_lock, flags); + ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime); + spin_unlock_irqrestore(&ds1337_rtc_lock, flags); + break; + } + + return status; +} + + +static char *ds1337_mon2str(unsigned int mon) +{ + char *mon2str[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + if (mon > 11) + return "error"; + else + return mon2str[mon]; +} + + +static int ds1337_rtc_proc_output(char *buf) +{ +#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no") + + unsigned char ram[DS1337_MEM_SIZE]; + int ret; + + char *p = buf; + + ret = ds1337_readram(ram, DS1337_MEM_SIZE); + if (ret > 0) { +#ifdef DEBUG + int i; + char text[9]; +#endif + struct rtc_time dt; + + p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n"); + + ds1337_convert_to_time(&dt, ram); + p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n", + dt.tm_mday, ds1337_mon2str(dt.tm_mon), + dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec); + +#ifdef DEBUG + p += sprintf(p, "RAM dump:\n"); + text[8] = '\0'; + for (i = 0; i < DS1337_MEM_SIZE; i++) { + if ((i % 8) == 0) + p += sprintf(p, "%02X: ", i); + p += sprintf(p, "%02X ", ram[i]); + + if ((ram[i] < 32) || (ram[i] > 126)) + ram[i] = '.'; + text[i % 8] = ram[i]; + if ((i % 8) == 7) + p += sprintf(p, "%s\n", text); + } + p += sprintf(p, "\n"); +#endif + } else { + p += sprintf(p, "Failed to read RTC memory!\n"); + } + + return p - buf; +} + + +static int ds1337_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = ds1337_rtc_proc_output(page); + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + + +static __init int ds1337_init(void) +{ + int retval = 0; + + if (rtc_debug>1) + printk("%s()\n", __FUNCTION__); + + if (slave_address != 0xffff) { + normal_addr[0] = slave_address; + } + + if (normal_addr[0] == 0xffff) { + printk(KERN_ERR + "I2C: Invalid slave address for DS1337 RTC (%#x)\n", + normal_addr[0]); + return -EINVAL; + } + + retval = i2c_add_driver(&ds1337_driver); + + if (retval == 0) { + misc_register(&ds1337_rtc_miscdev); + create_proc_read_entry(DS1337_PROC_NAME, 0, 0, + ds1337_rtc_read_proc, NULL); + printk("I2C: DS1337 RTC driver loaded\n"); + } + return retval; +} + + +static __exit void ds1337_exit(void) +{ + if (rtc_debug>1) + printk("%s()\n", __FUNCTION__); + + remove_proc_entry(DS1337_PROC_NAME, NULL); + misc_deregister(&ds1337_rtc_miscdev); + i2c_del_driver(&ds1337_driver); +} + + +module_init(ds1337_init); +module_exit(ds1337_exit); + +MODULE_PARM(slave_address, "i"); +MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC"); + +MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH"); +MODULE_LICENSE("GPL"); --- /dev/null +++ linux-2.4.21/drivers/char/ds1337.h @@ -0,0 +1,43 @@ +/* + * ds1337.h + * + * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef DS1337_H +#define DS1337_H + +#define DS1337_I2C_SLAVE_ADDR 0x68 +//#define DS1337_RAM_ADDR_START 0x10 +//#define DS1337_RAM_ADDR_END 0x10 +#define DS1337_MEM_SIZE 0x10 + +#define DS1337_PROC_NAME "driver/ds1337" + +struct rtc_mem { + unsigned int loc; + unsigned int nr; + unsigned char *data; +}; + +#define DS1337_GETDATETIME 0 +#define DS1337_SETTIME 1 +#define DS1337_SETDATETIME 2 + +#define DS1337_RATE_1HZ 0x00 /* Rate Select 1 Hz */ +#define DS1337_RATE_4096HZ 0x01 /* Rate Select 4096 kHz */ +#define DS1337_RATE_8192HZ 0x02 /* Rate Select 8192 kHz */ +#define DS1337_RATE_32768HZ 0x03 /* Rate Select 32768 kHz */ + +#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) +#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) + +#define DS1337_HOUR12 0x40 +#define DS1337_HOUR24 0x00 +#define DS1337_HOURS_24(val) BCD_TO_BIN((val & 0x3f)) + +#endif --- /dev/null +++ linux-2.4.21/drivers/char/german.map @@ -0,0 +1,528 @@ +keymaps 0-2,4-6,8-10,12 +keycode 1 = Escape Escape + alt keycode 1 = Meta_Escape + shift alt keycode 1 = Meta_Escape +keycode 2 = one exclam + alt keycode 2 = Meta_one + shift alt keycode 2 = Meta_exclam +keycode 3 = two quotedbl twosuperior nul + alt keycode 3 = Meta_two + shift alt keycode 3 = Meta_quotedbl + control alt keycode 3 = Meta_nul +keycode 4 = three section threesuperior Escape + alt keycode 4 = Meta_three + control alt keycode 4 = Meta_Escape +keycode 5 = four dollar + alt keycode 5 = Meta_four + shift alt keycode 5 = Meta_dollar +keycode 6 = five percent + alt keycode 6 = Meta_five + shift alt keycode 6 = Meta_percent +keycode 7 = six ampersand + control keycode 7 = Control_asciicircum + alt keycode 7 = Meta_six + shift alt keycode 7 = Meta_ampersand +keycode 8 = seven slash braceleft + alt keycode 8 = Meta_seven + shift alt keycode 8 = Meta_slash + altgr alt keycode 8 = Meta_braceleft +keycode 9 = eight parenleft bracketleft + alt keycode 9 = Meta_eight + shift alt keycode 9 = Meta_parenleft + altgr alt keycode 9 = Meta_bracketleft +keycode 10 = nine parenright bracketright + altgr control keycode 10 = Control_bracketright + alt keycode 10 = Meta_nine + shift alt keycode 10 = Meta_parenright + altgr alt keycode 10 = Meta_bracketright +keycode 11 = zero equal braceright + alt keycode 11 = Meta_zero + shift alt keycode 11 = Meta_equal + altgr alt keycode 11 = Meta_braceright +keycode 12 = ssharp question backslash + altgr control keycode 12 = Control_backslash + shift alt keycode 12 = Meta_question + altgr alt keycode 12 = Meta_backslash +keycode 13 = apostrophe grave + alt keycode 13 = 0x08b4 + shift alt keycode 13 = Meta_grave +keycode 14 = BackSpace Delete + alt keycode 14 = Meta_BackSpace + shift alt keycode 14 = Meta_Delete +keycode 15 = Tab Tab + alt keycode 15 = Meta_Tab + shift alt keycode 15 = Meta_Tab +keycode 16 = +q +Q at Control_q Control_q Control_q Meta_q Meta_Q Meta_at Meta_Control_q +keycode 17 = w +keycode 18 = +e +E currency Control_e Control_e Control_e Meta_e Meta_E Meta_e Meta_Control_e +keycode 19 = r +keycode 20 = t +keycode 21 = z +keycode 22 = u +keycode 23 = i +keycode 24 = o +keycode 25 = p +keycode 26 = +udiaeresis +Udiaeresis +keycode 27 = plus asterisk asciitilde + alt keycode 27 = Meta_plus + shift alt keycode 27 = Meta_asterisk +keycode 28 = Return + alt keycode 28 = Meta_Control_m +keycode 29 = Control +keycode 30 = a +keycode 31 = s +keycode 32 = d +keycode 33 = f +keycode 34 = g +keycode 35 = h +keycode 36 = j +keycode 37 = k +keycode 38 = l +keycode 39 = +odiaeresis +Odiaeresis +keycode 40 = +adiaeresis +Adiaeresis +keycode 41 = asciicircum degree Meta_asciicircum Control_asciicircum + control alt keycode 41 = Meta_Control_asciicircum +keycode 42 = Shift +keycode 43 = numbersign apostrophe + alt keycode 43 = Meta_numbersign + shift alt keycode 43 = Meta_apostrophe +keycode 44 = y +keycode 45 = x +keycode 46 = c +keycode 47 = v +keycode 48 = b +keycode 49 = n +keycode 50 = +m +M mu Control_m Control_m Control_m Meta_m Meta_M Meta_m Meta_Control_m +keycode 51 = comma semicolon + alt keycode 51 = Meta_comma + shift alt keycode 51 = Meta_semicolon +keycode 52 = period colon + alt keycode 52 = Meta_period + shift alt keycode 52 = Meta_colon +keycode 53 = minus underscore Meta_minus + shift control keycode 53 = Control_underscore + alt keycode 53 = Meta_minus + shift alt keycode 53 = Meta_underscore +keycode 54 = Shift +keycode 55 = KP_Multiply + altgr keycode 55 = Hex_C +keycode 56 = Alt +keycode 57 = space space Meta_space nul + alt keycode 57 = Meta_space + shift alt keycode 57 = Meta_space + control alt keycode 57 = Meta_nul +keycode 58 = Caps_Lock +keycode 59 = F1 F13 Console_13 F25 + altgr control keycode 59 = F1 + alt keycode 59 = Console_1 + control alt keycode 59 = Console_1 +keycode 60 = F2 F14 Console_14 F26 + altgr control keycode 60 = F2 + alt keycode 60 = Console_2 + control alt keycode 60 = Console_2 +keycode 61 = F3 F15 Console_15 F27 + altgr control keycode 61 = F3 + alt keycode 61 = Console_3 + control alt keycode 61 = Console_3 +keycode 62 = F4 F16 Console_16 F28 + altgr control keycode 62 = F4 + alt keycode 62 = Console_4 + control alt keycode 62 = Console_4 +keycode 63 = F5 F17 Console_17 F29 + altgr control keycode 63 = F5 + alt keycode 63 = Console_5 + control alt keycode 63 = Console_5 +keycode 64 = F6 F18 Console_18 F30 + altgr control keycode 64 = F6 + alt keycode 64 = Console_6 + control alt keycode 64 = Console_6 +keycode 65 = F7 F19 Console_19 F31 + altgr control keycode 65 = F7 + alt keycode 65 = Console_7 + control alt keycode 65 = Console_7 +keycode 66 = F8 F20 Console_20 F32 + altgr control keycode 66 = F8 + alt keycode 66 = Console_8 + control alt keycode 66 = Console_8 +keycode 67 = F9 F21 Console_21 F33 + altgr control keycode 67 = F9 + alt keycode 67 = Console_9 + control alt keycode 67 = Console_9 +keycode 68 = F10 F22 Console_22 F34 + altgr control keycode 68 = F10 + alt keycode 68 = Console_10 + control alt keycode 68 = Console_10 +keycode 69 = Num_Lock + altgr keycode 69 = Hex_A +keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State + alt keycode 70 = Scroll_Lock +keycode 71 = KP_7 + altgr keycode 71 = Hex_7 + alt keycode 71 = Ascii_7 +keycode 72 = KP_8 + altgr keycode 72 = Hex_8 + alt keycode 72 = Ascii_8 +keycode 73 = KP_9 + altgr keycode 73 = Hex_9 + alt keycode 73 = Ascii_9 +keycode 74 = KP_Subtract + altgr keycode 74 = Hex_D +keycode 75 = KP_4 + altgr keycode 75 = Hex_4 + alt keycode 75 = Ascii_4 +keycode 76 = KP_5 + altgr keycode 76 = Hex_5 + alt keycode 76 = Ascii_5 +keycode 77 = KP_6 + altgr keycode 77 = Hex_6 + alt keycode 77 = Ascii_6 +keycode 78 = KP_Add + altgr keycode 78 = Hex_E +keycode 79 = KP_1 + altgr keycode 79 = Hex_1 + alt keycode 79 = Ascii_1 +keycode 80 = KP_2 + altgr keycode 80 = Hex_2 + alt keycode 80 = Ascii_2 +keycode 81 = KP_3 + altgr keycode 81 = Hex_3 + alt keycode 81 = Ascii_3 +keycode 82 = KP_0 + altgr keycode 82 = Hex_0 + alt keycode 82 = Ascii_0 +keycode 83 = KP_Comma + altgr control keycode 83 = Boot + control alt keycode 83 = Boot +#keycode 84 = Last_Console +keycode 85 = +keycode 86 = less greater bar + alt keycode 86 = Meta_less + shift alt keycode 86 = Meta_greater + altgr alt keycode 86 = Meta_bar +keycode 87 = F11 F23 Console_23 F35 + altgr control keycode 87 = F11 + alt keycode 87 = Console_11 + control alt keycode 87 = Console_11 +keycode 88 = F12 F24 Console_24 F36 + altgr control keycode 88 = F12 + alt keycode 88 = Console_12 + control alt keycode 88 = Console_12 +keycode 89 = slash question degree + alt keycode 89 = Meta_slash + shift alt keycode 89 = Meta_question +keycode 90 = +keycode 91 = +keycode 92 = +keycode 93 = +keycode 94 = +keycode 95 = +keycode 96 = KP_Enter + altgr keycode 96 = Hex_F +keycode 97 = Control +keycode 98 = KP_Divide + altgr keycode 98 = Hex_B +keycode 99 = Compose +keycode 100 = AltGr + alt keycode 100 = Compose +keycode 101 = Break +keycode 102 = Find +keycode 103 = Up + alt keycode 103 = KeyboardSignal +keycode 104 = Prior + shift keycode 104 = Scroll_Backward +keycode 105 = Left +# alt keycode 105 = Decr_Console +keycode 106 = Right +# alt keycode 106 = Incr_Console +keycode 107 = Select +keycode 108 = Down +keycode 109 = Next + shift keycode 109 = Scroll_Forward +keycode 110 = Insert +keycode 111 = Remove + altgr control keycode 111 = Boot + control alt keycode 111 = Boot +keycode 112 = Macro + shift alt keycode 112 = VoidSymbol + altgr alt keycode 112 = VoidSymbol +keycode 113 = F13 + shift alt keycode 113 = VoidSymbol + altgr alt keycode 113 = VoidSymbol +keycode 114 = F14 + shift alt keycode 114 = VoidSymbol + altgr alt keycode 114 = VoidSymbol +keycode 115 = Help + shift alt keycode 115 = VoidSymbol + altgr alt keycode 115 = VoidSymbol +keycode 116 = Do + shift alt keycode 116 = VoidSymbol + altgr alt keycode 116 = VoidSymbol +keycode 117 = F17 + shift alt keycode 117 = VoidSymbol + altgr alt keycode 117 = VoidSymbol +keycode 118 = KP_MinPlus + shift alt keycode 118 = VoidSymbol + altgr alt keycode 118 = VoidSymbol +keycode 119 = Pause +keycode 120 = +keycode 121 = +keycode 122 = +keycode 123 = +keycode 124 = +#keycode 125 = Decr_Console +#keycode 126 = Incr_Console +keycode 127 = Compose +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '!' '!' to 'ˇ' +compose '"' 'A' to 'Ä' +compose '"' 'E' to 'Ë' +compose '"' 'I' to 'Ď' +compose '"' 'O' to 'Ö' +compose '"' 'U' to 'Ü' +compose '"' 'Y' to 'ľ' +compose '"' 'a' to 'ä' +compose '"' 'c' to '©' +compose '"' 'e' to 'ë' +compose '"' 'i' to 'ď' +compose '"' 'o' to 'ö' +compose '"' 'r' to '®' +compose '"' 'u' to 'ü' +compose '"' 'y' to '˙' +compose '(' 'c' to '©' +compose '(' 'r' to '®' +compose '+' '-' to '±' +compose ',' 'A' to 'ˇ' +compose ',' 'C' to 'Ç' +compose ',' 'E' to 'Ę' +compose ',' 'G' to '«' +compose ',' 'I' to 'Ç' +compose ',' 'K' to 'Ó' +compose ',' 'L' to '¦' +compose ',' 'N' to 'Ń' +compose ',' 'R' to 'Ł' +compose ',' 'S' to 'Ş' +compose ',' 'T' to 'Ţ' +compose ',' 'U' to 'Ů' +compose ',' 'a' to '±' +compose ',' 'c' to 'ç' +compose ',' 'e' to 'ę' +compose ',' 'g' to '»' +compose ',' 'i' to 'ç' +compose ',' 'k' to 'ó' +compose ',' 'l' to '¶' +compose ',' 'n' to 'ń' +compose ',' 'r' to 'ł' +compose ',' 's' to 'ş' +compose ',' 't' to 'ţ' +compose ',' 'u' to 'ů' +compose '-' ':' to '÷' +compose '-' 'A' to 'Ş' +compose '-' 'C' to '˘' +compose '-' 'D' to 'Đ' +compose '-' 'E' to '¤' +compose '-' 'H' to 'ˇ' +compose '-' 'L' to 'Ł' +compose '-' 'O' to 'ş' +compose '-' 'T' to '¬' +compose '-' 'Y' to 'Ą' +compose '-' 'a' to 'Ş' +compose '-' 'c' to '˘' +compose '-' 'd' to 'đ' +compose '-' 'e' to '¤' +compose '-' 'h' to '±' +compose '-' 'l' to 'Ł' +compose '-' 'l' to 'Ą' +compose '-' 'l' to 'ł' +compose '-' 'o' to 'ş' +compose '-' 't' to 'Ľ' +compose '.' '.' to '·' +compose '.' 'C' to 'Ĺ' +compose '.' 'C' to 'Ő' +compose '.' 'E' to 'Ě' +compose '.' 'I' to '©' +compose '.' 'Z' to 'Ż' +compose '.' 'c' to 'ĺ' +compose '.' 'c' to 'ő' +compose '.' 'e' to 'ě' +compose '.' 'i' to 'ą' +compose '.' 'z' to 'ż' +compose '/' 'D' to 'Đ' +compose '/' 'L' to 'Ł' +compose '/' 'O' to 'Ř' +compose '/' 'T' to '¬' +compose '/' 'c' to '˘' +compose '/' 'd' to 'đ' +compose '/' 'l' to 'ł' +compose '/' 'o' to 'ř' +compose '/' 't' to 'Ľ' +compose '0' 'A' to 'Ĺ' +compose '0' 'U' to 'Ů' +compose '0' 'a' to 'ĺ' +compose '0' 'u' to 'ů' +compose '1' '2' to '˝' +compose '1' '4' to 'Ľ' +compose '3' '4' to 'ľ' +compose ':' '-' to '÷' +compose ':' 'A' to 'Ä' +compose ':' 'E' to 'Ë' +compose ':' 'O' to 'Ö' +compose ':' 'U' to 'Ü' +compose ':' 'a' to 'ä' +compose ':' 'e' to 'ë' +compose ':' 'o' to 'ö' +compose ':' 'u' to 'ü' +compose '<' '<' to '«' +compose '>' '>' to '»' +compose '?' '?' to 'ż' +compose 'A' 'A' to 'Ĺ' +compose 'A' 'E' to 'Ć' +compose 'I' 'J' to 'ľ' +compose 'L' '=' to 'Ł' +compose 'N' 'G' to '˝' +compose 'N' 'H' to 'Ń' +compose 'N' 'N' to 'Ń' +compose 'N' 'Y' to 'Ń' +compose 'N' 'h' to 'Ń' +compose 'N' 'n' to 'Ń' +compose 'N' 'y' to 'Ń' +compose 'O' 'A' to 'Ĺ' +compose 'O' 'E' to 'Ľ' +compose 'O' 'e' to 'Ľ' +compose 'T' 'H' to 'Ţ' +compose 'U' 'U' to 'Ů' +compose 'Y' '=' to 'Ą' +compose '\'' 'A' to 'Á' +compose '\'' 'C' to 'Ć' +compose '\'' 'E' to 'É' +compose '\'' 'I' to 'Í' +compose '\'' 'L' to 'Ĺ' +compose '\'' 'N' to 'Ń' +compose '\'' 'O' to 'Ó' +compose '\'' 'R' to 'Ŕ' +compose '\'' 'S' to '¦' +compose '\'' 'U' to 'Ú' +compose '\'' 'Y' to 'Ý' +compose '\'' 'Z' to '¬' +compose '\'' 'a' to 'á' +compose '\'' 'c' to 'ć' +compose '\'' 'e' to 'é' +compose '\'' 'i' to 'í' +compose '\'' 'l' to 'ĺ' +compose '\'' 'n' to 'ń' +compose '\'' 'o' to 'ó' +compose '\'' 'r' to 'ŕ' +compose '\'' 's' to '¶' +compose '\'' 'u' to 'ú' +compose '\'' 'y' to 'ý' +compose '\'' 'z' to 'Ľ' +compose '^' '!' to 'ˇ' +compose '^' '*' to '×' +compose '^' '.' to '·' +compose '^' '/' to '÷' +compose '^' '1' to 'ą' +compose '^' '2' to '˛' +compose '^' '3' to 'ł' +compose '^' ':' to '÷' +compose '^' '?' to 'ż' +compose '^' 'A' to 'Â' +compose '^' 'C' to 'Ç' +compose '^' 'D' to 'Đ' +compose '^' 'E' to 'Ę' +compose '^' 'G' to 'Ô' +compose '^' 'H' to '¦' +compose '^' 'I' to 'Î' +compose '^' 'J' to '¬' +compose '^' 'L' to 'Ą' +compose '^' 'N' to 'Ń' +compose '^' 'R' to 'Ř' +compose '^' 'S' to '¦' +compose '^' 'T' to '«' +compose '^' 'U' to 'Ű' +compose '^' 'Z' to '´' +compose '^' 'a' to 'â' +compose '^' 'c' to 'ç' +compose '^' 'd' to 'đ' +compose '^' 'e' to 'ę' +compose '^' 'g' to 'ř' +compose '^' 'h' to '¶' +compose '^' 'i' to 'î' +compose '^' 'j' to 'Ľ' +compose '^' 'l' to 'µ' +compose '^' 'n' to 'ń' +compose '^' 'o' to 'ô' +compose '^' 'r' to 'ř' +compose '^' 's' to '¨' +compose '^' 't' to '»' +compose '^' 'u' to 'ű' +compose '^' 'x' to '×' +compose '^' 'z' to '¸' +compose '`' 'A' to 'Ŕ' +compose '`' 'E' to 'Č' +compose '`' 'I' to 'Ě' +compose '`' 'O' to 'Ň' +compose '`' 'U' to 'Ů' +compose '`' 'a' to 'ŕ' +compose '`' 'e' to 'č' +compose '`' 'i' to 'ě' +compose '`' 'o' to 'ň' +compose '`' 'u' to 'ů' +compose 'a' 'a' to 'ĺ' +compose 'a' 'e' to 'ć' +compose 'c' '/' to '˘' +compose 'c' '=' to '˘' +compose 'e' '=' to '¤' +compose 'i' 'j' to '˙' +compose 'm' 'u' to 'µ' +compose 'n' 'g' to 'ż' +compose 'n' 'h' to 'ń' +compose 'n' 'n' to 'ń' +compose 'o' 'a' to 'ĺ' +compose 'o' 'e' to '˝' +compose 's' 's' to 'ß' +compose 's' 'z' to 'ß' +compose 't' 'h' to 'ţ' +compose 'u' 'u' to 'ů' +compose 'v' 'S' to '¦' +compose 'v' 'Z' to '´' +compose 'v' 's' to '¨' +compose 'v' 'z' to '¸' +compose 'x' 'x' to '×' +compose '~' 'A' to 'Ă' +compose '~' 'G' to '«' +compose '~' 'I' to 'Ą' +compose '~' 'N' to 'Ń' +compose '~' 'O' to 'Ő' +compose '~' 'U' to 'Ý' +compose '~' 'a' to 'ă' +compose '~' 'g' to '»' +compose '~' 'i' to 'µ' +compose '~' 'n' to 'ń' +compose '~' 'o' to 'ő' +compose '~' 'u' to 'ý' --- /dev/null +++ linux-2.4.21/drivers/char/input_keyb.c @@ -0,0 +1,167 @@ +/* + * linux/drivers/char/input_keyb.c by Russ Dill + * taken from pc_keyb.c + * + * This code grabs keypresses from the input layer and makes them + * available to the console. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#include +#include + +#include +#include + +/* Simple translation table for the SysRq keys */ + +unsigned char input_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, 0, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, 0, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f */ + 0, 0, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, 0, E0_END, /* 0x48-0x4f */ + E0_DOWN, E0_PGDN, 0, 0, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, 0, 0, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int input_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode > 255 || keycode > 127) return -EINVAL; + e0_keys[scancode - 128] = keycode; + return 0; +} + +int input_getkeycode(unsigned int scancode) +{ + return scancode > 255 ? -EINVAL : e0_keys[scancode - 128]; +} + +#define KBD_REPORT_UNKN +int input_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else + *keycode = scancode; + return 1; +} + +char input_unexpected_up(unsigned char keycode) +{ + return 0200; +} + +/* Allow for loadable keyboard drivers */ +EXPORT_SYMBOL(input_setkeycode); +EXPORT_SYMBOL(input_unexpected_up); +EXPORT_SYMBOL(input_translate); +EXPORT_SYMBOL(input_sysrq_xlate); +EXPORT_SYMBOL(input_getkeycode); +EXPORT_SYMBOL(k_setkeycode); +EXPORT_SYMBOL(k_unexpected_up); +EXPORT_SYMBOL(k_translate); +EXPORT_SYMBOL(k_getkeycode); +#ifdef CONFIG_MAGIC_SYSRQ +EXPORT_SYMBOL(k_sysrq_key); +EXPORT_SYMBOL(k_sysrq_xlate); +#endif --- linux-2.4.21/drivers/char/keyboard.c~wedge +++ linux-2.4.21/drivers/char/keyboard.c @@ -77,6 +77,7 @@ void (*kbd_ledfunc)(unsigned int led); EXPORT_SYMBOL(handle_scancode); EXPORT_SYMBOL(kbd_ledfunc); +EXPORT_SYMBOL(key_maps); EXPORT_SYMBOL(kbd_refresh_leds); extern void ctrl_alt_del(void); --- linux-2.4.21/drivers/char/serial.c~ramses-serial +++ linux-2.4.21/drivers/char/serial.c @@ -1,138 +1,8 @@ -/* - * linux/drivers/char/serial.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, - * 1998, 1999 Theodore Ts'o - * - * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now - * much more extensible to support other serial cards based on the - * 16450/16550A UART's. Added support for the AST FourPort and the - * Accent Async board. - * - * set_serial_info fixed to set the flags, custom divisor, and uart - * type fields. Fix suggested by Michael K. Johnson 12/12/92. - * - * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis - * - * 03/96: Modularised by Angelo Haritsis - * - * rs_set_termios fixed to look also for changes of the input - * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. - * Bernd Anhäupl 05/17/96. - * - * 1/97: Extended dumb serial ports are a config option now. - * Saves 4k. Michael A. Griffith - * - * 8/97: Fix bug in rs_set_termios with RTS - * Stanislav V. Voronyi - * - * 3/98: Change the IRQ detection, use of probe_irq_o*(), - * suppress TIOCSERGWILD and TIOCSERSWILD - * Etienne Lorrain - * - * 4/98: Added changes to support the ARM architecture proposed by - * Russell King - * - * 5/99: Updated to include support for the XR16C850 and ST16C654 - * uarts. Stuart MacDonald - * - * 8/99: Generalized PCI support added. Theodore Ts'o - * - * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a - * few races on freeing buffers too. - * Alan Modra - * - * 5/00: Support for the RSA-DV II/S card added. - * Kiyokazu SUTO - * - * 6/00: Remove old-style timer, use timer_list - * Andrew Morton - * - * 7/00: Support Timedia/Sunix/Exsys PCI cards - * - * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. - * Arnaldo Carvalho de Melo - * - * 10/00: add in optional software flow control for serial console. - * Kanoj Sarcar (Modified by Theodore Ts'o) - * - * 02/02: Fix for AMD Elan bug in transmit irq routine, by - * Christer Weinigel , - * Robert Schwebel , - * Juergen Beisert , - * Theodore Ts'o - */ - -static char *serial_version = "5.05c"; -static char *serial_revdate = "2001-07-08"; - -/* - * Serial driver configuration section. Here are the various options: - * - * CONFIG_HUB6 - * Enables support for the venerable Bell Technologies - * HUB6 card. - * - * CONFIG_SERIAL_MANY_PORTS - * Enables support for ports beyond the standard, stupid - * COM 1/2/3/4. - * - * CONFIG_SERIAL_MULTIPORT - * Enables support for special multiport board support. - * - * CONFIG_SERIAL_SHARE_IRQ - * Enables support for multiple serial ports on one IRQ - * - * CONFIG_SERIAL_DETECT_IRQ - * Enable the autodetection of IRQ on standart ports - * - * SERIAL_PARANOIA_CHECK - * Check the magic number for the async_structure where - * ever possible. - * - * CONFIG_SERIAL_ACPI - * Enable support for serial console port and serial - * debug port as defined by the SPCR and DBGP tables in - * ACPI 2.0. - */ +#undef DEBUG #include #include -#undef SERIAL_PARANOIA_CHECK -#define CONFIG_SERIAL_NOPAUSE_IO -#define SERIAL_DO_RESTART - -#if 0 -/* These defines are normally controlled by the autoconf.h */ -#define CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_DETECT_IRQ -#define CONFIG_SERIAL_MULTIPORT -#define CONFIG_HUB6 -#endif - -#ifdef CONFIG_PCI -#define ENABLE_SERIAL_PCI -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#ifndef CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS -#endif -#endif - -#ifdef CONFIG_SERIAL_ACPI -#define ENABLE_SERIAL_ACPI -#endif - -#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) -#ifndef ENABLE_SERIAL_PNP -#define ENABLE_SERIAL_PNP -#endif -#endif - #ifdef CONFIG_ARCH_PXA #define pxa_port(x) ((x) == PORT_PXA) #define pxa_buggy_port(x) ({ \ @@ -149,39 +19,16 @@ #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW #undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT -#undef SERIAL_DEBUG_PCI -#undef SERIAL_DEBUG_AUTOCONF /* Sanity checks */ -#ifdef CONFIG_SERIAL_MULTIPORT -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#endif - -#ifdef CONFIG_HUB6 -#ifndef CONFIG_SERIAL_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS -#endif -#ifndef CONFIG_SERIAL_SHARE_IRQ -#define CONFIG_SERIAL_SHARE_IRQ -#endif -#endif - #ifdef MODULE #undef CONFIG_SERIAL_CONSOLE #endif -#define CONFIG_SERIAL_RSA - #define RS_STROBE_TIME (10*HZ) #define RS_ISR_PASS_LIMIT 256 -#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) -#define SERIAL_INLINE -#endif - /* * End of serial driver configuration section. */ @@ -213,53 +60,51 @@ #include #include #include -#if (LINUX_VERSION_CODE >= 131343) #include -#endif -#if (LINUX_VERSION_CODE >= 131336) #include -#endif #include #ifdef CONFIG_SERIAL_CONSOLE #include #endif -#ifdef ENABLE_SERIAL_PCI -#include -#endif -#ifdef ENABLE_SERIAL_PNP -#include -#endif #ifdef CONFIG_MAGIC_SYSRQ #include #endif -/* - * All of the compatibilty code so we can compile serial.c against - * older kernels is hidden in serial_compat.h - */ -#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ -#include "serial_compat.h" -#endif - #include #include #include #include -#if defined(CONFIG_MAC_SERIAL) -#define SERIAL_DEV_OFFSET ((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2) -#else -#define SERIAL_DEV_OFFSET 0 -#endif +#define _INLINE_ -#ifdef SERIAL_INLINE -#define _INLINE_ inline +/* + * The TI16754 has 4 UARTS. They are selected with nCS3 and some + * address bits: + * + * 12 8 4 + * nA8, nCS[3] for MN_UART_1, address mask 1110 1110 0000 0000 = 0xEE00 + * nA9, nCS[3] for MN_UART_1, address mask 1110 1101 0000 0000 = 0xED00 + * nA10, nCS[3] for MN_UART_1, address mask 1110 1011 0000 0000 = 0xEB00 + * nA11, nCS[3] for MN_UART_1, address mask 1110 0111 0000 0000 = 0xE700 + */ +#define RAMSES_UARTA_PHYS (PXA_CS3_PHYS+0xEE00) +#define RAMSES_UARTB_PHYS (PXA_CS3_PHYS+0xED00) +#define RAMSES_UARTC_PHYS (PXA_CS3_PHYS+0xEB00) +#define RAMSES_UARTD_PHYS (PXA_CS3_PHYS+0xE700) +static void *ramses_uarta; // address cookie for UART A +static void *ramses_uartb; // address cookie for UART B +static void *ramses_uartc; // address cookie for UART C/Scanner +static void *ramses_uartd; // address cookie for UART D +static int ramses_stay_on = 0; + +#ifdef DEBUG +#define DPRINTK(fmt,args...) printk("//HS " fmt, ## args) +static int show_io = 1; #else -#define _INLINE_ +#define DPRINTK(fmt,args...) +static int show_io = 0; #endif -static char *serial_name = "Serial driver"; - static DECLARE_TASK_QUEUE(tq_serial); static struct tty_driver serial_driver, callout_driver; @@ -282,9 +127,6 @@ */ static struct async_struct *IRQ_ports[NR_IRQS]; -#ifdef CONFIG_SERIAL_MULTIPORT -static struct rs_multiport_struct rs_multiport[NR_IRQS]; -#endif static int IRQ_timeout[NR_IRQS]; #ifdef CONFIG_SERIAL_CONSOLE static struct console sercons; @@ -294,8 +136,6 @@ static unsigned long break_pressed; /* break, really ... */ #endif -static unsigned detect_uart_irq (struct serial_state * state); -static void autoconfig(struct serial_state * state); static void change_speed(struct async_struct *info, struct termios *old); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); @@ -325,46 +165,86 @@ { 0, 0} }; -#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) - -#define PORT_RSA_MAX 4 -static int probe_rsa[PORT_RSA_MAX]; -static int force_rsa[PORT_RSA_MAX]; - -MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); -MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); -MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); -MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); -#endif /* CONFIG_SERIAL_RSA */ - -struct serial_state rs_table[RS_TABLE_SIZE] = { - SERIAL_PORT_DFNS /* Defined in serial.h */ +static struct serial_state rs_table[] = { + { + type: PORT_PXA, + xmit_fifo_size: 32, + baud_base: 921600, + iomem_base: (void *)&FFUART, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM32, + irq: IRQ_FFUART, + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_PXA, + xmit_fifo_size: 32, + baud_base: 921600, + iomem_base: (void *)&BTUART, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM32, + irq: IRQ_BTUART, + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_PXA, + xmit_fifo_size: 32, + baud_base: 921600, + iomem_base: (void *)&STUART, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM32, + irq: IRQ_STUART, + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_16750, + xmit_fifo_size: 64, + baud_base: 115200*2, + iomem_base: (void *)0, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM, + irq: IRQ_GPIO(7), + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_16750, + xmit_fifo_size: 64, + baud_base: 115200*2, + iomem_base: (void *)0, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM, + irq: IRQ_GPIO(24), + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_16750, + xmit_fifo_size: 64, + baud_base: 115200*2, + iomem_base: (void *)0, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM, + irq: IRQ_GPIO(25), + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_16750, + xmit_fifo_size: 64, + baud_base: 115200*2, + iomem_base: (void *)0, + iomem_reg_shift: 2, + io_type: SERIAL_IO_MEM, + irq: IRQ_GPIO(26), + flags: ASYNC_SKIP_TEST, + }, { + type: PORT_UNKNOWN, + baud_base: 115200, + }, { + type: PORT_UNKNOWN, + baud_base: 115200, + }, { + type: PORT_UNKNOWN, + baud_base: 115200, + }, { + type: PORT_UNKNOWN, + baud_base: 115200, + } }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) -int serial_nr_ports = NR_PORTS; - -#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) -#define NR_PCI_BOARDS 8 - -static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; - -#ifndef IS_PCI_REGION_IOPORT -#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_IO) -#endif -#ifndef IS_PCI_REGION_IOMEM -#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_MEM) -#endif -#ifndef PCI_IRQ_RESOURCE -#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) -#endif -#ifndef pci_get_subvendor -#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) -#define pci_get_subdevice(dev) ((dev)->subsystem_device) -#endif -#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ #ifndef PREPARE_FUNC #define PREPARE_FUNC(dev) (dev->prepare) @@ -403,39 +283,21 @@ #endif -static inline int serial_paranoia_check(struct async_struct *info, - kdev_t device, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null async_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, kdevname(device), routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; - } -#endif - return 0; -} - static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) { + unsigned int value; switch (info->io_type) { -#ifdef CONFIG_HUB6 - case SERIAL_IO_HUB6: - outb(info->hub6 - 1 + offset, info->port); - return inb(info->port+1); -#endif case SERIAL_IO_MEM: - return readb((unsigned long) info->iomem_base + + value = readb((unsigned long) info->iomem_base + (offset<iomem_reg_shift)); + udelay(10); + if (show_io) printk("in %02x = %02x\n", offset, value); + return value; case SERIAL_IO_MEM32: + value = readl((unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + if (show_io) printk("in %02x = %02x\n", offset, value); + return value; return readl((unsigned long) info->iomem_base + (offset<iomem_reg_shift)); default: @@ -447,17 +309,14 @@ int value) { switch (info->io_type) { -#ifdef CONFIG_HUB6 - case SERIAL_IO_HUB6: - outb(info->hub6 - 1 + offset, info->port); - outb(value, info->port+1); - break; -#endif case SERIAL_IO_MEM: + if (show_io) printk("out %02x, %02x\n", offset, value); writeb(value, (unsigned long) info->iomem_base + (offset<iomem_reg_shift)); + udelay(10); break; case SERIAL_IO_MEM32: + if (show_io) printk("out %02x, %02x\n", offset, value); writel(value, (unsigned long) info->iomem_base + (offset<iomem_reg_shift)); break; @@ -509,9 +368,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_stop")) - return; - save_flags(flags); cli(); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; @@ -529,9 +385,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_start")) - return; - save_flags(flags); cli(); if (info->xmit.head != info->xmit.tail && info->xmit.buf @@ -689,11 +542,7 @@ #endif *status = serial_inp(info, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); -#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ tty_flip_buffer_push(tty); -#else - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); -#endif } static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) @@ -758,11 +607,6 @@ icount->dsr++; if (status & UART_MSR_DDCD) { icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - (status & UART_MSR_DCD)) - hardpps(); -#endif } if (status & UART_MSR_DCTS) icount->cts++; @@ -810,120 +654,23 @@ } } -#ifdef CONFIG_SERIAL_SHARE_IRQ -/* - * This is the serial driver's generic interrupt routine - */ -static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - int status, iir; - struct async_struct * info; - int pass_counter = 0; - struct async_struct *end_mark = 0; -#ifdef CONFIG_SERIAL_MULTIPORT - int first_multi = 0; - struct rs_multiport_struct *multi; -#endif - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt(%d)...", irq); -#endif - - info = IRQ_ports[irq]; - if (!info) - return; - -#ifdef CONFIG_SERIAL_MULTIPORT - multi = &rs_multiport[irq]; - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); -#endif - - do { - if (!info->tty || - ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) { - if (!end_mark) - end_mark = info; - goto next; - } -#ifdef SERIAL_DEBUG_INTR - printk("IIR = %x...", serial_in(info, UART_IIR)); -#endif - end_mark = 0; - - info->last_active = jiffies; - - status = serial_inp(info, UART_LSR); -#ifdef SERIAL_DEBUG_INTR - printk("status = %x...", status); -#endif - if (status & UART_LSR_DR) - receive_chars(info, &status, regs); - check_modem_status(info); -#ifdef CONFIG_MELAN - if ((status & UART_LSR_THRE) || - /* for buggy ELAN processors */ - ((iir & UART_IIR_ID) == UART_IIR_THRI)) - transmit_chars(info, 0); -#else - if (status & UART_LSR_THRE) - transmit_chars(info, 0); -#endif - - next: - info = info->next_port; - if (!info) { - info = IRQ_ports[irq]; - if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 - printk("rs loop break\n"); -#endif - break; /* Prevent infinite loops */ - } - continue; - } - } while (end_mark != info); -#ifdef CONFIG_SERIAL_MULTIPORT - if (multi->port_monitor) - printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); -#endif -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} -#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ - /* * This is the serial driver's interrupt routine for a single port */ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) { - int status, iir; + int status; int pass_counter = 0; struct async_struct * info; -#ifdef CONFIG_SERIAL_MULTIPORT - int first_multi = 0; - struct rs_multiport_struct *multi; -#endif #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq); #endif - info = IRQ_ports[irq]; if (!info || !info->tty) return; -#ifdef CONFIG_SERIAL_MULTIPORT - multi = &rs_multiport[irq]; - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); -#endif - - iir = serial_in(info, UART_IIR); do { status = serial_inp(info, UART_LSR); #ifdef SERIAL_DEBUG_INTR @@ -932,120 +679,23 @@ if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); - if ((status & UART_LSR_THRE) || - /* For buggy ELAN processors */ - ((iir & UART_IIR_ID) == UART_IIR_THRI)) + if (status & UART_LSR_THRE) transmit_chars(info, 0); if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if SERIAL_DEBUG_INTR +#if 0 printk("rs_single loop break.\n"); #endif break; } - iir = serial_in(info, UART_IIR); -#ifdef SERIAL_DEBUG_INTR - printk("IIR = %x...", iir); -#endif - } while ((iir & UART_IIR_NO_INT) == 0); - info->last_active = jiffies; -#ifdef CONFIG_SERIAL_MULTIPORT - if (multi->port_monitor) - printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); -#endif -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} - -#ifdef CONFIG_SERIAL_MULTIPORT -/* - * This is the serial driver's for multiport boards - */ -static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) -{ - int status; - struct async_struct * info; - int pass_counter = 0; - int first_multi= 0; - struct rs_multiport_struct *multi; - #ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt_multi(%d)...", irq); + printk("IIR = %x...", serial_in(info, UART_IIR)); #endif - - info = IRQ_ports[irq]; - if (!info) - return; - multi = &rs_multiport[irq]; - if (!multi->port1) { - /* Should never happen */ - printk("rs_interrupt_multi: NULL port1!\n"); - return; - } - if (multi->port_monitor) - first_multi = inb(multi->port_monitor); - - while (1) { - if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) - goto next; - + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); info->last_active = jiffies; - - status = serial_inp(info, UART_LSR); -#ifdef SERIAL_DEBUG_INTR - printk("status = %x...", status); -#endif - if (status & UART_LSR_DR) - receive_chars(info, &status, regs); - check_modem_status(info); - if (status & UART_LSR_THRE) - transmit_chars(info, 0); - - next: - info = info->next_port; - if (info) - continue; - - info = IRQ_ports[irq]; - /* - * The user was a bonehead, and misconfigured their - * multiport info. Rather than lock up the kernel - * in an infinite loop, if we loop too many times, - * print a message and break out of the loop. - */ - if (pass_counter++ > RS_ISR_PASS_LIMIT) { - printk("Misconfigured multiport serial info " - "for irq %d. Breaking out irq loop\n", irq); - break; - } - if (multi->port_monitor) - printk("rs port monitor irq %d: 0x%x, 0x%x\n", - info->state->irq, first_multi, - inb(multi->port_monitor)); - if ((inb(multi->port1) & multi->mask1) != multi->match1) - continue; - if (!multi->port2) - break; - if ((inb(multi->port2) & multi->mask2) != multi->match2) - continue; - if (!multi->port3) - break; - if ((inb(multi->port3) & multi->mask3) != multi->match3) - continue; - if (!multi->port4) - break; - if ((inb(multi->port4) & multi->mask4) != multi->match4) - continue; - break; - } #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif } -#endif /* * ------------------------------------------------------------------- @@ -1107,22 +757,6 @@ if (!info) continue; save_flags(flags); cli(); -#ifdef CONFIG_SERIAL_SHARE_IRQ - if (info->next_port) { - do { - serial_out(info, UART_IER, 0); - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - info = info->next_port; - } while (info); -#ifdef CONFIG_SERIAL_MULTIPORT - if (rs_multiport[i].port1) - rs_interrupt_multi(i, NULL, NULL); - else -#endif - rs_interrupt(i, NULL, NULL); - } else -#endif /* CONFIG_SERIAL_SHARE_IRQ */ rs_interrupt_single(i, NULL, NULL); restore_flags(flags); } @@ -1132,11 +766,7 @@ if (IRQ_ports[0]) { save_flags(flags); cli(); -#ifdef CONFIG_SERIAL_SHARE_IRQ - rs_interrupt(0, NULL, NULL); -#else rs_interrupt_single(0, NULL, NULL); -#endif restore_flags(flags); mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); @@ -1177,50 +807,6 @@ IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1; } -#ifdef CONFIG_SERIAL_RSA -/* Attempts to turn on the RSA FIFO. Returns zero on failure */ -static int enable_rsa(struct async_struct *info) -{ - unsigned char mode; - int result; - unsigned long flags; - - save_flags(flags); cli(); - mode = serial_inp(info, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - - if (!result) { - serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(info, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - } - - restore_flags(flags); - return result; -} - -/* Attempts to turn off the RSA FIFO. Returns zero on failure */ -static int disable_rsa(struct async_struct *info) -{ - unsigned char mode; - int result; - unsigned long flags; - - save_flags(flags); cli(); - mode = serial_inp(info, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - - if (!result) { - serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(info, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - } - - restore_flags(flags); - return result; -} -#endif /* CONFIG_SERIAL_RSA */ - static int startup(struct async_struct * info) { unsigned long flags; @@ -1228,9 +814,6 @@ void (*handler)(int, void *, struct pt_regs *); struct serial_state *state= info->state; unsigned long page; -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned short ICP; -#endif page = get_zeroed_page(GFP_KERNEL); if (!page) @@ -1258,6 +841,22 @@ printk("starting up ttys%d (irq %d)...", info->line, state->irq); #endif + // Special handling to give power to devices + switch (info->line) { + case 3: + //printk("gsm on\n"); + RAMSES_GSM_ON(); + break; + case 4: + //printk("uart on\n"); + RAMSES_UART_ON(); + break; + case 5: + //printk("scanner on\n"); + RAMSES_SCANNER_ON(); + break; + } + if (uart_config[state->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); @@ -1305,25 +904,12 @@ serial_outp(info, UART_LCR, 0); } -#ifdef CONFIG_SERIAL_RSA - /* - * If this is an RSA port, see if we can kick it up to the - * higher speed clock. - */ - if (state->type == PORT_RSA) { - if (state->baud_base != SERIAL_RSA_BAUD_BASE && - enable_rsa(info)) - state->baud_base = SERIAL_RSA_BAUD_BASE; - if (state->baud_base == SERIAL_RSA_BAUD_BASE) - serial_outp(info, UART_RSA_FRR, 0); - } -#endif - #ifdef CONFIG_ARCH_PXA if (state->type == PORT_PXA) { switch ((long)state->iomem_base) { case (long)&FFUART: CKEN |= CKEN6_FFUART; break; case (long)&BTUART: CKEN |= CKEN7_BTUART; break; + //HS TODO: cerf keeps the clock on case (long)&STUART: CKEN |= CKEN5_STUART; break; } } @@ -1344,6 +930,7 @@ /* * Clear the interrupt registers. */ + (void) serial_inp(info, UART_IIR); (void) serial_inp(info, UART_LSR); (void) serial_inp(info, UART_RX); (void) serial_inp(info, UART_IIR); @@ -1371,18 +958,8 @@ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { -#ifdef CONFIG_SERIAL_SHARE_IRQ - free_irq(state->irq, &IRQ_ports[state->irq]); -#ifdef CONFIG_SERIAL_MULTIPORT - if (rs_multiport[state->irq].port1) - handler = rs_interrupt_multi; - else -#endif - handler = rs_interrupt; -#else retval = -EBUSY; goto errout; -#endif /* CONFIG_SERIAL_SHARE_IRQ */ } else handler = rs_interrupt_single; @@ -1417,12 +994,6 @@ info->MCR = 0; if (info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS; -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - if (state->irq == 0) - info->MCR |= UART_MCR_OUT1; - } else -#endif { if (state->irq != 0) info->MCR |= UART_MCR_OUT2; @@ -1437,18 +1008,9 @@ */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; if (pxa_port(state->type)) - info->IER |= UART_IER_UUE | UART_IER_RTOIE; + info->IER |= UART_IER_UUE | UART_IER_RTOIE; //HS TODO: UART_IER_THRI for PXA uarts? serial_outp(info, UART_IER, info->IER); /* enable interrupts */ -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - /* Enable interrupts on the AST Fourport board */ - ICP = (info->port & 0xFE0) | 0x01F; - outb_p(0x80, ICP); - (void) inb_p(ICP); - } -#endif - /* * And clear the interrupt registers again for luck. */ @@ -1469,7 +1031,6 @@ /* * Set up the tty->alt_speed kludge */ -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; @@ -1480,7 +1041,6 @@ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } -#endif /* * and set the speed of the serial port @@ -1516,6 +1076,30 @@ state->irq); #endif + switch (info->line) { + case 3: + if (ramses_stay_on & RAMSES_CONTROL_GSM_PWR) { + //printk("gsm on\n"); + RAMSES_GSM_OFF(); + } + //else printk("gsm stays on\n"); + break; + case 4: + if (ramses_stay_on & RAMSES_CONTROL_UART_PWR) { + //printk("uart off\n"); + RAMSES_UART_OFF(); + } + //else printk("uart stays on\n"); + break; + case 5: + if (ramses_stay_on & RAMSES_CONTROL_SCANNER_PWR) { + //printk("scanner off\n"); + RAMSES_SCANNER_OFF(); + } + //else printk("scanner on\n"); + break; + } + save_flags(flags); cli(); /* Disable interrupts */ /* @@ -1561,13 +1145,6 @@ info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - (void) inb((info->port & 0xFE0) | 0x01F); - info->MCR |= UART_MCR_OUT1; - } else -#endif info->MCR &= ~UART_MCR_OUT2; if (pxa_buggy_port(state->type)) info->MCR ^= UART_MCR_OUT2; @@ -1586,16 +1163,6 @@ UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); -#ifdef CONFIG_SERIAL_RSA - /* - * Reset the RSA board back to 115kbps compat mode. - */ - if ((state->type == PORT_RSA) && - (state->baud_base == SERIAL_RSA_BAUD_BASE && - disable_rsa(info))) - state->baud_base = SERIAL_RSA_BAUD_BASE_LO; -#endif - #ifdef CONFIG_ARCH_PXA if (state->type == PORT_PXA #ifdef CONFIG_SERIAL_CONSOLE @@ -1634,37 +1201,6 @@ restore_flags(flags); } -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, - 600, 1200, 1800, 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 460800, 0 }; - -static int tty_get_baud_rate(struct tty_struct *tty) -{ - struct async_struct * info = (struct async_struct *)tty->driver_data; - unsigned int cflag, i; - - cflag = tty->termios->c_cflag; - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 2) - tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 2; - } - return baud_table[i]; -} -#endif - /* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. @@ -1711,12 +1247,6 @@ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ -#ifdef CONFIG_SERIAL_RSA - if ((info->state->type == PORT_RSA) && - (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && - enable_rsa(info)) - info->state->baud_base = SERIAL_RSA_BAUD_BASE; -#endif baud_base = info->state->baud_base; if (info->state->type == PORT_16C950) { if (baud <= baud_base) @@ -1778,10 +1308,6 @@ if (uart_config[info->state->type].flags & UART_USE_FIFO) { if ((info->state->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; -#ifdef CONFIG_SERIAL_RSA - else if (info->state->type == PORT_RSA) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; -#endif else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } @@ -1864,9 +1390,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_put_char")) - return; - if (!tty || !info->xmit.buf) return; @@ -1888,9 +1411,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) - return; - if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped @@ -1900,8 +1420,6 @@ save_flags(flags); cli(); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); - if (pxa_buggy_port(info->state->type)) - rs_interrupt_single(info->state->irq, NULL, NULL); restore_flags(flags); } @@ -1912,9 +1430,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_write")) - return 0; - if (!tty || !info->xmit.buf || !tmp_buf) return 0; @@ -1978,11 +1493,6 @@ && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); - if (pxa_buggy_port(info->state->type)) { - save_flags(flags); cli(); - rs_interrupt_single(info->state->irq, NULL, NULL); - restore_flags(flags); - } } return ret; } @@ -1991,8 +1501,6 @@ { struct async_struct *info = (struct async_struct *)tty->driver_data; - if (serial_paranoia_check(info, tty->device, "rs_write_room")) - return 0; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } @@ -2000,8 +1508,6 @@ { struct async_struct *info = (struct async_struct *)tty->driver_data; - if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) - return 0; return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } @@ -2010,8 +1516,6 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) - return; save_flags(flags); cli(); info->xmit.head = info->xmit.tail = 0; restore_flags(flags); @@ -2032,16 +1536,11 @@ { struct async_struct *info = (struct async_struct *)tty->driver_data; - if (serial_paranoia_check(info, tty->device, "rs_send_char")) - return; - info->x_char = ch; if (ch) { /* Make sure transmit interrupts are on */ info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); - if (pxa_buggy_port(info->state->type)) - rs_interrupt_single(info->state->irq, NULL, NULL); } } @@ -2064,9 +1563,6 @@ tty->ldisc.chars_in_buffer(tty)); #endif - if (serial_paranoia_check(info, tty->device, "rs_throttle")) - return; - if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); @@ -2089,9 +1585,6 @@ tty->ldisc.chars_in_buffer(tty)); #endif - if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) - return; - if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; @@ -2134,7 +1627,6 @@ tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->custom_divisor; - tmp.hub6 = state->hub6; tmp.io_type = state->io_type; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; @@ -2160,8 +1652,7 @@ new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; change_irq = new_serial.irq != state->irq; - change_port = (new_port != ((int) state->port)) || - (new_serial.hub6 != state->hub6); + change_port = (new_port != ((int) state->port)); if (!capable(CAP_SYS_ADMIN)) { if (change_irq || change_port || @@ -2198,7 +1689,6 @@ if (new_serial.type) { for (i = 0 ; i < NR_PORTS; i++) if ((state != &rs_table[i]) && - (rs_table[i].io_type == SERIAL_IO_PORT) && (rs_table[i].port == new_port) && rs_table[i].type) return -EADDRINUSE; @@ -2220,18 +1710,11 @@ state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100; -#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; if ((state->type != PORT_UNKNOWN) && state->port) { -#ifdef CONFIG_SERIAL_RSA - if (old_state.type == PORT_RSA) - release_region(state->port + UART_RSA_BASE, 16); - else -#endif release_region(state->port,8); } state->type = new_serial.type; @@ -2243,31 +1726,19 @@ shutdown(info); state->irq = new_serial.irq; info->port = state->port = new_port; - info->hub6 = state->hub6 = new_serial.hub6; - if (info->hub6) - info->io_type = state->io_type = SERIAL_IO_HUB6; - else if (info->io_type == SERIAL_IO_HUB6) - info->io_type = state->io_type = SERIAL_IO_PORT; } if ((state->type != PORT_UNKNOWN) && state->port) { -#ifdef CONFIG_SERIAL_RSA - if (state->type == PORT_RSA) - request_region(state->port + UART_RSA_BASE, - 16, "serial_rsa(set)"); - else -#endif request_region(state->port,8,"serial(set)"); } check_and_exit: - if ((!state->port && !state->iomem_base) || !state->type) + if (!state->port || !state->type) return 0; if (info->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) @@ -2276,7 +1747,6 @@ info->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; -#endif change_speed(info, 0); } } else @@ -2414,60 +1884,14 @@ return 0; } -static int do_autoconfig(struct async_struct * info) -{ - int irq, retval; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (info->state->count > 1) - return -EBUSY; - - shutdown(info); - - autoconfig(info->state); - if ((info->state->flags & ASYNC_AUTO_IRQ) && - (info->state->port != 0 || info->state->iomem_base != 0) && - (info->state->type != PORT_UNKNOWN)) { - irq = detect_uart_irq(info->state); - if (irq > 0) - info->state->irq = irq; - } - - retval = startup(info); - if (retval) - return retval; - return 0; -} - /* * rs_break() --- routine which turns the break handling on or off */ -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ -static void send_break( struct async_struct * info, int duration) -{ - if (!CONFIGURED_SERIAL_PORT(info)) - return; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; - cli(); - info->LCR |= UART_LCR_SBC; - serial_out(info, UART_LCR, info->LCR); - schedule(); - info->LCR &= ~UART_LCR_SBC; - serial_out(info, UART_LCR, info->LCR); - sti(); -} -#else static void rs_break(struct tty_struct *tty, int break_state) { struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long flags; - if (serial_paranoia_check(info, tty->device, "rs_break")) - return; - if (!CONFIGURED_SERIAL_PORT(info)) return; save_flags(flags); cli(); @@ -2478,121 +1902,6 @@ serial_out(info, UART_LCR, info->LCR); restore_flags(flags); } -#endif - -#ifdef CONFIG_SERIAL_MULTIPORT -static int get_multiport_struct(struct async_struct * info, - struct serial_multiport_struct *retinfo) -{ - struct serial_multiport_struct ret; - struct rs_multiport_struct *multi; - - multi = &rs_multiport[info->state->irq]; - - ret.port_monitor = multi->port_monitor; - - ret.port1 = multi->port1; - ret.mask1 = multi->mask1; - ret.match1 = multi->match1; - - ret.port2 = multi->port2; - ret.mask2 = multi->mask2; - ret.match2 = multi->match2; - - ret.port3 = multi->port3; - ret.mask3 = multi->mask3; - ret.match3 = multi->match3; - - ret.port4 = multi->port4; - ret.mask4 = multi->mask4; - ret.match4 = multi->match4; - - ret.irq = info->state->irq; - - if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int set_multiport_struct(struct async_struct * info, - struct serial_multiport_struct *in_multi) -{ - struct serial_multiport_struct new_multi; - struct rs_multiport_struct *multi; - struct serial_state *state; - int was_multi, now_multi; - int retval; - void (*handler)(int, void *, struct pt_regs *); - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - state = info->state; - - if (copy_from_user(&new_multi, in_multi, - sizeof(struct serial_multiport_struct))) - return -EFAULT; - - if (new_multi.irq != state->irq || state->irq == 0 || - !IRQ_ports[state->irq]) - return -EINVAL; - - multi = &rs_multiport[state->irq]; - was_multi = (multi->port1 != 0); - - multi->port_monitor = new_multi.port_monitor; - - if (multi->port1) - release_region(multi->port1,1); - multi->port1 = new_multi.port1; - multi->mask1 = new_multi.mask1; - multi->match1 = new_multi.match1; - if (multi->port1) - request_region(multi->port1,1,"serial(multiport1)"); - - if (multi->port2) - release_region(multi->port2,1); - multi->port2 = new_multi.port2; - multi->mask2 = new_multi.mask2; - multi->match2 = new_multi.match2; - if (multi->port2) - request_region(multi->port2,1,"serial(multiport2)"); - - if (multi->port3) - release_region(multi->port3,1); - multi->port3 = new_multi.port3; - multi->mask3 = new_multi.mask3; - multi->match3 = new_multi.match3; - if (multi->port3) - request_region(multi->port3,1,"serial(multiport3)"); - - if (multi->port4) - release_region(multi->port4,1); - multi->port4 = new_multi.port4; - multi->mask4 = new_multi.mask4; - multi->match4 = new_multi.match4; - if (multi->port4) - request_region(multi->port4,1,"serial(multiport4)"); - - now_multi = (multi->port1 != 0); - - if (IRQ_ports[state->irq]->next_port && - (was_multi != now_multi)) { - free_irq(state->irq, &IRQ_ports[state->irq]); - if (now_multi) - handler = rs_interrupt_multi; - else - handler = rs_interrupt; - - retval = request_irq(state->irq, handler, SA_SHIRQ, - "serial", &IRQ_ports[state->irq]); - if (retval) { - printk("Couldn't reallocate serial interrupt " - "driver!!\n"); - } - } - return 0; -} -#endif static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -2601,12 +1910,6 @@ struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct icount; unsigned long flags; -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - int retval, tmp; -#endif - - if (serial_paranoia_check(info, tty->device, "rs_ioctl")) - return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && @@ -2616,45 +1919,6 @@ } switch (cmd) { -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCGSOFTCAR: - tmp = C_CLOCAL(tty) ? 1 : 0; - if (copy_to_user((void *)arg, &tmp, sizeof(int))) - return -EFAULT; - return 0; - case TIOCSSOFTCAR: - if (copy_from_user(&tmp, (void *)arg, sizeof(int))) - return -EFAULT; - - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (tmp ? CLOCAL : 0)); - return 0; -#endif case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2667,9 +1931,6 @@ case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); - case TIOCSERCONFIG: - return do_autoconfig(info); - case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); @@ -2679,15 +1940,6 @@ return -EFAULT; return 0; -#ifdef CONFIG_SERIAL_MULTIPORT - case TIOCSERGETMULTI: - return get_multiport_struct(info, - (struct serial_multiport_struct *) arg); - case TIOCSERSETMULTI: - return set_multiport_struct(info, - (struct serial_multiport_struct *) arg); -#endif - /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest @@ -2754,6 +2006,39 @@ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; + case TIOCSERSETMULTI: + switch (arg) { + + // switch devices on + case 2: RAMSES_LCD_BLIGHT_ON(); break; + case 3: RAMSES_GSM_ON(); break; + case 4: RAMSES_UART_ON(); break; + case 5: RAMSES_SCANNER_ON(); break; + case 7: RAMSES_SCANNER_WAKE_ON(); break; + case 8: RAMSES_SCANNER_TRIG_ON(); break; + case 9: RAMSES_GSM_RESET_ON(); break; + + // switch devices off + case 12: RAMSES_LCD_BLIGHT_OFF(); break; + case 13: RAMSES_GSM_OFF(); break; + case 14: RAMSES_UART_OFF(); break; + case 15: RAMSES_SCANNER_OFF(); break; + case 17: RAMSES_SCANNER_WAKE_OFF(); break; + case 18: RAMSES_SCANNER_TRIG_OFF(); break; + case 19: RAMSES_GSM_RESET_OFF(); break; + + // disable automatic poweroff on file-handle close + case 23: ramses_stay_on |= RAMSES_CONTROL_GSM_PWR; break; + case 24: ramses_stay_on |= RAMSES_CONTROL_UART_PWR; break; + case 25: ramses_stay_on |= RAMSES_CONTROL_SCANNER_PWR; break; + + // enable automatic poweroff on file-handle close + case 33: ramses_stay_on &= ~RAMSES_CONTROL_GSM_PWR; break; + case 34: ramses_stay_on &= ~RAMSES_CONTROL_UART_PWR; break; + case 35: ramses_stay_on &= ~RAMSES_CONTROL_SCANNER_PWR; break; + } + return 0; + default: return -ENOIOCTLCMD; } @@ -2801,18 +2086,6 @@ tty->hw_stopped = 0; rs_start(tty); } - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif } /* @@ -2831,9 +2104,6 @@ struct serial_state *state; unsigned long flags; - if (!info || serial_paranoia_check(info, tty->device, "rs_close")) - return; - state = info->state; save_flags(flags); cli(); @@ -2933,10 +2203,7 @@ { struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long orig_jiffies, char_time; - int lsr; - - if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) - return; + int lsr, old_show_io; if (info->state->type == PORT_UNKNOWN) return; @@ -2974,9 +2241,11 @@ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies); #endif + old_show_io = show_io; + show_io = 0; while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); + printk("lsr = %02x (jiff=%lu)...", lsr, jiffies); #endif set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); @@ -2986,8 +2255,9 @@ break; } #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); + printk("lsr = %02x (jiff=%lu)...done\n", lsr, jiffies); #endif + show_io = old_show_io; } /* @@ -2998,9 +2268,6 @@ struct async_struct * info = (struct async_struct *)tty->driver_data; struct serial_state *state = info->state; - if (serial_paranoia_check(info, tty->device, "rs_hangup")) - return; - state = info->state; rs_flush_buffer(tty); @@ -3036,12 +2303,8 @@ (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif } /* @@ -3114,14 +2377,10 @@ set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && @@ -3223,16 +2482,12 @@ } tty->driver_data = info; info->tty = tty; - if (serial_paranoia_check(info, tty->device, "rs_open")) - return -ENODEV; #ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count); #endif -#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif /* * This relies on lock_kernel() stuff so wants tidying for 2.5 @@ -3254,12 +2509,8 @@ (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif } /* @@ -3313,17 +2564,14 @@ int ret; unsigned long flags; - /* - * Return zero characters for ports not claimed by driver. - */ - if (state->type == PORT_UNKNOWN) { - return 0; /* ignore unused ports */ - } - ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", state->line, uart_config[state->type].name, - (state->port ? state->port : (long)state->iomem_base), - state->irq); + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } /* * Figure out the current RS-232 lines @@ -3334,7 +2582,6 @@ info->magic = SERIAL_MAGIC; info->port = state->port; info->flags = state->flags; - info->hub6 = state->hub6; info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; @@ -3389,13 +2636,13 @@ } static int rs_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) + int *eof, void *data) { int i, len = 0, l; off_t begin = 0; - len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", - serial_version, LOCAL_VERSTRING, serial_revdate); + len += sprintf(page, "serial: %s\n", + LOCAL_VERSTRING); for (i = 0; i < NR_PORTS && len < 4000; i++) { l = line_info(page + len, &rs_table[i]); len += l; @@ -3423,125 +2670,212 @@ */ /* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. + * The serial driver boot-time initialization code! */ -static char serial_options[] __initdata = -#ifdef CONFIG_HUB6 - " HUB-6" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_MANY_PORTS - " MANY_PORTS" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_MULTIPORT - " MULTIPORT" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_SHARE_IRQ - " SHARE_IRQ" -#define SERIAL_OPT -#endif -#ifdef CONFIG_SERIAL_DETECT_IRQ - " DETECT_IRQ" -#define SERIAL_OPT -#endif -#ifdef ENABLE_SERIAL_PCI - " SERIAL_PCI" -#define SERIAL_OPT -#endif -#ifdef ENABLE_SERIAL_PNP - " ISAPNP" -#define SERIAL_OPT +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + printk("pxa & ti16c754b serial driver\n"); + set_GPIO_IRQ_edge(7, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(24, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(25, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge(26, GPIO_RISING_EDGE); + + if (!request_mem_region(RAMSES_UARTA_PHYS, 16*4, "Ramses UART A")) + printk(KERN_ERR "unable to reserve region\n"); + else { + ramses_uarta = ioremap_nocache(RAMSES_UARTA_PHYS, 16*4); + if (!ramses_uarta) + printk(KERN_ERR "unable to map region\n"); + else { + //printk("ramses_uarta cookie is: %08x\n", (unsigned int) ramses_uarta); + rs_table[3].iomem_base = ramses_uarta; + } + } + if (!request_mem_region(RAMSES_UARTB_PHYS, 16*4, "Ramses UART B")) + printk(KERN_ERR "unable to reserve region\n"); + else { + ramses_uartb = ioremap_nocache(RAMSES_UARTB_PHYS, 16*4); + if (!ramses_uartb) + printk(KERN_ERR "unable to map region\n"); + else { + //printk("ramses_uartb cookie is: %08x\n", (unsigned int) ramses_uartb); + rs_table[4].iomem_base = ramses_uartb; + } + } + if (!request_mem_region(RAMSES_UARTC_PHYS, 16*4, "Ramses UART C")) + printk(KERN_ERR "unable to reserve region\n"); + else { + ramses_uartc = ioremap_nocache(RAMSES_UARTC_PHYS, 16*4); + if (!ramses_uartc) + printk(KERN_ERR "unable to map region\n"); + else { + //printk("ramses_uartc cookie is: %08x\n", (unsigned int) ramses_uartc); + rs_table[5].iomem_base = ramses_uartc; + } + } + if (!request_mem_region(RAMSES_UARTD_PHYS, 16*4, "Ramses UART D")) + printk(KERN_ERR "unable to reserve region\n"); + else { + ramses_uartd = ioremap_nocache(RAMSES_UARTD_PHYS, 16*4); + if (!ramses_uartd) + printk(KERN_ERR "unable to map region\n"); + else { + //printk("ramses_uartd cookie is: %08x\n", (unsigned int) ramses_uartd); + rs_table[6].iomem_base = ramses_uartd; + } + } + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; + } +#ifdef CONFIG_SERIAL_CONSOLE + /* + * The interrupt of the serial console port + * can't be shared. + */ + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } #endif -#ifdef ENABLE_SERIAL_ACPI - " SERIAL_ACPI" -#define SERIAL_OPT + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; +#if defined(CONFIG_DEVFS_FS) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; #endif -#ifdef SERIAL_OPT - " enabled\n"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.name_base = 0; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.put_char = rs_put_char; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; +#if defined(CONFIG_DEVFS_FS) + callout_driver.name = "cua/%d"; #else - " no serial options enabled\n"; + callout_driver.name = "cua"; #endif -#undef SERIAL_OPT + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; -static _INLINE_ void show_serial_version(void) -{ - printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, - serial_version, LOCAL_VERSTRING, serial_revdate, - serial_options); + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->port && check_region(state->port,8)) { + state->type = PORT_UNKNOWN; + continue; + } + } + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + printk(KERN_INFO"tts/%d at irq %d is a %s\n", + state->line, + state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } + return 0; } /* - * This routine detect the IRQ of a serial port by clearing OUT2 when - * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at - * each time, as long as no other device permanently request the IRQ. - * If no IRQ is detected, or multiple IRQ appear, this function returns 0. - * The variable "state" and the field "state->port" should not be null. + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). */ -static unsigned detect_uart_irq (struct serial_state * state) +int __init early_serial_setup(struct serial_struct *req) { - int irq; - unsigned long irqs; - unsigned char save_mcr, save_ier; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ - -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned char save_ICP=0; /* no warning */ - unsigned short ICP=0; - - if (state->flags & ASYNC_FOURPORT) { - ICP = (state->port & 0xFE0) | 0x01F; - save_ICP = inb_p(ICP); - outb_p(0x80, ICP); - (void) inb_p(ICP); - } -#endif - scr_info.magic = SERIAL_MAGIC; - scr_info.state = state; - scr_info.port = state->port; - scr_info.flags = state->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif - scr_info.io_type = state->io_type; - scr_info.iomem_base = state->iomem_base; - scr_info.iomem_reg_shift = state->iomem_reg_shift; + int i = req->line; - /* forget possible initially masked and pending IRQ */ - probe_irq_off(probe_irq_on()); - save_mcr = serial_inp(&scr_info, UART_MCR); - save_ier = serial_inp(&scr_info, UART_IER); - serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); - - irqs = probe_irq_on(); - serial_outp(&scr_info, UART_MCR, 0); - udelay (10); - if (state->flags & ASYNC_FOURPORT) { - serial_outp(&scr_info, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS); - } else { - serial_outp(&scr_info, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); - } - serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ - (void)serial_inp(&scr_info, UART_LSR); - (void)serial_inp(&scr_info, UART_RX); - (void)serial_inp(&scr_info, UART_IIR); - (void)serial_inp(&scr_info, UART_MSR); - serial_outp(&scr_info, UART_TX, 0xFF); - udelay (20); - irq = probe_irq_off(irqs); + printk("%s\n", __FUNCTION__); - serial_outp(&scr_info, UART_MCR, save_mcr); - serial_outp(&scr_info, UART_IER, save_ier); -#ifdef CONFIG_SERIAL_MANY_PORTS - if (state->flags & ASYNC_FOURPORT) - outb_p(save_ICP, ICP); -#endif - return (irq > 0)? irq : 0; + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); } /* @@ -3894,1762 +3228,10 @@ restore_flags(flags); } -int register_serial(struct serial_struct *req); -void unregister_serial(int line); - -#if (LINUX_VERSION_CODE > 0x20100) -EXPORT_SYMBOL(register_serial); -EXPORT_SYMBOL(unregister_serial); -#else -static struct symbol_table serial_syms = { -#include - X(register_serial), - X(unregister_serial), -#include -}; -#endif - - -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - -static void __devinit printk_pnp_dev_id(unsigned short vendor, - unsigned short device) -{ - printk("%c%c%c%x%x%x%x", - 'A' + ((vendor >> 2) & 0x3f) - 1, - 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, - 'A' + ((vendor >> 8) & 0x1f) - 1, - (device >> 4) & 0x0f, - device & 0x0f, - (device >> 12) & 0x0f, - (device >> 8) & 0x0f); -} - -static _INLINE_ int get_pci_port(struct pci_dev *dev, - struct pci_board *board, - struct serial_struct *req, - int idx) -{ - unsigned long port; - int base_idx; - int max_port; - int offset; - - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; - - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; - - /* Timedia/SUNIX uses a mixture of BARs and offsets */ - /* Ugh, this is ugly as all hell --- TYT */ - if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ - switch(idx) { - case 0: base_idx=0; - break; - case 1: base_idx=0; offset=8; - break; - case 2: base_idx=1; - break; - case 3: base_idx=1; offset=8; - break; - case 4: /* BAR 2*/ - case 5: /* BAR 3 */ - case 6: /* BAR 4*/ - case 7: base_idx=idx-2; /* BAR 5*/ - } - - /* Some Titan cards are also a little weird */ - if (dev->vendor == PCI_VENDOR_ID_TITAN && - (dev->device == PCI_DEVICE_ID_TITAN_400L || - dev->device == PCI_DEVICE_ID_TITAN_800L)) { - switch (idx) { - case 0: base_idx = 1; - break; - case 1: base_idx = 2; - break; - default: - base_idx = 4; - offset = 8 * (idx - 2); - } - - } - - /* HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ - if (dev->vendor == PCI_VENDOR_ID_HP && - dev->device == PCI_DEVICE_ID_HP_SAS) { - switch (dev->subsystem_device) { - case 0x104B: /* Maestro */ - if (idx == 3) idx++; - break; - case 0x1282: /* Everest / Longs Peak */ - if (idx > 0) idx++; - if (idx > 2) idx++; - break; - } - if (idx > 2) { - offset = 0x18; - } - } - - port = pci_resource_start(dev, base_idx) + offset; - - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); - - if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - req->port = port; - if (HIGH_BITS_OFFSET) - req->port_high = port >> HIGH_BITS_OFFSET; - else - req->port_high = 0; - return 0; - } - req->io_type = SERIAL_IO_MEM; - req->iomem_base = ioremap(port, board->uart_offset); - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return 0; -} - -static _INLINE_ int get_pci_irq(struct pci_dev *dev, - struct pci_board *board, - int idx) -{ - int base_idx; - - if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) - return dev->irq; - - base_idx = SPCI_FL_GET_IRQBASE(board->flags); - if (board->flags & SPCI_FL_IRQ_TABLE) - base_idx += idx; - - return PCI_IRQ_RESOURCE(dev, base_idx); -} - -/* - * Common enabler code shared by both PCI and ISAPNP probes - */ -static void __devinit start_pci_pnp_board(struct pci_dev *dev, - struct pci_board *board) -{ - int k, line; - struct serial_struct serial_req; - int base_baud; - - if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { - printk("serial: PNP device '"); - printk_pnp_dev_id(dev->vendor, dev->device); - printk("' prepare failed\n"); - return; - } - - if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { - printk("serial: PNP device '"); - printk_pnp_dev_id(dev->vendor, dev->device); - printk("' activate failed\n"); - return; - } - - /* - * Run the initialization function, if any - */ - if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) - return; - - /* - * Register the serial board in the array if we need to - * shutdown the board on a module unload or card removal - */ - if (DEACTIVATE_FUNC(dev) || board->init_fn) { - for (k=0; k < NR_PCI_BOARDS; k++) - if (serial_pci_board[k].dev == 0) - break; - if (k >= NR_PCI_BOARDS) - return; - serial_pci_board[k].board = *board; - serial_pci_board[k].dev = dev; - } - - base_baud = board->base_baud; - if (!base_baud) - base_baud = BASE_BAUD; - memset(&serial_req, 0, sizeof(serial_req)); - - for (k=0; k < board->num_ports; k++) { - serial_req.irq = get_pci_irq(dev, board, k); - if (get_pci_port(dev, board, &serial_req, k)) - break; - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; -#ifdef SERIAL_DEBUG_PCI - printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", - serial_req.port, serial_req.irq, serial_req.io_type); -#endif - line = register_serial(&serial_req); - if (line < 0) - break; - rs_table[line].baud_base = base_baud; - rs_table[line].dev = dev; - } -} -#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ - -#ifdef ENABLE_SERIAL_PCI -/* - * Some PCI serial cards using the PLX 9050 PCI interface chip require - * that the card interrupt be explicitly enabled or disabled. This - * seems to be mainly needed on card using the PLX which also use I/O - * mapped memory. - */ -static int __devinit -pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u8 data, *p, irq_config; - int pci_config; - - irq_config = 0x41; - pci_config = PCI_COMMAND_MEMORY; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - irq_config = 0x43; - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the deep - * FIFOs - */ - irq_config = 0x5b; - pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; - } - - pci_read_config_byte(dev, PCI_COMMAND, &data); - - if (enable) - pci_write_config_byte(dev, PCI_COMMAND, - data | pci_config); - - /* enable/disable interrupts */ - p = ioremap(pci_resource_start(dev, 0), 0x80); - writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); - iounmap(p); - if (!enable) - pci_write_config_byte(dev, PCI_COMMAND, - data & ~pci_config); - return 0; -} /* - * SIIG serial cards have an PCI interface chip which also controls - * the UART clocking frequency. Each UART can be clocked independently - * (except cards equiped with 4 UARTs) and initial clocking settings - * are stored in the EEPROM chip. It can cause problems because this - * version of serial driver doesn't support differently clocked UART's - * on single PCI card. To prevent this, initialization functions set - * high frequency clocking for all UART's on given card. It is safe (I - * hope) because it doesn't touch EEPROM settings to prevent conflicts - * with other OSes (like M$ DOS). - * - * SIIG support added by Andrey Panin , 10/1999 - * - * There is two family of SIIG serial cards with different PCI - * interface chip and different configuration methods: - * - 10x cards have control registers in IO and/or memory space; - * - 20x cards have control registers in standard PCI configuration space. - * - * SIIG initialization functions exported for use by parport_serial.c module. - */ - -#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) - -int __devinit -pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u16 data, *p; - - if (!enable) return 0; - - p = ioremap(pci_resource_start(dev, 0), 0x80); - - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; - } - - writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); - iounmap(p); - return 0; -} -EXPORT_SYMBOL(pci_siig10x_fn); - -#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) - -int __devinit -pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - u8 data; - - if (!enable) return 0; - - /* Change clock frequency for the first UART. */ - pci_read_config_byte(dev, 0x6f, &data); - pci_write_config_byte(dev, 0x6f, data & 0xef); - - /* If this card has 2 UART, we have to do the same with second UART. */ - if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || - ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { - pci_read_config_byte(dev, 0x73, &data); - pci_write_config_byte(dev, 0x73, data & 0xef); - } - return 0; -} -EXPORT_SYMBOL(pci_siig20x_fn); - -/* Added for EKF Intel i960 serial boards */ -static int __devinit -pci_inteli960ni_fn(struct pci_dev *dev, - struct pci_board *board, - int enable) -{ - unsigned long oldval; - - if (!(pci_get_subdevice(dev) & 0x1000)) - return(-1); - - if (!enable) /* is there something to deinit? */ - return(0); - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n", - (unsigned long) board->subdevice); -#endif - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void*) &oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return(-1); - } - return(0); -} - -/* - * Timedia has an explosion of boards, and to avoid the PCI table from - * growing *huge*, we use this function to collapse some 70 entries - * in the PCI table into one, for sanity's and compactness's sake. - */ -static unsigned short timedia_single_port[] = { - 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; -static unsigned short timedia_dual_port[] = { - 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, - 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, - 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, - 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, - 0xD079, 0 }; -static unsigned short timedia_quad_port[] = { - 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, - 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, - 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, - 0xB157, 0 }; -static unsigned short timedia_eight_port[] = { - 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, - 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; -static struct timedia_struct { - int num; - unsigned short *ids; -} timedia_data[] = { - { 1, timedia_single_port }, - { 2, timedia_dual_port }, - { 4, timedia_quad_port }, - { 8, timedia_eight_port }, - { 0, 0 } -}; - -static int __devinit -pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - int i, j; - unsigned short *ids; - - if (!enable) - return 0; - - for (i=0; timedia_data[i].num; i++) { - ids = timedia_data[i].ids; - for (j=0; ids[j]; j++) { - if (pci_get_subdevice(dev) == ids[j]) { - board->num_ports = timedia_data[i].num; - return 0; - } - } - } - return 0; -} - -/* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. - */ -static int __devinit -pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable) -{ - if (!enable) - return 0; - - switch (dev->subsystem_device) { - case 0x1049: /* Prelude Diva 1 */ - case 0x1223: /* Superdome */ - case 0x1226: /* Keystone */ - case 0x1282: /* Everest / Longs Peak */ - board->num_ports = 3; - break; - case 0x104A: /* Prelude Diva 2 */ - board->num_ports = 2; - break; - case 0x104B: /* Maestro */ - board->num_ports = 4; - break; - case 0x1227: /* Powerbar */ - board->num_ports = 1; - break; - } - - return 0; -} - -static int __devinit -pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) -{ - __set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); - return 0; -} - -/* - * This is the configuration table for all of the PCI serial boards - * which we support. It is directly indexed by the pci_board_num_t enum - * value, which is encoded in the pci_device_id PCI probe table's - * driver_data member. - */ -enum pci_board_num_t { - pbn_b0_1_115200, - pbn_default = 0, - - pbn_b0_2_115200, - pbn_b0_4_115200, - - pbn_b0_1_921600, - pbn_b0_2_921600, - pbn_b0_4_921600, - - pbn_b0_bt_1_115200, - pbn_b0_bt_2_115200, - pbn_b0_bt_1_460800, - pbn_b0_bt_2_460800, - pbn_b0_bt_2_921600, - - pbn_b1_1_115200, - pbn_b1_2_115200, - pbn_b1_4_115200, - pbn_b1_8_115200, - - pbn_b1_2_921600, - pbn_b1_4_921600, - pbn_b1_8_921600, - - pbn_b1_2_1382400, - pbn_b1_4_1382400, - pbn_b1_8_1382400, - - pbn_b2_1_115200, - pbn_b2_8_115200, - pbn_b2_4_460800, - pbn_b2_8_460800, - pbn_b2_16_460800, - pbn_b2_4_921600, - pbn_b2_8_921600, - - pbn_b2_bt_1_115200, - pbn_b2_bt_2_115200, - pbn_b2_bt_4_115200, - pbn_b2_bt_2_921600, - - pbn_panacom, - pbn_panacom2, - pbn_panacom4, - pbn_plx_romulus, - pbn_oxsemi, - pbn_timedia, - pbn_intel_i960, - pbn_sgi_ioc3, - pbn_hp_diva, -#ifdef CONFIG_DDB5074 - pbn_nec_nile4, -#endif -#if 0 - pbn_dci_pccom8, -#endif - pbn_xircom_combo, - - pbn_siig10x_0, - pbn_siig10x_1, - pbn_siig10x_2, - pbn_siig10x_4, - pbn_siig20x_0, - pbn_siig20x_2, - pbn_siig20x_4, - - pbn_computone_4, - pbn_computone_6, - pbn_computone_8, -}; - -static struct pci_board pci_boards[] __devinitdata = { - /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset - */ - - /* Generic serial board, pbn_b0_1_115200, pbn_default */ - { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, - pbn_default */ - - { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ - { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ - - { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ - { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ - { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ - - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b0_bt_2_921600 */ - - { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ - { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ - { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ - { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ - - { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ - { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ - { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ - - { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ - { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ - { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ - - { SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ - { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ - { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ - { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ - { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ - { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ - { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ - - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ - - { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ - 0x20, 2, pci_plx9050_fn, 0x03 }, - /* This board uses the size of PCI Base region 0 to - * signal now many ports are available */ - { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ - { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ - 0, 0, pci_timedia_fn }, - /* EKF addition for i960 Boards form EKF with serial port */ - { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, - { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ - 1, 458333, 0, 0, 0, 0x20178 }, - { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0}, /* pbn_hp_diva */ -#ifdef CONFIG_DDB5074 - /* - * NEC Vrc-5074 (Nile 4) builtin UART. - * Conditionally compiled in since this is a motherboard device. - */ - { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ - 64, 3, NULL, 0x300 }, -#endif -#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ - { SPCI_FL_BASE3, 8, 115200, 8 }, -#endif - { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ - 0, 0, pci_xircom_fn }, - - { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ - 0, 0, pci_siig20x_fn }, - - { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ - 0x40, 2, NULL, 0x200 }, -}; - -/* - * Given a complete unknown PCI device, try to use some heuristics to - * guess what the configuration might be, based on the pitiful PCI - * serial specs. Returns 0 on success, 1 on failure. - */ -static int __devinit serial_pci_guess_board(struct pci_dev *dev, - struct pci_board *board) -{ - int num_iomem = 0, num_port = 0, first_port = -1; - int i; - - /* - * If it is not a communications device or the programming - * interface is greater than 6, give up. - * - * (Should we try to make guesses for multiport serial devices - * later?) - */ - if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && - ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || - (dev->class & 0xff) > 6) - return 1; - - for (i=0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { - num_port++; - if (first_port == -1) - first_port = i; - } - if (IS_PCI_REGION_IOMEM(dev, i)) - num_iomem++; - } - - /* - * If there is exactly one port of 8 bytes, use it. - */ - if (num_port == 1 && pci_resource_len(dev, first_port) == 8) { - board->flags = first_port; - return 0; - } - - /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. - */ - if (num_iomem <= 1 && num_port == 1) { - board->flags = first_port; - return 0; - } - return 1; -} - -static int __devinit serial_init_one(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - struct pci_board *board, tmp; - int rc; - - board = &pci_boards[ent->driver_data]; - - rc = pci_enable_device(dev); - if (rc) return rc; - - if (ent->driver_data == pbn_default && - serial_pci_guess_board(dev, board)) - return -ENODEV; - else if (serial_pci_guess_board(dev, &tmp) == 0) { - printk(KERN_INFO "Redundant entry in serial pci_table. " - "Please send the output of\n" - "lspci -vv, this message (%04x,%04x,%04x,%04x)\n" - "and the manufacturer and name of " - "serial board or modem board\n" - "to serial-pci-info@lists.sourceforge.net.\n", - dev->vendor, dev->device, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } - - start_pci_pnp_board(dev, board); - - return 0; -} - -static void __devexit serial_remove_one(struct pci_dev *dev) -{ - int i; - - /* - * Iterate through all of the ports finding those that belong - * to this PCI device. - */ - for(i = 0; i < NR_PORTS; i++) { - if (rs_table[i].dev != dev) - continue; - unregister_serial(i); - rs_table[i].dev = 0; - } - /* - * Now execute any board-specific shutdown procedure - */ - for (i=0; i < NR_PCI_BOARDS; i++) { - struct pci_board_inst *brd = &serial_pci_board[i]; - - if (serial_pci_board[i].dev != dev) - continue; - if (brd->board.init_fn) - (brd->board.init_fn)(brd->dev, &brd->board, 0); - if (DEACTIVATE_FUNC(brd->dev)) - (DEACTIVATE_FUNC(brd->dev))(brd->dev); - serial_pci_board[i].dev = 0; - } -} - - -static struct pci_device_id serial_pci_tbl[] __devinitdata = { - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, - pbn_b1_2_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, - pbn_b1_4_921600 }, - - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_1_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - /* VScom SPCOM800, from sl@s.pl */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, - pbn_panacom }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom4 }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom2 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, - pbn_b2_8_460800 }, - /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ - /* (Exoray@isys.ca) */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, 0, 0, - pbn_plx_romulus }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - - /* Digitan DS560-558, from jimd@esoft.com */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, - - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ - { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, }, - - /* Titan Electronic cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, - /* The 400L and 800L have a custom hack in get_pci_port */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 4, 921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 8, 921600 }, - - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, - - /* Computone devices submitted by Doug McNash dmcnash@computone.com */ - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - 0, 0, pbn_computone_4 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - 0, 0, pbn_computone_8 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - 0, 0, pbn_computone_6 }, - - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, - - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_460800 }, - - /* RAStel 2 port modem, gerg@moreton.com.au */ - { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, - 0xE4BF, PCI_ANY_ID, 0, 0, - pbn_intel_i960 }, - - /* Xircom Cardbus/Ethernet combos */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_xircom_combo }, - - /* - * Untested PCI modems, sent in from various folks... - */ - - /* Elsa Model 56K PCI Modem, from Andreas Rath */ - { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, 0, 0, - pbn_sgi_ioc3 }, - - /* HP Diva card */ - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_SAS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_hp_diva }, - { PCI_VENDOR_ID_HP, 0x1290, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - -#ifdef CONFIG_DDB5074 - /* - * NEC Vrc-5074 (Nile 4) builtin UART. - * Conditionally compiled in since this is a motherboard device. - */ - { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_nec_nile4 }, -#endif - -#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom8 }, -#endif - - { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, - { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, serial_pci_tbl); - -static struct pci_driver serial_pci_driver = { - name: "serial", - probe: serial_init_one, - remove: __devexit_p(serial_remove_one), - id_table: serial_pci_tbl, -}; - - -/* - * Query PCI space for known serial boards - * If found, add them to the PCI device space in rs_table[] - * - * Accept a maximum of eight boards - * - */ -static void __devinit probe_serial_pci(void) -{ -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Entered probe_serial_pci()\n"); -#endif - - /* Register call PCI serial devices. Null out - * the driver name upon failure, as a signal - * not to attempt to unregister the driver later - */ - if (pci_module_init (&serial_pci_driver) != 0) - serial_pci_driver.name = ""; - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); -#endif - return; -} - -#endif /* ENABLE_SERIAL_PCI */ - -#ifdef ENABLE_SERIAL_PNP - -struct pnp_board { - unsigned short vendor; - unsigned short device; -}; - -static struct pnp_board pnp_devices[] __devinitdata = { - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, - /* Anchor Datacomm BV */ - /* SXPro 144 External Data Fax Modem Plug & Play */ - { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) }, - /* SXPro 288 External Data Fax Modem Plug & Play */ - { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) }, - /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) }, - /* AZT3005 PnP SOUND DEVICE */ - { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, - /* Boca Research */ - /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, - /* Boca Research 33,600 ACF Modem */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, - /* Computer Peripherals Inc */ - /* EuroViVa CommCenter-33.6 SP PnP */ - { ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) }, - /* Creative Labs */ - /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) }, - /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) }, - /* Creative */ - /* Creative Modem Blaster Flash56 DI5601-1 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) }, - /* Creative Modem Blaster V.90 DI5660 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) }, - /* FUJITSU */ - /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) }, - /* Fujitsu FMV-FX431 Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) }, - /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) }, - /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) }, - /* Hayes */ - /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) }, - /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) }, - /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) }, - /* Hayes Accura 56K Fax Modem PnP */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) }, - /* Hayes 288, V.34 + FAX */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) }, - /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) }, - /* IBM */ - /* IBM Thinkpad 701 Internal Modem Voice */ - { ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) }, - /* Intertex */ - /* Intertex 28k8 33k6 Voice EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) }, - /* Intertex 33k6 56k Voice EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) }, - /* Intertex 28k8 33k6 Voice SP INT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) }, - /* Kortex International */ - /* KORTEX 28800 Externe PnP */ - { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) }, - /* KXPro 33.6 Vocal ASVD PnP */ - { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) }, - /* Lasat */ - /* LASAT Internet 33600 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) }, - /* Lasat Safire 560 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) }, - /* Lasat Safire 336 PnP */ - { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) }, - /* Microcom, Inc. */ - /* Microcom TravelPorte FAST V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) }, - /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) }, - /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) }, - /* Microcom DeskPorte 28.8P Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, - /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) }, - /* Motorola */ - /* Motorola BitSURFR Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) }, - /* Motorola TA210 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) }, - /* Motorola HMTA 200 (ISDN) Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) }, - /* Motorola BitSURFR Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) }, - /* Motorola Lifestyle 28.8 Internal */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) }, - /* Motorola V.3400 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) }, - /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) }, - /* Motorola Power 28.8 V.34 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) }, - /* Motorola ModemSURFR External 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) }, - /* Motorola Premier 33.6 Desktop Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) }, - /* Motorola VoiceSURFR 56K External PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) }, - /* Motorola ModemSURFR 56K External PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) }, - /* Motorola ModemSURFR 56K Internal PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) }, - /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) }, - /* Motorola Premier 33.6 Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) }, - /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) }, - /* Motorola VoiceSURFR 56K Internal PnP */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) }, - /* Com 1 */ - /* Deskline K56 Phone System PnP */ - { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) }, - /* PC Rider K56 Phone System PnP */ - { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) }, - /* Pace 56 Voice Internal Plug & Play Modem */ - { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) }, - /* Generic */ - /* Generic standard PC COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, - /* Generic 16550A-compatible COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, - /* Compaq 14400 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) }, - /* Compaq 2400/9600 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) }, - /* Dial-Up Networking Serial Cable between 2 PCs */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) }, - /* Dial-Up Networking Parallel Cable between 2 PCs */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) }, - /* Standard 28800 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) }, - /* Standard Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) }, - /* Standard 9600 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) }, - /* Standard 14400 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) }, - /* Standard 28800 bps Modem*/ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) }, - /* Standard 28800 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) }, - /* Standard 9600 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) }, - /* Standard 14400 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) }, - /* Standard 28800 bps Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) }, - /* Standard Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) }, - /* Standard PCMCIA Card Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) }, - /* Rockwell */ - /* Modular Technology */ - /* Rockwell 33.6 DPF Internal PnP */ - /* Modular Technology 33.6 Internal PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) }, - /* Kortex International */ - /* KORTEX 14400 Externe PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) }, - /* Viking Components, Inc */ - /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) }, - /* Rockwell */ - /* British Telecom */ - /* Modular Technology */ - /* Rockwell 33.6 DPF External PnP */ - /* BT Prologue 33.6 External PnP */ - /* Modular Technology 33.6 External PnP */ - { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) }, - /* Viking 56K FAX INT */ - { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) }, - /* SupraExpress 28.8 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) }, - /* Phoebe Micro */ - /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, - /* 3Com Corp. */ - /* Gateway Telepath IIvi 33.6 */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) }, - /* Sportster Vi 14.4 PnP FAX Voicemail */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) }, - /* U.S. Robotics 33.6K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) }, - /* U.S. Robotics 56K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) }, - /* U.S. Robotics 56K FAX INT */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) }, - /* U.S. Robotics 56K Voice EXT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) }, - /* U.S. Robotics 56K Voice INT PnP */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) }, - /* U.S. Robotics 56K Message */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) }, - /* U.S. Robotics 56K FAX EXT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) }, - /* U.S. Robotics 56K FAX INT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) }, - /* U.S. Robotics 56K Voice EXT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) }, - /* U.S. Robotics 56K Voice INT PnP*/ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) }, - { 0, } -}; - -static inline void avoid_irq_share(struct pci_dev *dev) -{ - int i, map = 0x1FF8; - struct serial_state *state = rs_table; - struct isapnp_irq *irq; - struct isapnp_resources *res = dev->sysdata; - - for (i = 0; i < NR_PORTS; i++) { - if (state->type != PORT_UNKNOWN) - clear_bit(state->irq, &map); - state++; - } - - for ( ; res; res = res->alt) - for(irq = res->irq; irq; irq = irq->next) - irq->map = map; -} - -static char *modem_names[] __devinitdata = { - "MODEM", "Modem", "modem", "FAX", "Fax", "fax", - "56K", "56k", "K56", "33.6", "28.8", "14.4", - "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", - "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 -}; - -static int __devinit check_name(char *name) -{ - char **tmp = modem_names; - - while (*tmp) { - if (strstr(name, *tmp)) - return 1; - tmp++; - } - return 0; -} - -static inline int check_compatible_id(struct pci_dev *dev) -{ - int i; - for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) - if ((dev->vendor_compatible[i] == - ISAPNP_VENDOR('P', 'N', 'P')) && - (swab16(dev->device_compatible[i]) >= 0xc000) && - (swab16(dev->device_compatible[i]) <= 0xdfff)) - return 0; - return 1; -} - -/* - * Given a complete unknown ISA PnP device, try to use some heuristics to - * detect modems. Currently use such heuristic set: - * - dev->name or dev->bus->name must contain "modem" substring; - * - device must have only one IO region (8 byte long) with base adress - * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. - * - * Such detection looks very ugly, but can detect at least some of numerous - * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] - * table. - */ -static int _INLINE_ serial_pnp_guess_board(struct pci_dev *dev, - struct pci_board *board) -{ - struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; - struct isapnp_resources *resa; - - if (!(check_name(dev->name) || check_name(dev->bus->name)) && - !(check_compatible_id(dev))) - return 1; - - if (!res || res->next) - return 1; - - for (resa = res->alt; resa; resa = resa->alt) { - struct isapnp_port *port; - for (port = res->port; port; port = port->next) - if ((port->size == 8) && - ((port->min == 0x2f8) || - (port->min == 0x3f8) || - (port->min == 0x2e8) || - (port->min == 0x3e8))) - return 0; - } - - return 1; -} - -static void __devinit probe_serial_pnp(void) -{ - struct pci_dev *dev = NULL; - struct pnp_board *pnp_board; - struct pci_board board; - -#ifdef SERIAL_DEBUG_PNP - printk("Entered probe_serial_pnp()\n"); -#endif - if (!isapnp_present()) { -#ifdef SERIAL_DEBUG_PNP - printk("Leaving probe_serial_pnp() (no isapnp)\n"); -#endif - return; - } - - isapnp_for_each_dev(dev) { - if (dev->active) - continue; - - memset(&board, 0, sizeof(board)); - board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT; - board.num_ports = 1; - board.base_baud = 115200; - - for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++) - if ((dev->vendor == pnp_board->vendor) && - (dev->device == pnp_board->device)) - break; - - if (pnp_board->vendor) { - /* Special case that's more efficient to hardcode */ - if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') && - pnp_board->device == ISAPNP_DEVICE(0x1021))) - board.flags |= SPCI_FL_NO_SHIRQ; - } else { - if (serial_pnp_guess_board(dev, &board)) - continue; - } - - if (board.flags & SPCI_FL_NO_SHIRQ) - avoid_irq_share(dev); - start_pci_pnp_board(dev, &board); - } - -#ifdef SERIAL_DEBUG_PNP - printk("Leaving probe_serial_pnp() (probe finished)\n"); -#endif - return; -} - -#endif /* ENABLE_SERIAL_PNP */ - -/* - * The serial driver boot-time initialization code! - */ -static int __init rs_init(void) -{ - int i; - struct serial_state * state; - - init_bh(SERIAL_BH, do_serial_bh); - init_timer(&serial_timer); - serial_timer.function = rs_timer; - mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); - - for (i = 0; i < NR_IRQS; i++) { - IRQ_ports[i] = 0; - IRQ_timeout[i] = 0; -#ifdef CONFIG_SERIAL_MULTIPORT - memset(&rs_multiport[i], 0, - sizeof(struct rs_multiport_struct)); -#endif - } -#ifdef CONFIG_SERIAL_CONSOLE - /* - * The interrupt of the serial console port - * can't be shared. - */ - if (sercons.flags & CON_CONSDEV) { - for(i = 0; i < NR_PORTS; i++) - if (i != sercons.index && - rs_table[i].irq == rs_table[sercons.index].irq) - rs_table[i].irq = 0; - } -#endif - show_serial_version(); - - /* Initialize the tty_driver structure */ - - memset(&serial_driver, 0, sizeof(struct tty_driver)); - serial_driver.magic = TTY_DRIVER_MAGIC; -#if (LINUX_VERSION_CODE > 0x20100) - serial_driver.driver_name = "serial"; -#endif -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - serial_driver.name = "tts/%d"; -#else - serial_driver.name = "ttyS"; -#endif - serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; - serial_driver.name_base = SERIAL_DEV_OFFSET; - serial_driver.num = NR_PORTS; - serial_driver.type = TTY_DRIVER_TYPE_SERIAL; - serial_driver.subtype = SERIAL_TYPE_NORMAL; - serial_driver.init_termios = tty_std_termios; - serial_driver.init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; - serial_driver.refcount = &serial_refcount; - serial_driver.table = serial_table; - serial_driver.termios = serial_termios; - serial_driver.termios_locked = serial_termios_locked; - - serial_driver.open = rs_open; - serial_driver.close = rs_close; - serial_driver.write = rs_write; - serial_driver.put_char = rs_put_char; - serial_driver.flush_chars = rs_flush_chars; - serial_driver.write_room = rs_write_room; - serial_driver.chars_in_buffer = rs_chars_in_buffer; - serial_driver.flush_buffer = rs_flush_buffer; - serial_driver.ioctl = rs_ioctl; - serial_driver.throttle = rs_throttle; - serial_driver.unthrottle = rs_unthrottle; - serial_driver.set_termios = rs_set_termios; - serial_driver.stop = rs_stop; - serial_driver.start = rs_start; - serial_driver.hangup = rs_hangup; -#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ - serial_driver.break_ctl = rs_break; -#endif -#if (LINUX_VERSION_CODE >= 131343) - serial_driver.send_xchar = rs_send_xchar; - serial_driver.wait_until_sent = rs_wait_until_sent; - serial_driver.read_proc = rs_read_proc; -#endif - - /* - * The callout device is just like normal device except for - * major number and the subtype code. - */ - callout_driver = serial_driver; -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - callout_driver.name = "cua/%d"; -#else - callout_driver.name = "cua"; -#endif - callout_driver.major = TTYAUX_MAJOR; - callout_driver.subtype = SERIAL_TYPE_CALLOUT; -#if (LINUX_VERSION_CODE >= 131343) - callout_driver.read_proc = 0; - callout_driver.proc_entry = 0; -#endif - - if (tty_register_driver(&serial_driver)) - panic("Couldn't register serial driver\n"); - if (tty_register_driver(&callout_driver)) - panic("Couldn't register callout driver\n"); - - for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - state->magic = SSTATE_MAGIC; - state->line = i; - state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; - state->callout_termios = callout_driver.init_termios; - state->normal_termios = serial_driver.init_termios; - state->icount.cts = state->icount.dsr = - state->icount.rng = state->icount.dcd = 0; - state->icount.rx = state->icount.tx = 0; - state->icount.frame = state->icount.parity = 0; - state->icount.overrun = state->icount.brk = 0; - state->irq = irq_cannonicalize(state->irq); - if (state->hub6) - state->io_type = SERIAL_IO_HUB6; - if (state->port && check_region(state->port,8)) { - state->type = PORT_UNKNOWN; - continue; - } -#ifdef CONFIG_MCA - if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) - continue; -#endif - if (state->flags & ASYNC_BOOT_AUTOCONF) { - state->type = PORT_UNKNOWN; - autoconfig(state); - } - } - for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - if (state->type == PORT_UNKNOWN) - continue; - if ( (state->flags & ASYNC_BOOT_AUTOCONF) - && (state->flags & ASYNC_AUTO_IRQ) - && (state->port != 0 || state->iomem_base != 0)) - state->irq = detect_uart_irq(state); - if (state->io_type == SERIAL_IO_MEM) { - printk(KERN_INFO"ttyS%02d%s at 0x%p (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, - (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", - state->iomem_base, state->irq, - uart_config[state->type].name); - } - else { - printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, - (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", - state->port, state->irq, - uart_config[state->type].name); - } - tty_register_devfs(&serial_driver, 0, - serial_driver.minor_start + state->line); - tty_register_devfs(&callout_driver, 0, - callout_driver.minor_start + state->line); - } -#ifdef ENABLE_SERIAL_PCI - probe_serial_pci(); -#endif -#ifdef ENABLE_SERIAL_PNP - probe_serial_pnp(); -#endif - return 0; -} - -/* - * This is for use by architectures that know their serial console - * attributes only at run time. Not to be invoked after rs_init(). - */ -int __init early_serial_setup(struct serial_struct *req) -{ - int i = req->line; - - if (i >= NR_IRQS) - return(-ENOENT); - rs_table[i].magic = 0; - rs_table[i].baud_base = req->baud_base; - rs_table[i].port = req->port; - if (HIGH_BITS_OFFSET) - rs_table[i].port += (unsigned long) req->port_high << - HIGH_BITS_OFFSET; - rs_table[i].irq = req->irq; - rs_table[i].flags = req->flags; - rs_table[i].close_delay = req->close_delay; - rs_table[i].io_type = req->io_type; - rs_table[i].hub6 = req->hub6; - rs_table[i].iomem_base = req->iomem_base; - rs_table[i].iomem_reg_shift = req->iomem_reg_shift; - rs_table[i].type = req->type; - rs_table[i].xmit_fifo_size = req->xmit_fifo_size; - rs_table[i].custom_divisor = req->custom_divisor; - rs_table[i].closing_wait = req->closing_wait; - return(0); -} - -/* * register_serial and unregister_serial allows for 16x50 serial ports to be * configured at run-time, to support PCMCIA modems. */ @@ -5734,11 +3316,8 @@ } restore_flags(flags); - if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) - state->irq = detect_uart_irq(state); - printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", - state->line + SERIAL_DEV_OFFSET, + state->line, state->iomem_base ? "iomem" : "port", state->iomem_base ? (unsigned long)state->iomem_base : state->port, state->irq, uart_config[state->type].name); @@ -5746,7 +3325,7 @@ serial_driver.minor_start + state->line); tty_register_devfs(&callout_driver, 0, callout_driver.minor_start + state->line); - return state->line + SERIAL_DEV_OFFSET; + return state->line; } /** @@ -5785,7 +3364,6 @@ int i; struct async_struct *info; - /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ del_timer_sync(&serial_timer); save_flags(flags); cli(); remove_bh(SERIAL_BH); @@ -5803,41 +3381,31 @@ kfree(info); } if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { -#ifdef CONFIG_SERIAL_RSA - if (rs_table[i].type == PORT_RSA) - release_region(rs_table[i].port + - UART_RSA_BASE, 16); - else -#endif release_region(rs_table[i].port, 8); } -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - if (rs_table[i].iomem_base) - iounmap(rs_table[i].iomem_base); -#endif - } -#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) - for (i=0; i < NR_PCI_BOARDS; i++) { - struct pci_board_inst *brd = &serial_pci_board[i]; - - if (serial_pci_board[i].dev == 0) - continue; - if (brd->board.init_fn) - (brd->board.init_fn)(brd->dev, &brd->board, 0); - if (DEACTIVATE_FUNC(brd->dev)) - (DEACTIVATE_FUNC(brd->dev))(brd->dev); } -#endif if (tmp_buf) { unsigned long pg = (unsigned long) tmp_buf; tmp_buf = NULL; free_page(pg); } -#ifdef ENABLE_SERIAL_PCI - if (serial_pci_driver.name[0]) - pci_unregister_driver (&serial_pci_driver); -#endif + if (ramses_uarta) { + iounmap(ramses_uarta); + release_mem_region(RAMSES_UARTA_PHYS, 16*4); + } + if (ramses_uartb) { + iounmap(ramses_uartb); + release_mem_region(RAMSES_UARTB_PHYS, 16*4); + } + if (ramses_uartc) { + iounmap(ramses_uartc); + release_mem_region(RAMSES_UARTC_PHYS, 16*4); + } + if (ramses_uartd) { + iounmap(ramses_uartd); + release_mem_region(RAMSES_UARTD_PHYS, 16*4); + } } module_init(rs_init); @@ -5946,7 +3514,7 @@ static struct async_struct *info; struct serial_state *state; unsigned cval; - int baud = 9600; + int baud = 115200; int bits = 8; int parity = 'n'; int doflow = 0; @@ -5954,6 +3522,8 @@ int quot = 0; char *s; + printk("%s\n", __FUNCTION__); + if (options) { baud = simple_strtoul(options, NULL, 10); s = options; @@ -6028,19 +3598,12 @@ info->state = state; info->port = state->port; info->flags = state->flags; -#ifdef CONFIG_HUB6 - info->hub6 = state->hub6; -#endif info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; quot = state->baud_base / baud; cval = cflag & (CSIZE | CSTOPB); -#if defined(__powerpc__) || defined(__alpha__) - cval >>= 8; -#else /* !__powerpc__ && !__alpha__ */ cval >>= 4; -#endif /* !__powerpc__ && !__alpha__ */ if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) @@ -6082,10 +3645,15 @@ */ void __init serial_console_init(void) { + printk("%s\n", __FUNCTION__); + register_console(&sercons); } #endif +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + /* Local variables: compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" --- /dev/null +++ linux-2.4.21/drivers/char/sysctl.c @@ -0,0 +1,948 @@ +/* + * /proc/sys-board - Interface to the 16 bit latch and other + * ramses-related hardware settings + * + * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH + * written by H.Schurig + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../drivers/misc/ucb1x00.h" + +//#define DEBUG +//define CPLD_LED 1 +//define POTI 1 + +/* + * This is the number for the "board" entry in /proc/sys: + */ +#define RAMSES_SYSCTL 1312 + +/* + * These are the numbers for the entries in /etc/sys/board + */ +enum { + CTL_NAME=991, + CTL_CPLD_VERSION, + CTL_BOOTLOADER_CRC, + CTL_LINUX_CRC, + CTL_LED_BLUE, + CTL_LED_ORANGE, + CTL_UART, + CTL_MMC, + CTL_POWEROFF, + CTL_GSM_POWER, + CTL_GSM_RESET, + CTL_SCANNER_POWER, + CTL_SCANNER_WAKE, + CTL_SCANNER_TRIG, + CTL_SCANNER_BEAM, + CTL_KEY_SCAN, + CTL_KEY_SUSPEND, + CTL_KEY_OFF, + CTL_USBBUS_POWER, + CTL_USBCHIP_POWER, +#ifdef CPLD_LED + CTL_LED_CPLD, + CTL_LED_CPLD_RED, +#endif + CTL_LCD_VCC, + CTL_LCD_DISPOFF, + CTL_LCD_BLIGHT, +#ifdef DEBUG + CTL_LCD_PWM0, + CTL_LCD_PWM1, +#endif + CTL_LCD_BRIGHTNESS, + CTL_LCD_CONTRAST, + CTL_LCD_FBTURN, + CTL_LCD_TYPE, + CTL_CONTROL_SHADOW, + CTL_COREVOLT, +#ifdef POTI + CTL_LCD_POTI_NINC, + CTL_LCD_POTI_NCS, + CTL_LCD_POTI_UP, +#endif +#ifdef CONFIG_MCP_UCB1400_TS + CTL_ADC0, + CTL_ADC1, + CTL_ADC2, + CTL_ADC3, +#else +#error NO UCB +#endif + CTL_CHG_STS, + CTL_WALL_IN, + CTL_BATT_TMP, + CTL_BATT_LMD, + CTL_BATT_VSB, + CTL_BATT_RCAC, + CTL_BATT_CACT, + CTL_BATT_SAE, + CTL_BATT_DCR, +}; + +static const char ramses_board_name[] = "ramses"; +static int dummy_int = 0; +static char dummy_str[80]; + + + +/******************************************************************/ +/* ADC communication */ +/******************************************************************/ + + +#ifdef CONFIG_MCP_UCB1400_TS +static int adc_get(int channel) +{ + int val; + struct ucb1x00 *ucb = ucb1x00_get(); + + ucb1x00_adc_enable(ucb); + val = ucb1x00_adc_read(ucb, channel, 0); + ucb1x00_adc_disable(ucb); + + return val; +} +#endif + + + +static int +ramses_sysctl_handler(ctl_table * ctl, int write, struct file *filp, + void *buffer, size_t * lenp) +{ + int *valp = ctl->data; + int val; + int ret; + unsigned crc; + void *flash; + +#ifdef DEBUG + printk("ramses_control_shadow: %04x\n", ramses_control_shadow); +#endif + + // Update parameters from the real registers + switch (ctl->ctl_name) { + case CTL_CPLD_VERSION: + sprintf(dummy_str,"20%02ld-%02ld-%02ld.%ld\n", + RAMSES_CPLD_YEAR & 0xff, + RAMSES_CPLD_MONTH & 0xff, + RAMSES_CPLD_DAY & 0xff, + RAMSES_CPLD_REV & 0xff); + return proc_dostring(ctl,write,filp,buffer,lenp); + + case CTL_BOOTLOADER_CRC: + flash = ioremap_nocache(RAMSES_FLASH_PHYS, 0x40000); + crc = ether_crc_le(0x40000, flash); + iounmap(flash); + sprintf(dummy_str,"%08x", crc); + return proc_dostring(ctl,write,filp,buffer,lenp); + + case CTL_LINUX_CRC: + flash = ioremap_nocache(RAMSES_FLASH_PHYS+0x40000, 3*0x40000); + crc = ether_crc_le(3*0x40000, flash); + iounmap(flash); + sprintf(dummy_str,"%08x", crc); + return proc_dostring(ctl,write,filp,buffer,lenp); + + case CTL_LED_BLUE: + *valp = (ramses_control_shadow & RAMSES_CONTROL_LED_BLUE_) == 0; + break; + case CTL_LED_ORANGE: + *valp = (ramses_control_shadow & RAMSES_CONTROL_LED_ORANGE_) == 0; + break; + case CTL_UART: + *valp = (ramses_control_shadow & RAMSES_CONTROL_UART_PWR) != 0; + break; + case CTL_MMC: + *valp = (ramses_control_shadow & RAMSES_CONTROL_MMC_PWR) != 0; + break; + case CTL_POWEROFF: + *valp = 0; + break; + case CTL_GSM_POWER: + *valp = (ramses_control_shadow & RAMSES_CONTROL_GSM_PWR) != 0; + break; + case CTL_GSM_RESET: + *valp = (ramses_control_shadow & RAMSES_CONTROL_GSM_RESET) != 0; + break; + + case CTL_SCANNER_POWER: + *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_PWR) != 0; + break; + case CTL_SCANNER_WAKE: + *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_WAKE_) == 0; + break; + case CTL_SCANNER_TRIG: + *valp = (ramses_control_shadow & RAMSES_CONTROL_SCANNER_TRIG_) == 0; + break; + case CTL_SCANNER_BEAM: + *valp = ramses_flags & RAMSES_FLAGS_SCANNER_BEAM; + break; + + case CTL_KEY_SCAN: + *valp = (ramses_flags & RAMSES_FLAGS_KEY_SCAN) != 0; + break; + case CTL_KEY_SUSPEND: + *valp = (ramses_flags & RAMSES_FLAGS_KEY_SUSPEND) != 0; + break; + case CTL_KEY_OFF: + *valp = (ramses_flags & RAMSES_FLAGS_KEY_OFF) != 0; + break; + + case CTL_USBBUS_POWER: + *valp = (ramses_control_shadow & RAMSES_CONTROL_USB) != 0; + break; + case CTL_USBCHIP_POWER: + *valp = (RAMSES_CPLD_PERIPH_PWR & USB_HOST_PWR_EN) != 0; + break; +#ifdef CPLD_LED + case CTL_LED_CPLD: + *valp = (RAMSES_CPLD_LED_CONTROL & CPLD_LED1) == 0; + break; + case CTL_LED_CPLD_RED: + *valp = (RAMSES_CPLD_LED_CONTROL & CPLD_LED2) == 0; + break; +#endif + case CTL_LCD_BLIGHT: + *valp = (ramses_control_shadow & RAMSES_CONTROL_LCD_BLIGHT) != 0; + break; + case CTL_LCD_VCC: + *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_VCC) != 0; + break; + case CTL_LCD_DISPOFF: + *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_DISPOFF) != 0; + break; +#ifdef DEBUG + case CTL_LCD_PWM0: + *valp = PWM_PWDUTY0; + break; + case CTL_LCD_PWM1: +#ifdef OLDCODE + *valp = ramses_lcd_pwm1_shadow; +#else + *valp = PWM_PWDUTY1; +#endif + break; +#endif + case CTL_LCD_BRIGHTNESS: + *valp = ramses_lcd_get_brightness(); + break; + case CTL_LCD_CONTRAST: + *valp = ramses_lcd_get_contrast(); + break; + case CTL_LCD_FBTURN: + *valp = (ramses_flags & RAMSES_FLAGS_LCD_FBTURN) != 0; + break; + case CTL_LCD_TYPE: + *valp = ramses_lcd_type; + break; + + case CTL_CONTROL_SHADOW: + sprintf(dummy_str,"%04x", ramses_control_shadow); + return proc_dostring(ctl,write,filp,buffer,lenp); + + case CTL_COREVOLT: + *valp = ramses_corevolt_shadow; + break; + +#ifdef POTI + case CTL_LCD_POTI_NINC: + *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PINC) != 0; + break; + case CTL_LCD_POTI_NCS: + *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PCS) != 0; + break; + case CTL_LCD_POTI_UP: + *valp = (RAMSES_CPLD_LCD & RAMSES_LCD_PUP) != 0; + break; +#endif + +#ifdef CONFIG_MCP_UCB1400_TS + case CTL_ADC0: + *valp = adc_get(UCB_ADC_INP_AD0); + break; + case CTL_ADC1: + *valp = adc_get(UCB_ADC_INP_AD1); + break; + case CTL_ADC2: + *valp = adc_get(UCB_ADC_INP_AD2); + break; + case CTL_ADC3: + *valp = adc_get(UCB_ADC_INP_AD3); + break; +#endif + + case CTL_CHG_STS: + *valp = (RAMSES_CPLD_MISC_STATUS & RAMSES_CHG_STS) == 0; + break; + case CTL_WALL_IN: + *valp = (RAMSES_CPLD_MISC_STATUS & RAMSES_WALL_IN) != 0; + break; + + case CTL_BATT_TMP: + *valp = ramses_hdq_get_reg(HDQ_TMP) >> 4; + break; + case CTL_BATT_LMD: + *valp = ramses_hdq_get_reg(HDQ_LMD); + break; + case CTL_BATT_VSB: + *valp = ramses_hdq_get_reg(HDQ_VSB); + break; + case CTL_BATT_RCAC: + *valp = ramses_hdq_get_reg(HDQ_RCAC) & 0x7f; + break; + case CTL_BATT_CACT: + *valp = ramses_hdq_get_reg(HDQ_CACT); + break; + case CTL_BATT_SAE: + *valp = ramses_hdq_get_reg(HDQ_SAEH) << 8 | ramses_hdq_get_reg(HDQ_SAEL); + break; + case CTL_BATT_DCR: + *valp = ramses_hdq_get_reg(HDQ_DCR); + break; + + default: + // Just ignore unsupported parameters + break; + } + + // Save old state + val = *valp; + + // Perform the generic integer operation + if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0) + return (ret); + + // Write changes out to the registers + if (write && *valp != val) { + + val = *valp; + switch (ctl->ctl_name) { + + case CTL_LED_BLUE: + if (val) + RAMSES_LED_BLUE_ON() + else + RAMSES_LED_BLUE_OFF(); + break; + + case CTL_LED_ORANGE: + if (val) + RAMSES_LED_ORANGE_ON() + else + RAMSES_LED_ORANGE_OFF(); + break; + + case CTL_UART: + if (val) + RAMSES_UART_ON() + else + RAMSES_UART_OFF(); + break; + + case CTL_MMC: + if (val) + RAMSES_MMC_ON() + else + RAMSES_MMC_OFF(); + break; + + case CTL_POWEROFF: + if (val) + pm_power_off(); + break; + + case CTL_GSM_POWER: + if (val) + RAMSES_GSM_ON() + else + RAMSES_GSM_OFF(); + break; + + case CTL_GSM_RESET: + if (val) + RAMSES_GSM_RESET_ON() + else + RAMSES_GSM_RESET_OFF(); + break; + + case CTL_SCANNER_POWER: + if (val) + RAMSES_SCANNER_ON() + else + RAMSES_SCANNER_OFF(); + break; + + case CTL_SCANNER_WAKE: + if (val) + RAMSES_SCANNER_WAKE_ON() + else + RAMSES_SCANNER_WAKE_OFF(); + break; + + case CTL_SCANNER_TRIG: + if (val) + RAMSES_SCANNER_TRIG_ON() + else + RAMSES_SCANNER_TRIG_OFF(); + break; + + case CTL_SCANNER_BEAM: + if (val) + ramses_flags |= RAMSES_FLAGS_SCANNER_BEAM; + else + ramses_flags &= ~RAMSES_FLAGS_SCANNER_BEAM; + break; + + case CTL_KEY_SCAN: + if (val) + ramses_flags |= RAMSES_FLAGS_KEY_SCAN; + else + ramses_flags &= ~RAMSES_FLAGS_KEY_SCAN; + break; + + case CTL_KEY_SUSPEND: + if (val) + ramses_flags |= RAMSES_FLAGS_KEY_SUSPEND; + else + ramses_flags &= ~RAMSES_FLAGS_KEY_SUSPEND; + break; + + case CTL_KEY_OFF: + if (val) + ramses_flags |= RAMSES_FLAGS_KEY_OFF; + else + ramses_flags &= ~RAMSES_FLAGS_KEY_OFF; + break; + + case CTL_USBBUS_POWER: + if (val) + RAMSES_USB_BUS_ON() + else + RAMSES_USB_BUS_OFF(); + break; + + case CTL_USBCHIP_POWER: + if (val) + RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN; + else + RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN; + break; + +#ifdef CPLD_LED + case CTL_LED_CPLD: + if (val) + RAMSES_CPLD_LED_CONTROL &= ~CPLD_LED1; + else + RAMSES_CPLD_LED_CONTROL |= CPLD_LED1; + break; + + case CTL_LED_CPLD_RED: + if (val) + RAMSES_CPLD_LED_CONTROL &= ~CPLD_LED2; + else + RAMSES_CPLD_LED_CONTROL |= CPLD_LED2; + break; +#endif + + case CTL_LCD_BLIGHT: + if (val) + ramses_lcd_backlight_on(); + else + ramses_lcd_backlight_off(); + break; + + case CTL_LCD_VCC: + if (val) + RAMSES_CPLD_LCD |= RAMSES_LCD_VCC; + else + RAMSES_CPLD_LCD &= ~RAMSES_LCD_VCC; + break; + + case CTL_LCD_DISPOFF: + if (val) + RAMSES_CPLD_LCD |= RAMSES_LCD_DISPOFF; + else + RAMSES_CPLD_LCD &= ~RAMSES_LCD_DISPOFF; + break; + +#ifdef DEBUG + case CTL_LCD_PWM0: + PWM_PWDUTY0 = val; + break; + + case CTL_LCD_PWM1: +#ifdef OLDCODE + ramses_lcd_set_pwm1(val); +#else + PWM_PWDUTY1 = val; +#endif + break; +#endif + + case CTL_LCD_BRIGHTNESS: + ramses_lcd_set_brightness(val); + break; + + case CTL_LCD_CONTRAST: + ramses_lcd_set_contrast(val); + break; + + case CTL_LCD_FBTURN: + if (val) + ramses_flags |= RAMSES_FLAGS_LCD_FBTURN; + else + ramses_flags &= ~RAMSES_FLAGS_LCD_FBTURN; + break; + + case CTL_COREVOLT: + ramses_set_corevolt(val); + break; + +#ifdef POTI + case CTL_LCD_POTI_NCS: + if (val) + RAMSES_CPLD_LCD |= RAMSES_LCD_PCS; + else + RAMSES_CPLD_LCD &= ~RAMSES_LCD_PCS; + break; + case CTL_LCD_POTI_NINC: + if (val) + RAMSES_CPLD_LCD |= RAMSES_LCD_PINC; + else + RAMSES_CPLD_LCD &= ~RAMSES_LCD_PINC; + break; + case CTL_LCD_POTI_UP: + if (val) + RAMSES_CPLD_LCD |= RAMSES_LCD_PUP; + else + RAMSES_CPLD_LCD &= ~RAMSES_LCD_PUP; + break; +#endif + + default: + // Just ignore unsupported parameters + break; + } + } + +#ifdef DEBUG + printk("ramses_control_shadow new: %04x\n", ramses_control_shadow); +#endif + return ret; +} + + + +static ctl_table ramses_table[] = { + { + procname: "sys_name", + ctl_name: CTL_NAME, + data: &ramses_board_name, + maxlen: sizeof(ramses_board_name), + proc_handler: &proc_dostring, + mode: 0444, // read-only + }, { + procname: "sys_cpldver", + ctl_name: CTL_CPLD_VERSION, + data: &dummy_str, + maxlen: sizeof(dummy_str), + proc_handler: &ramses_sysctl_handler, + mode: 0444, // read-only + }, { + procname: "sys_bootcrc", + ctl_name: CTL_BOOTLOADER_CRC, + data: &dummy_str, + maxlen: sizeof(dummy_str), + proc_handler: &ramses_sysctl_handler, + mode: 0444, // read-only + }, { + procname: "sys_linuxcrc", + ctl_name: CTL_LINUX_CRC, + data: &dummy_str, + maxlen: sizeof(dummy_str), + proc_handler: &ramses_sysctl_handler, + mode: 0444, // read-only + }, { + procname: "led_blue", + ctl_name: CTL_LED_BLUE, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "led_orange", + ctl_name: CTL_LED_ORANGE, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "pwr_uart", + ctl_name: CTL_UART, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "pwr_mmc", + ctl_name: CTL_MMC, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "pwr_off", + ctl_name: CTL_POWEROFF, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "gsm_power", + ctl_name: CTL_GSM_POWER, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "gsm_reset", + ctl_name: CTL_GSM_RESET, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "scanner_power", + ctl_name: CTL_SCANNER_POWER, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "scanner_wake", + ctl_name: CTL_SCANNER_WAKE, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "scanner_trig", + ctl_name: CTL_SCANNER_TRIG, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "scanner_beam", + ctl_name: CTL_SCANNER_BEAM, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "key_scan", + ctl_name: CTL_KEY_SCAN, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "key_suspend", + ctl_name: CTL_KEY_SUSPEND, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "key_off", + ctl_name: CTL_KEY_OFF, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "usb_bus_power", + ctl_name: CTL_USBBUS_POWER, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "usb_chip_power", + ctl_name: CTL_USBCHIP_POWER, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, +#if LED_CPLD + { + procname: "led_cpld", + ctl_name: CTL_LED_CPLD, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "led_cpld_red", + ctl_name: CTL_LED_CPLD_RED, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, +#endif + { + procname: "lcd_backlight", + ctl_name: CTL_LCD_BLIGHT, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_vcc", + ctl_name: CTL_LCD_VCC, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_dispoff", + ctl_name: CTL_LCD_DISPOFF, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_brightness", + ctl_name: CTL_LCD_BRIGHTNESS, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_contrast", + ctl_name: CTL_LCD_CONTRAST, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_fbturn", + ctl_name: CTL_LCD_FBTURN, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_type", + ctl_name: CTL_LCD_TYPE, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, +#ifdef POTI + { + procname: "lcd_poti_ncs", + ctl_name: CTL_LCD_POTI_NCS, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_poti_ninc", + ctl_name: CTL_LCD_POTI_NINC, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_poti_up", + ctl_name: CTL_LCD_POTI_UP, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, +#endif +#ifdef DEBUG + { + procname: "lcd_pwm0", + ctl_name: CTL_LCD_PWM0, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, { + procname: "lcd_pwm1", + ctl_name: CTL_LCD_PWM1, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, +#endif + { + procname: "sys_shadowreg", + ctl_name: CTL_CONTROL_SHADOW, + data: &dummy_str, + maxlen: sizeof(dummy_str), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, + { + procname: "pwr_corevolt", + ctl_name: CTL_COREVOLT, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0664, + }, +#ifdef CONFIG_MCP_UCB1400_TS + { + procname: "adc0_vcc", + ctl_name: CTL_ADC0, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "adc1_ntc", + ctl_name: CTL_ADC1, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "adc2_goldcap", + ctl_name: CTL_ADC2, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "adc3_batt", + ctl_name: CTL_ADC3, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, +#else +#error No UCB +#endif + { + procname: "pwr_wall_in", + ctl_name: CTL_WALL_IN, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_charge", + ctl_name: CTL_CHG_STS, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_temp", + ctl_name: CTL_BATT_TMP, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_lmd", + ctl_name: CTL_BATT_LMD, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_vsb", + ctl_name: CTL_BATT_VSB, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_rcac", + ctl_name: CTL_BATT_RCAC, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_cact", + ctl_name: CTL_BATT_CACT, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_sae", + ctl_name: CTL_BATT_SAE, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, { + procname: "batt_dcr", + ctl_name: CTL_BATT_DCR, + data: &dummy_int, + maxlen: sizeof(int), + proc_handler: &ramses_sysctl_handler, + mode: 0444, + }, + + {0} + }; + +static ctl_table ramses_root_table[] = { + {RAMSES_SYSCTL, "board", NULL, 0, 0555, ramses_table}, + {0} + }; + + +static struct ctl_table_header *ramses_table_header; + + +static int __init ramses_sysctl_init(void) +{ + ramses_table_header = register_sysctl_table(ramses_root_table, 0); + if (!ramses_table_header) + return -ENOMEM; + return 0; +} + +static void __exit ramses_sysctl_exit(void) +{ + unregister_sysctl_table(ramses_table_header); +} + + +module_init(ramses_sysctl_init); +module_exit(ramses_sysctl_exit); + +MODULE_AUTHOR("Holger Schurig "); +MODULE_DESCRIPTION("Implements /proc/sys/board"); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/char/vt.c~linux-vtcomparison +++ linux-2.4.21/drivers/char/vt.c @@ -163,7 +163,9 @@ if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) return -EFAULT; - if (i >= NR_KEYS || s >= MAX_NR_KEYMAPS) + if (i >= NR_KEYS) + return -EINVAL; + if (s >= MAX_NR_KEYMAPS) return -EINVAL; switch (cmd) { --- linux-2.4.21/drivers/input/Config.in~keyb-input +++ linux-2.4.21/drivers/input/Config.in @@ -7,6 +7,8 @@ tristate 'Input core support' CONFIG_INPUT dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_INPUT +dep_tristate ' Ramses keyboard' CONFIG_INPUT_RAMSES_KEYB $CONFIG_INPUT_KEYBDEV $CONFIG_ARCH_RAMSES +dep_tristate ' Ramses wedge' CONFIG_INPUT_RAMSES_WEDGE $CONFIG_INPUT_RAMSES_KEYB dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 @@ -14,6 +16,7 @@ fi dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT +dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT dep_tristate ' MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS endmenu --- linux-2.4.21/drivers/input/Makefile~bluetooth +++ linux-2.4.21/drivers/input/Makefile @@ -8,7 +8,7 @@ # Objects that export symbols. -export-objs := input.o +export-objs := input.o ramses_keyb.o # Object file lists. @@ -21,10 +21,13 @@ obj-$(CONFIG_INPUT) += input.o obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o +obj-$(CONFIG_INPUT_RAMSES_KEYB) += ramses_keyb.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o +obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o +obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o # The global Rules.make. --- linux-2.4.21/drivers/input/keybdev.c~bluetooth +++ linux-2.4.21/drivers/input/keybdev.c @@ -154,16 +154,18 @@ static struct input_handler keybdev_handler; +static unsigned int ledstate = 0xff; + void keybdev_ledfunc(unsigned int led) { struct input_handle *handle; - for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { + ledstate = led; + for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01)); input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02)); input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04)); - } } @@ -203,6 +205,12 @@ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); kbd_refresh_leds(); + if (ledstate != 0xff) { + input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01)); + input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02)); + input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04)); + } + return handle; } --- /dev/null +++ linux-2.4.21/drivers/input/ramses_cellmap.h @@ -0,0 +1,34 @@ +static int ramses_cellmap[][8] = { + { KEY_A, KEY_B, KEY_C, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 0 + { KEY_D, KEY_E, KEY_F, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 1 + { KEY_G, KEY_H, KEY_I, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 2 + { KEY_J, KEY_K, KEY_L, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 3 + { KEY_M, KEY_N, KEY_O, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 4 + { KEY_P, KEY_Q, KEY_R, KEY_S, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 5 + { KEY_T, KEY_U, KEY_V, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 6 + { KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 7 + { KEY_AE, KEY_OE, KEY_UE, KEY_SZ, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 8 + { KEY_sA, KEY_sB, KEY_sC, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 9 + { KEY_sD, KEY_sE, KEY_sF, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 10 + { KEY_sG, KEY_sH, KEY_sI, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 11 + { KEY_sJ, KEY_sK, KEY_sL, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 12 + { KEY_sM, KEY_sN, KEY_sO, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 13 + { KEY_sP, KEY_sQ, KEY_sR, KEY_sS, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 14 + { KEY_sT, KEY_sU, KEY_sV, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 15 + { KEY_sW, KEY_sX, KEY_sY, KEY_sZ, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 16 + { KEY_sAE, KEY_sOE, KEY_sUE, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 17 + { KEY_COLON, KEY_FSLASH,KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 18 + { KEY_SEMI, KEY_BSLASH,KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 19 + { KEY_COMMA, KEY_STAR, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 20 + { KEY_UNDERL,KEY_EQUAL, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 21 + { KEY_PLUS, KEY_MINUS, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 22 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 24 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 24 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 25 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 26 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 27 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 28 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 29 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 30 + { KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, // 31 +}; --- /dev/null +++ linux-2.4.21/drivers/input/ramses_keyb.c @@ -0,0 +1,596 @@ +/* + * Keyboard driver using input layer + * + * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH + * written by H.Schurig + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +// Debug +//#define DEBUG +//#define DEBUG_DUMP_KEYSTATE +#ifdef DEBUG +# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +# define PRINTK(fmt, args...) printk(fmt, ## args) +#else +# define DPRINTK(fmt, args...) +# define PRINTK(fmt, args...) +#endif + + +/* + * Timeouts + */ +#define SCANINTERVAL HZ/10 +#define TRIGOFFINTERVAL HZ*2 +#define CELLINTERVAL HZ+HZ/2 +#define MAPINTERVAL 15*HZ +#define SUSPEND_COUNTER 40 +#define SUSPEND_LED_COUNTER 8 +#define SUSPEND_NOW 0xffff +#define KEYBD_MATRIX_SETTLING_TIME_US 100 + + +/* + * macros for matrix keyboard driver + */ +#define KEYBD_MATRIX_NUMBER_INPUTS 7 +#define KEYBD_MATRIX_NUMBER_OUTPUTS 14 + +#define KEYBD_MATRIX_SET_OUTPUTS(outputs) \ +{\ + RAMSES_CPLD_KB_COL_LOW = outputs;\ + RAMSES_CPLD_KB_COL_HIGH = outputs >> 7;\ +} + +#define KEYBD_MATRIX_GET_INPUTS(inputs) \ +{\ + inputs = (RAMSES_CPLD_KB_ROW & 0x7f);\ +} + + +// External functions (are they in some #include file?) +extern int input_setkeycode(unsigned int scancode, unsigned int keycode); +extern int input_getkeycode(unsigned int scancode); +extern int input_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char input_unexpected_up(unsigned char keycode); +extern unsigned char input_sysrq_xlate[]; +extern int pm_suggest_suspend(void); + +// Keyboard-Related definitions +#define KEYBD_MATRIX_INPUT_MASK ((1 << KEYBD_MATRIX_NUMBER_INPUTS)-1) +#define KEYBD_MATRIX_OUTPUT_MASK ((1 << KEYBD_MATRIX_NUMBER_OUTPUTS)-1) + +#include "ramses_scancodes.h" +#include "ramses_keymap.h" +#include "ramses_cellmap.h" + + +static char *kbd_name = "Keyboard"; +struct input_dev ramses_kbd_dev; +static struct timer_list reenable_timer; +static struct timer_list trigoff_timer; +static struct tq_struct tq_suspend; +static __u16 keystate_cur[KEYBD_MATRIX_NUMBER_OUTPUTS]; +static __u16 keystate_prev[KEYBD_MATRIX_NUMBER_OUTPUTS]; +static __u16 keystate_keep[KEYBD_MATRIX_NUMBER_OUTPUTS]; // used for auto-repeat +static struct timer_list cell_timer; +static int curr_map = MAP_NORMAL; +static int cell_key = -1; +static int cell_sel = -1; +static struct pm_dev *pm_keyb; +static int suspend_counter = 0; + + +void ramses_key(int keycode) +{ + DPRINTK("keycode: %d 0x%x\n", keycode, keycode); + if (KVAL(keycode)) { + switch (KMOD(keycode)) { + case KM_SHIFT: + DPRINTK("shift\n"); + input_report_key(&ramses_kbd_dev, KEY_LEFTSHIFT, 1); + break; + case KM_CTRL: + DPRINTK("ctrl\n"); + input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 1); + break; + case KM_ALT: + DPRINTK("alt\n"); + input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 1); + break; + case KM_ALTGR: + DPRINTK("altgr\n"); + input_report_key(&ramses_kbd_dev, KEY_RIGHTALT, 1); + break; + case KM_ALTCTRL: + DPRINTK("alt+ctrl\n"); + input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 1); + input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 1); + break; + } + + DPRINTK("report: %d 0x%x\n", KVAL(keycode), KVAL(keycode)); + input_report_key(&ramses_kbd_dev, KVAL(keycode), 1); + input_report_key(&ramses_kbd_dev, KVAL(keycode), 0); + + switch (KMOD(keycode)) { + case KM_SHIFT: + input_report_key(&ramses_kbd_dev, KEY_LEFTSHIFT, 0); + break; + case KM_CTRL: + input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 0); + break; + case KM_ALT: + input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 0); + break; + case KM_ALTGR: + input_report_key(&ramses_kbd_dev, KEY_RIGHTALT, 0); + break; + case KM_ALTCTRL: + input_report_key(&ramses_kbd_dev, KEY_LEFTALT, 0); + input_report_key(&ramses_kbd_dev, KEY_LEFTCTRL, 0); + break; + } + } +} + +static void kbd_cell_timer(unsigned long keepmap) +{ + int keycode; + + if (cell_sel != -1) { + keycode = ramses_cellmap[cell_key][cell_sel]; + //DPRINTK("key: %d sel: %d keycode: %d 0x%x\n", cell_key, cell_sel, keycode, keycode); + ramses_key(keycode); + cell_sel = -1; + } + + if (!keepmap && curr_map!=MAP_NORMAL) { + DPRINTK("normal map because of %ld\n", keepmap); + curr_map = MAP_NORMAL; + RAMSES_LED_BLUE_OFF(); + RAMSES_LED_ORANGE_OFF(); + } +} + +static void kbd_setleds(void) +{ + if (suspend_counter >= SUSPEND_LED_COUNTER) { + if (suspend_counter & 4) { + RAMSES_LED_ORANGE_OFF(); + RAMSES_LED_BLUE_ON(); + } else { + RAMSES_LED_ORANGE_ON(); + RAMSES_LED_BLUE_OFF(); + } + return; + } + + switch (curr_map) { + case MAP_NORMAL: + RAMSES_LED_BLUE_OFF(); + RAMSES_LED_ORANGE_OFF(); + return; + + case MAP_BLUE: + RAMSES_LED_BLUE_ON(); + RAMSES_LED_ORANGE_OFF(); + return; + + case MAP_ORANGE: + RAMSES_LED_BLUE_OFF(); + RAMSES_LED_ORANGE_ON(); + return; + + case MAP_CAPS: + RAMSES_LED_BLUE_ON(); + RAMSES_LED_ORANGE_ON(); + return; + } + DPRINTK("unknown map\n"); +} + + +static void kbd_start_scanner(void) +{ + RAMSES_SCANNER_TRIG_OFF(); + RAMSES_SCANNER_WAKE_OFF(); + RAMSES_SCANNER_TRIG_ON(); + mod_timer(&trigoff_timer, jiffies + TRIGOFFINTERVAL); +} + +static void kbd_stop_scanner(unsigned long dummy) +{ + RAMSES_SCANNER_TRIG_OFF(); +} + +static int kbd_dokeycode(unsigned char scancode, int down) +{ + int i,keycode; + + //DPRINTK("calling with (%d,%x,%d)\n", scancode, scancode, down); + if (scancode >= MAX_SCANCODES) { + printk("%s: scancode too big for table\n", __FUNCTION__); + return 0; + } + + keycode = ramses_keymap[scancode][curr_map]; + + + if (keycode==KEY_SCAN) { + if ((ramses_flags & RAMSES_FLAGS_KEY_SCAN) == 0) + return 0; + + DPRINTK("scan btn\n"); + if (down) { + if (ramses_flags & RAMSES_FLAGS_SCANNER_BEAM) { + // just turn on laser beam + RAMSES_SCANNER_WAKE_ON(); + } else { + kbd_start_scanner(); + } + } else { + if (ramses_flags & RAMSES_FLAGS_SCANNER_BEAM) { + kbd_start_scanner(); + } else { + kbd_stop_scanner(0); + } + } + return 0; + } + + + if (keycode==KEY_SUSP) { + if ((ramses_flags & RAMSES_FLAGS_KEY_SUSPEND) == 0) + return 0; + + if (down) { + suspend_counter++; + if (suspend_counter >= SUSPEND_COUNTER) { + suspend_counter = SUSPEND_NOW; + } + } else { + if (suspend_counter == SUSPEND_NOW) { + curr_map = MAP_NORMAL; + schedule_task(&tq_suspend); + } + suspend_counter = 0; + } + return down; + } + + + if (keycode==KEY_OFF) { + if (down || ((ramses_flags & RAMSES_FLAGS_KEY_OFF) == 0)) + return 0; + curr_map = MAP_NORMAL; + ramses_shut_off(); + return 0; + } + + + if (!down) + return 0; + + + DPRINTK("curr_map %d scancode %d keycode %d 0x%x typ %d\n", curr_map, scancode, keycode, keycode, KMOD(keycode)); + + + // Cell-Phone keyboard handling + if (KMOD(keycode)==KM_CELL) { + //DPRINTK("cell phone key %d\n", KVAL(keycode)); + + // did we press a different cell-phone key as last time? + if (KVAL(keycode)!=cell_key) + kbd_cell_timer(1); + + cell_key = KVAL(keycode); // store current cell-phone key + cell_sel++; // increase current sub-key + if (ramses_cellmap[cell_key][cell_sel]==0) // if at end of sub-key list, back off + cell_sel = 0; + //DPRINTK("cell_key: %d cell_sel: %d\n", cell_key, cell_sel); + // auto-emit via kbd_cell_timer + mod_timer(&cell_timer, jiffies + CELLINTERVAL); + return 0; // do not revert to keys_normal + } + + + // if we pressed any other key then a cell-phone key, we look if the + // current half-pressed cell-phone key should be emitted + kbd_cell_timer(1); + + + switch(keycode) { + + // Keymap handling + + case KEY_NORM: + DPRINTK("norm key map\n"); + curr_map = MAP_NORMAL; + return 0; + + case KEY_BLUE: + //DPRINTK("blue key map\n"); + curr_map = MAP_BLUE; + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_ORNG: + //DPRINTK("orange key map\n"); + curr_map = MAP_ORANGE; + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_CAPS: + DPRINTK("caps key map\n"); + curr_map = MAP_CAPS; + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_BRIP: + i = ramses_lcd_get_brightness()-6; + ramses_lcd_set_brightness(i); + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_BRIM: + i = ramses_lcd_get_brightness()+6; + ramses_lcd_set_brightness(i); + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_CTRM: + i = ramses_lcd_get_contrast()+3; + ramses_lcd_set_contrast(i); + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + + case KEY_CTRP: + i = ramses_lcd_get_contrast()-3; + ramses_lcd_set_contrast(i); + mod_timer(&cell_timer, jiffies + MAPINTERVAL); // automatically disable keymap + return 0; + } + + // normal keys + + ramses_key(keycode); + + if (curr_map!=MAP_NORMAL) + DPRINTK("back to normal map\n"); + curr_map = MAP_NORMAL; + RAMSES_LED_BLUE_OFF(); + RAMSES_LED_ORANGE_OFF(); + return 0; +} + + +/** + * @param rescan 0 if we look for pressed keys, 1 if we look for released keys + * + * This routine get's called from the ISR (then rescan is always 0) or from + * the reenable_timer function (then rescan is 1). + */ +static void kbd_scan_keyboard(unsigned long rescan) +{ + int i,n; + int cols; + unsigned char code; + __u16 keystate_xor[KEYBD_MATRIX_NUMBER_OUTPUTS]; + + + // Find out if a key (or more) was pressed down. It's possible that + // because of spikes we got an interrupt, but when the IRQ service + // routine fired there is currently no detectable key. If this is + // true, then delay some times and re-try, but not too often. + + cols = 0; + for(n=0; n<10; n++) { + for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { + KEYBD_MATRIX_SET_OUTPUTS( 1 << i ); + udelay(KEYBD_MATRIX_SETTLING_TIME_US); + KEYBD_MATRIX_GET_INPUTS(keystate_cur[i]); + if (keystate_cur[i]) + cols++; + } + if (cols || rescan) + break; + udelay(KEYBD_MATRIX_SETTLING_TIME_US*50); + } + + // if rescan is true, we are in the process of turning on the IRQ. + // Ignore any spurious IRQ. However, if we got an IRQ but could not + // detect any key, we note this on the console, clear the keystate + // completely and make sure the IRQ gets re-enabled via the timer + // shortly. + + if (!cols && !rescan) { + printk("%s: spurious kbd int\n", __FUNCTION__); + for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { + keystate_cur[i] = 0; + keystate_prev[i] = 0; + } + mod_timer(&reenable_timer, jiffies + SCANINTERVAL); + return; + } + + pm_access(pm_keyb); + + // Okay, all went well. We now keystate_cur[] may contain the rows + // where we had keypresses, e.g. + // 0 0 0 2 0 0 0 0 0 0 0 0 0 0 + // We would see this if DEBUG_DUMP_KEYSTATE is on: + +#ifdef DEBUG_DUMP_KEYSTATE + cols = 0; + for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { + printk("%d-%d ",keystate_cur[i], keystate_keep[i]); + } + printk("\n"); +#endif + + cols = 0; + for (i = 0; i < KEYBD_MATRIX_NUMBER_OUTPUTS; i++) { + + // detect which key has changes doing an XOR of old state with new state + keystate_xor[i] = keystate_prev[i] ^ keystate_cur[i]; + //printk("%d: prev %d cur %d xor %d keep %d\n", i, keystate_prev[i], keystate_cur[i], keystate_xor[i], keystate_keep[i]); + + // some key changed, find out which one and do the scancode handling + if (keystate_xor[i] || keystate_keep[i]) { + for (n = 0; n < KEYBD_MATRIX_NUMBER_INPUTS; n++) + { + if ( (keystate_keep[i] & keystate_cur[i]) || + (keystate_xor[i] & (1 << n)) ) + { + int res; + code = n * KEYBD_MATRIX_NUMBER_OUTPUTS + i + 1; + res = kbd_dokeycode(code, keystate_cur[i] & (1 << n) ? 1 : 0); + kbd_setleds(); + if (res) { + keystate_keep[i] = 1 << n; + goto out; + } + } + } + } +out: + keystate_prev[i] = keystate_cur[i]; + } + + + // fire reenable time if we are in the ISR + if (!rescan) + mod_timer(&reenable_timer, jiffies + SCANINTERVAL); +} + + + +static void kbd_reenable_timer(unsigned long dummy) +{ + // re-scan the keyboard (to detect released keys) + kbd_scan_keyboard(1); + + // re-enable interrupts from the CPLD + KEYBD_MATRIX_SET_OUTPUTS( KEYBD_MATRIX_OUTPUT_MASK ); +} + + + + + +/** + * Referenced by request_irq() + */ +static void kbd_interrupt(int irq, void *dummy, struct pt_regs *fp) +{ + kbd_scan_keyboard(0); +} + +static void ramseskbd_suspend(void *data) +{ + pm_suggest_suspend(); +} + + +static int __init ramseskbd_init(void) +{ + int irq_gpio_pin; + + // Activate the normal pc-keycodes for the input-layer-keyboard + k_setkeycode = input_setkeycode; + k_getkeycode = input_getkeycode; + k_translate = input_translate; + k_unexpected_up = input_unexpected_up; +#ifdef CONFIG_MAGIC_SYSRQ + k_sysrq_key = 0x54; + k_sysrq_xlate = input_sysrq_xlate; +#endif + + // In linux-2.5.x we can do + // init_input_dev(&ramses_kbd_dev); + // but here we don't have this on linux-2.4, so we fill it with zeros: + memset(&ramses_kbd_dev, 0, sizeof(ramses_kbd_dev)); + ramses_kbd_dev.name = kbd_name; + + // which events we can produce (only keypresses): + ramses_kbd_dev.evbit[0] = BIT(EV_KEY); + + // which keypresses we can produce (all): + memset(&ramses_kbd_dev.keybit, 0xff, sizeof(ramses_kbd_dev.keybit)); + + // We set the 14 output columns to 0. This stops the CPLD to + // generate an IRQ before we finished our setup + KEYBD_MATRIX_SET_OUTPUTS(0); + + // Turn all LEDs off, meaning that we have the normal keymap active + RAMSES_LED_BLUE_OFF(); + RAMSES_LED_ORANGE_OFF(); + // TODO: used leds.c? + + // Now we make sure that the GPIO for our IRQ is programmed correctly + irq_gpio_pin = IRQ_TO_GPIO_2_80(RAMSES_KEYBOARD_IRQ); + GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin); + set_GPIO_IRQ_edge(irq_gpio_pin, RAMSES_KEYBOARD_IRQ_EDGE); + request_irq(RAMSES_KEYBOARD_IRQ, kbd_interrupt, 0, kbd_name, NULL); + + // Initialize timer to re-enable IRQs. That's our method of keyboard de-prelling + init_timer(&reenable_timer); + reenable_timer.function = kbd_reenable_timer; + + init_timer(&trigoff_timer); + trigoff_timer.function = kbd_stop_scanner; + + // Initialize to escape the blue mode, so we emit the current cell-phone key + init_timer(&cell_timer); + cell_timer.function = kbd_cell_timer; + + tq_suspend.routine = ramseskbd_suspend; + + // Register with Power-Management +#ifdef PM_DEBUG + pm_keyb = pm_register(PM_SYS_DEV, PM_SYS_KBC+1, NULL, "ramses_keyb"); +#else + pm_keyb = pm_register(PM_SYS_DEV, PM_SYS_KBC+1, NULL); +#endif + + // Register our keyboard + input_register_device(&ramses_kbd_dev); + + // We set the 14 output columns to 1. This allows the CPLD to + // generate an IRQ when one of the rows goes high + KEYBD_MATRIX_SET_OUTPUTS(KEYBD_MATRIX_OUTPUT_MASK); + + return 0; +} + + +static void __exit ramseskbd_exit(void) +{ + // make IRQs impossible, return the IRQ and unregister us + KEYBD_MATRIX_SET_OUTPUTS(0); + free_irq(RAMSES_KEYBOARD_IRQ, NULL); + pm_unregister(pm_keyb); + input_unregister_device(&ramses_kbd_dev); +} + + +module_init(ramseskbd_init); +module_exit(ramseskbd_exit); + +MODULE_AUTHOR("Holger Schurig "); +MODULE_DESCRIPTION("Ramses keyboard driver"); +MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(ramses_key); +EXPORT_SYMBOL(ramses_kbd_dev); --- /dev/null +++ linux-2.4.21/drivers/input/ramses_keymap.h @@ -0,0 +1,68 @@ +// Normal Map +static int ramses_keymap[][6] = { +/* Normal Blue Orange Caps Spare Spare */ +/* 0 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 1 */ {KEY_SUSP, KEY_SUSP, KEY_SUSP, KEY_OFF, KEY_SUSP, KEY_SUSP }, +/* 2 */ {KEY_UP, KEY_UP, KEY_PGUP, KEY_UP, KEY_noop, KEY_noop }, +/* 3 */ {KEY_1, KEY_SPACE, KEY_BRIM, KEY_SPACE, KEY_noop, KEY_noop }, +/* 4 */ {KEY_4, KEY_ghi , KEY_CTRM, KEY_GHI , KEY_noop, KEY_noop }, +/* 5 */ {KEY_7, KEY_pqrs, KEY_cel7, KEY_PQRS, KEY_noop, KEY_noop }, +/* 6 */ {KEY_DOT, KEY_uml, KEY_celP, KEY_UML, KEY_noop, KEY_noop }, +/* 7 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 8 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 9 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 10 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 11 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 12 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 13 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 14 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 15 */ {KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER, KEY_ENTER}, +/* 16 */ {KEY_DOWN, KEY_DOWN, KEY_PGDN, KEY_DOWN, KEY_noop, KEY_noop }, +/* 17 */ {KEY_2, KEY_abc , KEY_BRIP, KEY_ABC , KEY_noop, KEY_noop }, +/* 18 */ {KEY_5, KEY_jkl , KEY_CTRP, KEY_JKL, KEY_noop, KEY_noop }, +/* 19 */ {KEY_8, KEY_tuv , KEY_cel8, KEY_TUV, KEY_noop, KEY_noop }, + +/* Normal Blue Orange Caps Spare Spare */ +/* 20 */ {KEY_0, KEY_TAB, KEY_cel0, KEY_BTAB, KEY_noop, KEY_noop }, +/* 21 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 22 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 23 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 24 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 25 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 26 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 27 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 28 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 29 */ {KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC, KEY_ESC }, +/* 30 */ {KEY_RIGHT, KEY_RIGHT, KEY_END, KEY_C2, KEY_noop, KEY_noop }, +/* 31 */ {KEY_3, KEY_def, KEY_FXIT, KEY_DEF, KEY_noop, KEY_noop }, +/* 32 */ {KEY_6, KEY_mno , KEY_FRST, KEY_MNO, KEY_noop, KEY_noop }, +/* 33 */ {KEY_9, KEY_wxyz, KEY_cel9, KEY_WXYZ, KEY_noop, KEY_noop }, +/* 34 */ {KEY_BACKSPACE,KEY_ATSIGN,KEY_BAR, KEY_noop, KEY_noop, KEY_noop }, +/* 35 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 36 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 37 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 38 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 39 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, + +/* Normal Blue Orange Caps Spare Spare */ +/* 40 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 41 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 42 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 43 */ {KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN }, +/* 44 */ {KEY_LEFT, KEY_LEFT, KEY_HOME, KEY_C1, KEY_noop, KEY_noop }, +/* 45 */ {KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF, KEY_OFF }, +/* 46 */ {KEY_F1, KEY_F4, KEY_F7, KEY_F10, KEY_noop, KEY_noop }, +/* 47 */ {KEY_F2, KEY_F5, KEY_F8, KEY_F11, KEY_noop, KEY_noop }, +/* 48 */ {KEY_F3, KEY_F6, KEY_F9, KEY_F12, KEY_noop, KEY_noop }, +/* 49 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 50 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 51 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 52 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 53 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 54 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 55 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 56 */ {KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop, KEY_noop }, +/* 57 */ {KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN, KEY_SCAN }, +/* 58 */ {KEY_BLUE, KEY_NORM, KEY_CAPS, KEY_NORM, KEY_NORM, KEY_NORM }, +/* 59 */ {KEY_ORNG, KEY_CAPS, KEY_NORM, KEY_NORM, KEY_NORM, KEY_NORM }, +}; --- /dev/null +++ linux-2.4.21/drivers/input/ramses_scancodes.h @@ -0,0 +1,134 @@ +#ifndef _RAMSES_SCANCODES_H +#define _RAMSES_SCANCODES_H + +#define KMOD(a) (a & 0xff00) +#undef KVAL +#define KVAL(a) (a & 0x00ff) + +// which modifiers to send before/after +#define KM_SPECIAL 0x300 +#define KM_CELL 0x400 +#define KM_SHIFT 0x500 +#define KM_ALT 0x600 +#define KM_CTRL 0x700 +#define KM_ALTGR 0x800 +#define KM_ALTCTRL 0x900 + +// or special keys +#define KEY_noop KM_SPECIAL + 0 +#define KEY_OFF KM_SPECIAL + 1 +#define KEY_SUSP KM_SPECIAL + 2 +#define KEY_SCAN KM_SPECIAL + 3 +#define KEY_CTRM KM_SPECIAL + 4 +#define KEY_CTRP KM_SPECIAL + 5 +#define KEY_BRIM KM_SPECIAL + 6 +#define KEY_BRIP KM_SPECIAL + 7 + +#define KEY_NORM KM_SPECIAL + 10 +#define KEY_BLUE KM_SPECIAL + 11 +#define KEY_ORNG KM_SPECIAL + 12 +#define KEY_CAPS KM_SPECIAL + 13 + + +// our cell-phone like keys +#define KEY_abc KM_CELL + 0 +#define KEY_def KM_CELL + 1 +#define KEY_ghi KM_CELL + 2 +#define KEY_jkl KM_CELL + 3 +#define KEY_mno KM_CELL + 4 +#define KEY_pqrs KM_CELL + 5 +#define KEY_tuv KM_CELL + 6 +#define KEY_wxyz KM_CELL + 7 +#define KEY_uml KM_CELL + 8 +#define KEY_ABC KM_CELL + 9 +#define KEY_DEF KM_CELL + 10 +#define KEY_GHI KM_CELL + 11 +#define KEY_JKL KM_CELL + 12 +#define KEY_MNO KM_CELL + 13 +#define KEY_PQRS KM_CELL + 14 +#define KEY_TUV KM_CELL + 15 +#define KEY_WXYZ KM_CELL + 16 +#define KEY_UML KM_CELL + 17 +#define KEY_cel7 KM_CELL + 18 +#define KEY_cel8 KM_CELL + 19 +#define KEY_cel9 KM_CELL + 20 +#define KEY_celP KM_CELL + 21 +#define KEY_cel0 KM_CELL + 22 + +// Shift-Keys +#define KEY_sA KM_SHIFT + KEY_A +#define KEY_sB KM_SHIFT + KEY_B +#define KEY_sC KM_SHIFT + KEY_C +#define KEY_sD KM_SHIFT + KEY_D +#define KEY_sE KM_SHIFT + KEY_E +#define KEY_sF KM_SHIFT + KEY_F +#define KEY_sG KM_SHIFT + KEY_G +#define KEY_sH KM_SHIFT + KEY_H +#define KEY_sI KM_SHIFT + KEY_I +#define KEY_sJ KM_SHIFT + KEY_J +#define KEY_sK KM_SHIFT + KEY_K +#define KEY_sL KM_SHIFT + KEY_L +#define KEY_sM KM_SHIFT + KEY_M +#define KEY_sN KM_SHIFT + KEY_N +#define KEY_sO KM_SHIFT + KEY_O +#define KEY_sP KM_SHIFT + KEY_P +#define KEY_sQ KM_SHIFT + KEY_Q +#define KEY_sR KM_SHIFT + KEY_R +#define KEY_sS KM_SHIFT + KEY_S +#define KEY_sT KM_SHIFT + KEY_T +#define KEY_sU KM_SHIFT + KEY_U +#define KEY_sV KM_SHIFT + KEY_V +#define KEY_sW KM_SHIFT + KEY_W +#define KEY_sX KM_SHIFT + KEY_X +#define KEY_sY KM_SHIFT + KEY_Y +#define KEY_sZ KM_SHIFT + KEY_Z + +// Umlaute +#define KEY_sAE KM_SHIFT + 40 +#define KEY_sOE KM_SHIFT + 39 +#define KEY_sUE KM_SHIFT + 26 +#define KEY_AE 40 +#define KEY_OE 39 +#define KEY_UE 26 +#define KEY_SZ 12 + +// AS400-Keys +#define KEY_FRST KM_ALT + KEY_R +#define KEY_FXIT KM_ALT + KEY_X + +// Console-Switch +#define KEY_C1 KM_ALTCTRL + KEY_F1 +#define KEY_C2 KM_ALTCTRL + KEY_F2 + +// additional keys from the german keyboard +#undef KEY_MINUS +#undef KEY_EQUAL +#undef KEY_Y +#undef KEY_Z +#define KEY_Y 44 +#define KEY_Z 21 +#define KEY_STAR 55 +#define KEY_COLON KM_SHIFT + 52 +#define KEY_UNDERL KM_SHIFT + 53 +#define KEY_ATSIGN KM_ALTGR + 16 +#define KEY_BAR KM_ALTGR + 86 +#define KEY_EQUAL KM_SHIFT + 11 +#define KEY_SEMI KM_SHIFT + 51 +#define KEY_BSLASH KM_ALTGR + 12 +#define KEY_FSLASH KM_SHIFT + KEY_7 +#define KEY_MINUS 53 +#define KEY_PLUS 27 +#define KEY_GAENSE KM_SHIFT + 3 +#define KEY_PARA KM_SHIFT + 4 +#define KEY_HASH 43 +#define KEY_PGUP KEY_PAGEUP +#define KEY_PGDN KEY_PAGEDOWN +#define KEY_BTAB KM_SHIFT + KEY_TAB + +#define MAP_NORMAL 0 +#define MAP_BLUE 1 +#define MAP_ORANGE 2 +#define MAP_CAPS 3 +#define MAX_SCANCODES 100 + +#endif --- /dev/null +++ linux-2.4.21/drivers/input/uinput.c @@ -0,0 +1,428 @@ +/* + * User level driver support for input subsystem + * + * Heavily based on evdev.c by Vojtech Pavlik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Aristeu Sergio Rozanski Filho + * + * Changes/Revisions: + * 0.1 20/06/2002 + * - first public version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int uinput_dev_open(struct input_dev *dev) +{ + return 0; +} + +static void uinput_dev_close(struct input_dev *dev) +{ +} + +static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct uinput_device *udev; + + udev = (struct uinput_device *)dev->private; + + udev->buff[udev->head].type = type; + udev->buff[udev->head].code = code; + udev->buff[udev->head].value = value; + do_gettimeofday(&udev->buff[udev->head].time); + udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; + + wake_up_interruptible(&udev->waitq); + + return 0; +} + +static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect) +{ + return 0; +} + +static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) +{ + return 0; +} + +static int uinput_create_device(struct uinput_device *udev) +{ + if (!udev->dev->name) { + printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); + return -EINVAL; + } + + udev->dev->open = uinput_dev_open; + udev->dev->close = uinput_dev_close; + udev->dev->event = uinput_dev_event; + udev->dev->upload_effect = uinput_dev_upload_effect; + udev->dev->erase_effect = uinput_dev_erase_effect; + udev->dev->private = udev; + + init_waitqueue_head(&(udev->waitq)); + + input_register_device(udev->dev); + + set_bit(UIST_CREATED, &(udev->state)); + + return 0; +} + +static int uinput_destroy_device(struct uinput_device *udev) +{ + if (!test_bit(UIST_CREATED, &(udev->state))) { + printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME); + return -EINVAL; + } + + input_unregister_device(udev->dev); + + clear_bit(UIST_CREATED, &(udev->state)); + + return 0; +} + +static int uinput_open(struct inode *inode, struct file *file) +{ + struct uinput_device *newdev; + struct input_dev *newinput; + + newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL); + if (!newdev) + goto error; + memset(newdev, 0, sizeof(struct uinput_device)); + + newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL); + if (!newinput) + goto cleanup; + memset(newinput, 0, sizeof(struct input_dev)); + + newdev->dev = newinput; + + file->private_data = newdev; + + return 0; +cleanup: + kfree(newdev); +error: + return -ENOMEM; +} + +static int uinput_validate_absbits(struct input_dev *dev) +{ + unsigned int cnt; + int retval = 0; + + for (cnt = 0; cnt < ABS_MAX; cnt++) { + if (!test_bit(cnt, dev->absbit)) + continue; + + if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */ + (dev->absmax[cnt] <= dev->absmin[cnt])) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, cnt, + dev->absmin[cnt], dev->absmax[cnt]); + retval = -EINVAL; + break; + } + + if ((dev->absflat[cnt] < dev->absmin[cnt]) || + (dev->absflat[cnt] > dev->absmax[cnt])) { + printk(KERN_DEBUG + "%s: absflat[%02x] out of range: %d " + "(min:%d/max:%d)\n", + UINPUT_NAME, cnt, dev->absflat[cnt], + dev->absmin[cnt], dev->absmax[cnt]); + retval = -EINVAL; + break; + } + } + return retval; +} + +static int uinput_alloc_device(struct file *file, const char *buffer, size_t count) +{ + struct uinput_user_dev *user_dev; + struct input_dev *dev; + struct uinput_device *udev; + int size, + retval; + + retval = count; + + udev = (struct uinput_device *)file->private_data; + dev = udev->dev; + + user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL); + if (!user_dev) { + retval = -ENOMEM; + goto exit; + } + + if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { + retval = -EFAULT; + goto exit; + } + + if (NULL != dev->name) + kfree(dev->name); + + size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; + dev->name = kmalloc(size, GFP_KERNEL); + if (!dev->name) { + retval = -ENOMEM; + goto exit; + } + + strncpy(dev->name, user_dev->name, size); + dev->idbus = user_dev->idbus; + dev->idvendor = user_dev->idvendor; + dev->idproduct = user_dev->idproduct; + dev->idversion = user_dev->idversion; + dev->ff_effects_max = user_dev->ff_effects_max; + + size = sizeof(int) * (ABS_MAX + 1); + memcpy(dev->absmax, user_dev->absmax, size); + memcpy(dev->absmin, user_dev->absmin, size); + memcpy(dev->absfuzz, user_dev->absfuzz, size); + memcpy(dev->absflat, user_dev->absflat, size); + + /* check if absmin/absmax/absfuzz/absflat are filled as + * told in Documentation/input/input-programming.txt */ + if (test_bit(EV_ABS, dev->evbit)) { + retval = uinput_validate_absbits(dev); + if (retval < 0) + kfree(dev->name); + } + +exit: + kfree(user_dev); + return retval; +} + +static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct uinput_device *udev = file->private_data; + + if (test_bit(UIST_CREATED, &(udev->state))) { + struct input_event ev; + + if (copy_from_user(&ev, buffer, sizeof(struct input_event))) + return -EFAULT; + input_event(udev->dev, ev.type, ev.code, ev.value); + } + else + count = uinput_alloc_device(file, buffer, count); + + return count; +} + +static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct uinput_device *udev = file->private_data; + int retval = 0; + + if (!test_bit(UIST_CREATED, &(udev->state))) + return -ENODEV; + + if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(udev->waitq, + (udev->head != udev->tail) || + !test_bit(UIST_CREATED, &(udev->state))); + + if (retval) + return retval; + + if (!test_bit(UIST_CREATED, &(udev->state))) + return -ENODEV; + + while ((udev->head != udev->tail) && + (retval + sizeof(struct input_event) <= count)) { + if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]), + sizeof(struct input_event))) return -EFAULT; + udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; + retval += sizeof(struct input_event); + } + + return retval; +} + +static unsigned int uinput_poll(struct file *file, poll_table *wait) +{ + struct uinput_device *udev = file->private_data; + + poll_wait(file, &udev->waitq, wait); + + if (udev->head != udev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int uinput_burn_device(struct uinput_device *udev) +{ + if (test_bit(UIST_CREATED, &(udev->state))) + uinput_destroy_device(udev); + + kfree(udev->dev); + kfree(udev); + + return 0; +} + +static int uinput_close(struct inode *inode, struct file *file) +{ + return uinput_burn_device((struct uinput_device *)file->private_data); +} + +static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct uinput_device *udev; + + udev = (struct uinput_device *)file->private_data; + + /* device attributes can not be changed after the device is created */ + if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state))) + return -EINVAL; + + switch (cmd) { + case UI_DEV_CREATE: + retval = uinput_create_device(udev); + break; + + case UI_DEV_DESTROY: + retval = uinput_destroy_device(udev); + break; + + case UI_SET_EVBIT: + if (arg > EV_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->evbit); + break; + + case UI_SET_KEYBIT: + if (arg > KEY_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->keybit); + break; + + case UI_SET_RELBIT: + if (arg > REL_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->relbit); + break; + + case UI_SET_ABSBIT: + if (arg > ABS_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->absbit); + break; + + case UI_SET_MSCBIT: + if (arg > MSC_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->mscbit); + break; + + case UI_SET_LEDBIT: + if (arg > LED_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->ledbit); + break; + + case UI_SET_SNDBIT: + if (arg > SND_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->sndbit); + break; + + case UI_SET_FFBIT: + if (arg > FF_MAX) { + retval = -EINVAL; + break; + } + set_bit(arg, udev->dev->ffbit); + break; + + default: + retval = -EFAULT; + } + return retval; +} + +struct file_operations uinput_fops = { + owner: THIS_MODULE, + open: uinput_open, + release: uinput_close, + read: uinput_read, + write: uinput_write, + poll: uinput_poll, + ioctl: uinput_ioctl, +}; + +static struct miscdevice uinput_misc = { + fops: &uinput_fops, + minor: UINPUT_MINOR, + name: UINPUT_NAME, +}; + +static int __init uinput_init(void) +{ + return misc_register(&uinput_misc); +} + +static void __exit uinput_exit(void) +{ + misc_deregister(&uinput_misc); +} + +MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); +MODULE_DESCRIPTION("User level driver support for input subsystem"); +MODULE_LICENSE("GPL"); + +module_init(uinput_init); +module_exit(uinput_exit); + --- /dev/null +++ linux-2.4.21/drivers/input/wedge.c @@ -0,0 +1,241 @@ +/* + * Virtual keyboard wedge using input layer + * + * (C) 2002,2003 by M&N Logistik-Lösungen Online GmbH + * written by H.Schurig + * + * Creates a misc char device /dev/misc/wedge. Any output to this + * device will be translated (via a german keyboard map) into scancodes + * and re-submitted into the keyboard channel. Any console, X-Windows + * or Qt/Embedded application will be able to receive this info. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +// Debug +//#define DEBUG 1 +#ifdef DEBUG +# define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +# define PRINTK(fmt, args...) printk(fmt, ## args) +#else +# define DPRINTK(fmt, args...) +# define PRINTK(fmt, args...) +#endif + + +// Defines +#define KBD_STUFF_MAX_BYTES 512 + +// Für den IOCTL +#define WEDGE_RAWKEY_DOWN _IOW('w', 0x72, unsigned long) +#define WEDGE_RAWKEY_UP _IOW('w', 0x73, unsigned long) +#define WEDGE_TS_ABS_X _IOW('w', 0x74, unsigned long) +#define WEDGE_TS_ABS_Y _IOW('w', 0x75, unsigned long) +#define WEDGE_TS_ABS_PRESSURE _IOW('w', 0x76, unsigned long) + +// Externs +#define MAX_NR_KEYMAPS 256 +extern void ramses_key(int keycode); +extern unsigned short *key_maps[MAX_NR_KEYMAPS]; +extern struct input_dev ramses_kbd_dev; +extern void ucb1x00_ts_evt_add(void *, u16 pressure, u16 x, u16 y); + +// for special keys +struct wedge_lookup_t { + u_short c; + u_short keysym; +}; + +struct wedge_lookup_t wedge_lookup[] = { + { 0x0a, 0x001c, }, + { 0x2f, 0x0508, }, +}; + + + + +static void *outbuf; + +static int wedge_open(struct inode *inode, struct file *filp) +{ + int ret; + + ret = -ENXIO; + outbuf = kmalloc(KBD_STUFF_MAX_BYTES, GFP_KERNEL); + if (!outbuf) + goto out; + + ret = 0; + +out: + if (ret) { + kfree(outbuf); + } + return ret; +} + + +static int wedge_close(struct inode *inode, struct file *filp) +{ + kfree(outbuf); + return 0; +} + + +static int wedge_search_map(u_short map[], int c) +{ + int i; + + for (i=0; ibuf); len = CAPIMSG_LEN(cmsg->buf); skb = alloc_skb(len, GFP_ATOMIC); + if(!skb) { + printk(KERN_ERR "no skb len(%d) memory\n", len); + return; + } memcpy(skb_put(skb, len), cmsg->buf, len); - (*capifuncs->capi_put_message) (global.appid, skb); + err = (*capifuncs->capi_put_message) (global.appid, skb); + if (err) { + printk(KERN_WARNING "%s: capi_put_message error: %04x\n", + __FUNCTION__, err); + kfree_skb(skb); + return; + } global.nsentctlpkt++; } @@ -2188,10 +2200,10 @@ free_ncci(card, card->bchans[card->nbchan-1].nccip); if (card->bchans[card->nbchan-1].plcip) free_plci(card, card->bchans[card->nbchan-1].plcip); - if (card->plci_list) - printk(KERN_ERR "capidrv: bug in free_plci()\n"); card->nbchan--; } + if (card->plci_list) + printk(KERN_ERR "capidrv: bug in free_plci()\n"); kfree(card->bchans); card->bchans = 0; --- linux-2.4.21/drivers/isdn/avmb1/kcapi.c~bluetooth +++ linux-2.4.21/drivers/isdn/avmb1/kcapi.c @@ -546,7 +546,13 @@ static void notify_up(__u32 contr) { struct capi_interface_user *p; + __u16 appl; + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (!VALID_APPLID(appl)) continue; + if (APPL(appl)->releasing) continue; + CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam); + } printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr); spin_lock(&capi_users_lock); for (p = capi_users; p; p = p->next) { @@ -714,12 +720,16 @@ nextpp = &(*pp)->next; } } - APPL(appl)->releasing--; - if (APPL(appl)->releasing <= 0) { - APPL(appl)->signal = 0; - APPL_MARK_FREE(appl); - printk(KERN_INFO "kcapi: appl %d down\n", appl); - } + if (APPL(appl)->releasing) { /* only release if the application was marked for release */ + printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing); + APPL(appl)->releasing--; + if (APPL(appl)->releasing <= 0) { + APPL(appl)->signal = 0; + APPL_MARK_FREE(appl); + printk(KERN_INFO "kcapi: appl %d down\n", appl); + } + } else + printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr); } /* * ncci management @@ -872,16 +882,7 @@ static void controllercb_ready(struct capi_ctr * card) { - __u16 appl; - card->cardstate = CARD_RUNNING; - - for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { - if (!VALID_APPLID(appl)) continue; - if (APPL(appl)->releasing) continue; - card->driver->register_appl(card, appl, &APPL(appl)->rparam); - } - printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", CARDNR(card), card->name); --- linux-2.4.21/drivers/misc/Makefile~wedge +++ linux-2.4.21/drivers/misc/Makefile @@ -12,7 +12,7 @@ O_TARGET := misc.o export-objs := mcp-core.o mcp-sa1100.o mcp-pxa.o \ - ucb1x00-core.o + ucb1x00-core.o ucb1x00-ts.o obj-$(CONFIG_MCP_SA1100) += mcp-core.o mcp-sa1100.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o --- linux-2.4.21/drivers/misc/mcp-pxa.c~ucb1x00 +++ linux-2.4.21/drivers/misc/mcp-pxa.c @@ -31,6 +31,11 @@ return (struct mcp *)codec; } +void mcp_put(void) +{ + pxa_ac97_put(); +} + void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) { struct ac97_codec *codec = (struct ac97_codec *)mcp; @@ -55,3 +60,7 @@ void mcp_disable(struct mcp *mcp) { } + +MODULE_AUTHOR("Jeff Sutherland "); +MODULE_DESCRIPTION("PXA mcp low level support"); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/misc/mcp.h~ucb1x00 +++ linux-2.4.21/drivers/misc/mcp.h @@ -43,6 +43,7 @@ /* noddy implementation alert! */ struct mcp *mcp_get(void); +void mcp_put(void); int mcp_register(struct mcp *); #define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) --- linux-2.4.21/drivers/misc/ucb1x00-core.c~pm +++ linux-2.4.21/drivers/misc/ucb1x00-core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -181,8 +182,9 @@ if (val & UCB_ADC_DAT_VAL) break; /* yield to other processes */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + //HS set_current_state(TASK_INTERRUPTIBLE); + //HS schedule_timeout(1); + udelay(200); } return UCB_ADC_DAT(val); @@ -209,7 +211,8 @@ struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data; unsigned int isr; - if (rqst == PM_RESUME) { + switch (rqst) { + case PM_RESUME: ucb1x00_enable(ucb); isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); @@ -521,7 +524,9 @@ */ static int __init ucb1x00_configure(struct ucb1x00 *ucb) { +#ifndef CONFIG_ARCH_RAMSES unsigned int irq_gpio_pin = 0; +#endif int irq, default_irq = NO_IRQ; #ifdef CONFIG_ARCH_SA1100 @@ -611,12 +616,14 @@ /* * Eventually, this will disappear. */ +#ifndef CONFIG_ARCH_RAMSES if (irq_gpio_pin) #ifdef CONFIG_ARCH_PXA_IDP set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_FALLING_EDGE); #else set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE); #endif +#endif irq = ucb1x00_detect_irq(ucb); if (irq != NO_IRQ) { if (default_irq != NO_IRQ && irq != default_irq) --- linux-2.4.21/drivers/misc/ucb1x00-ts.c~ramses-ucb1x00-dejitter +++ linux-2.4.21/drivers/misc/ucb1x00-ts.c @@ -29,6 +29,7 @@ #include #include +#include #include "ucb1x00.h" @@ -97,7 +98,7 @@ }; static struct ucb1x00_ts ucbts; -static int adcsync = UCB_NOSYNC; +static int adcsync = UCB_SYNC; static int ucb1x00_ts_startup(struct ucb1x00_ts *ts); static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts); @@ -116,8 +117,14 @@ next_head = (ts->evt_head + 1) & (NR_EVENTS - 1); if (next_head != ts->evt_tail) { ts->events[ts->evt_head].pressure = pressure; +#if 0 ts->events[ts->evt_head].x = x; ts->events[ts->evt_head].y = y; +#else + // rotate by -90 + ts->events[ts->evt_head].x = y; + ts->events[ts->evt_head].y = x; +#endif do_gettimeofday(&ts->events[ts->evt_head].stamp); ts->evt_head = next_head; @@ -256,11 +263,11 @@ #define ucb1x00_ts_evt_clear(ts) do { } while (0) -static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) { - input_report_abs(&ts->idev, ABS_X, x); - input_report_abs(&ts->idev, ABS_Y, y); - input_report_abs(&ts->idev, ABS_PRESSURE, pressure); + input_report_abs(&ucbts.idev, ABS_X, y); + input_report_abs(&ucbts.idev, ABS_Y, x); + input_report_abs(&ucbts.idev, ABS_PRESSURE, pressure); } static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) @@ -335,7 +342,7 @@ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSMY, ts->adcsync); } /* @@ -346,19 +353,15 @@ */ static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) { - ucb1x00_reg_write(ts->ucb, UCB_TS_CR, - UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | - UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1x00_reg_write(ts->ucb, UCB_TS_CR, - UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | - UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + unsigned int res; ucb1x00_reg_write(ts->ucb, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); - udelay(55); + udelay(600); - return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); + res = ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSMY, ts->adcsync); + return res; } /* @@ -369,19 +372,15 @@ */ static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) { + unsigned int res; ucb1x00_reg_write(ts->ucb, UCB_TS_CR, - UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | - UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1x00_reg_write(ts->ucb, UCB_TS_CR, - UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | - UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1x00_reg_write(ts->ucb, UCB_TS_CR, - UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_TSPY_GND | UCB_TS_CR_TSMY_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); - udelay(55); + udelay(300); - return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); + res = ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); + return res; } /* @@ -430,8 +429,9 @@ * We could run as a real-time thread. However, thus far * this doesn't seem to be necessary. */ -// tsk->policy = SCHED_FIFO; -// tsk->rt_priority = 1; +//HS + tsk->policy = SCHED_FIFO; + tsk->rt_priority = 1; /* only want to receive SIGKILL */ spin_lock_irq(&tsk->sigmask_lock); @@ -451,8 +451,8 @@ ucb1x00_adc_enable(ts->ucb); x = ucb1x00_ts_read_xpos(ts); - y = ucb1x00_ts_read_ypos(ts); p = ucb1x00_ts_read_pressure(ts); + y = ucb1x00_ts_read_ypos(ts); /* * Switch back to interrupt mode. @@ -461,7 +461,7 @@ ucb1x00_adc_disable(ts->ucb); set_task_state(tsk, TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 100); + schedule_timeout(HZ / 200); if (signal_pending(tsk)) break; @@ -504,7 +504,7 @@ } set_task_state(tsk, TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 100); + schedule_timeout(HZ / 200); } if (signal_pending(tsk)) @@ -655,8 +655,8 @@ char *p; while ((p = strsep(&str, ",")) != NULL) { - if (strcmp(p, "sync") == 0) - adcsync = UCB_SYNC; + if (strcmp(p, "nosync") == 0) + adcsync = UCB_NOSYNC; } return 1; @@ -674,6 +674,7 @@ module_init(ucb1x00_ts_init); module_exit(ucb1x00_ts_exit); +EXPORT_SYMBOL(ucb1x00_ts_evt_add); MODULE_AUTHOR("Russell King "); MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/Config.in~mtd-cvs +++ linux-2.4.21/drivers/mtd/Config.in @@ -1,5 +1,5 @@ -# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ +# $Id: Config.in,v 1.78 2004/08/09 18:46:03 dmarlin Exp $ mainmenu_option next_comment comment 'Memory Technology Devices (MTD)' @@ -11,10 +11,16 @@ if [ "$CONFIG_MTD_DEBUG" = "y" ]; then int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 fi - dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD + bool ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD dep_tristate ' MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS - dep_tristate ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS $CONFIG_MTD_PARTITIONS + if [ "$CONFIG_MTD_REDBOOT_PARTS" = "y" -o "$CONFIG_MTD_REDBOOT_PARTS" = "m" ]; then + bool ' Include unallocated flash space' CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + bool ' Force read-only for RedBoot system images' CONFIG_MTD_REDBOOT_PARTS_READONLY + fi + if [ "$CONFIG_MTD_PARTITIONS" = "y" ]; then + bool ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS + fi if [ "$CONFIG_ARM" = "y" ]; then dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS fi @@ -30,6 +36,7 @@ if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW fi + dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD source drivers/mtd/chips/Config.in --- linux-2.4.21/drivers/mtd/Makefile~mtd-cvs +++ linux-2.4.21/drivers/mtd/Makefile @@ -1,66 +1,54 @@ # -# Makefile for the memory technology device drivers. -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# -# $Id: Makefile,v 1.65 2002/03/22 07:10:34 dwmw2 Exp $ - - -obj-y += chips/chipslink.o maps/mapslink.o \ - devices/devlink.o nand/nandlink.o -obj-m := -obj-n := -obj- := - -O_TARGET := mtdlink.o - -export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o -list-multi := nftl.o - -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. +# linux/drivers/Makefile.24 +# Makefile for obsolete kernels. # -# dwmw2 21/11/0 +# $Id: Makefile.24,v 1.3 2004/08/11 14:45:53 dmarlin Exp $ # Core functionality. -obj-$(CONFIG_MTD) += mtdcore.o +mtd-y := mtdcore.o +mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +obj-$(CONFIG_MTD) += $(mtd-y) + obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o -obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.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 +obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs-24.o +obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs-24.o +obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs-24.o +obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs-24.o +obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs-24.o nftl-objs := nftlcore.o nftlmount.o +inftl-objs := inftlcore.o inftlmount.o + +export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \ + mtdconcat.o mtd_blkdevs-24.o + +mtd_blkdevs-objs := mtd_blkdevs-24.o + +obj-y += chips/chipslink.o maps/mapslink.o \ + devices/devlink.o nand/nandlink.o + +O_TARGET := mtdlink.o + +list-multi := nftl.o inftl.o mtd_blkdevs-24.o + +mod-subdirs := +subdir-y := chips maps devices nand +subdir-m := $(subdir-y) include $(TOPDIR)/Rules.make nftl.o: $(nftl-objs) $(LD) -r -o $@ $(nftl-objs) +inftl.o: $(inftl-objs) + $(LD) -r -o $@ $(inftl-objs) + +mtd_blkdevs.o: $(mtd_blkdevs-objs) + $(LD) -r -o $@ $(mtd_blkdevs-objs) + --- /dev/null +++ linux-2.4.21/drivers/mtd/Makefile.common @@ -0,0 +1,27 @@ +# +# Makefile for the memory technology device drivers. +# +# $Id: Makefile.common,v 1.5 2004/08/10 20:51:49 dwmw2 Exp $ + +# Core functionality. +mtd-y := mtdcore.o +mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o +obj-$(CONFIG_MTD) += $(mtd-y) + +obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o +obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o +obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o +obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + +# 'Users' - code which presents functionality to userspace. +obj-$(CONFIG_MTD_CHAR) += mtdchar.o +obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o +obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o +obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o +obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o +obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o + +nftl-objs := nftlcore.o nftlmount.o +inftl-objs := inftlcore.o inftlmount.o + +obj-y += chips/ maps/ devices/ nand/ --- linux-2.4.21/drivers/mtd/afs.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/afs.c @@ -21,7 +21,7 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: afs.c,v 1.8 2002/05/04 08:49:09 rmk Exp $ + $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $ ======================================================================*/ @@ -57,6 +57,17 @@ u32 checksum; /* Image checksum (inc. this struct) */ }; +static u32 word_sum(void *words, int num) +{ + u32 *p = words; + u32 sum = 0; + + while (num--) + sum += *p++; + + return sum; +} + static int afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start, u_int off, u_int mask) @@ -76,17 +87,25 @@ return ret; } + ret = 1; + /* * Does it contain the magic number? */ if (fs.signature != 0xa0ffff9f) - ret = 1; + ret = 0; + + /* + * Check the checksum. + */ + if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff) + ret = 0; /* * Don't touch the SIB. */ if (fs.type == 2) - ret = 1; + ret = 0; *iis_start = fs.image_info_base & mask; *img_start = fs.image_start & mask; @@ -96,14 +115,14 @@ * be located after the footer structure. */ if (*iis_start >= ptr) - ret = 1; + ret = 0; /* * Check the start of this image. The image * data can not be located after this block. */ if (*img_start > off) - ret = 1; + ret = 0; return ret; } @@ -112,20 +131,41 @@ afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr) { size_t sz; - int ret; + int ret, i; memset(iis, 0, sizeof(*iis)); ret = mtd->read(mtd, ptr, sizeof(*iis), &sz, (u_char *) iis); - if (ret >= 0 && sz != sizeof(*iis)) - ret = -EINVAL; if (ret < 0) + goto failed; + + if (sz != sizeof(*iis)) { + ret = -EINVAL; + goto failed; + } + + ret = 0; + + /* + * Validate the name - it must be NUL terminated. + */ + for (i = 0; i < sizeof(iis->name); i++) + if (iis->name[i] == '\0') + break; + + if (i < sizeof(iis->name)) + ret = 1; + + return ret; + + failed: printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n", ptr, ret); - return ret; } -int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts) +static int parse_afs_partitions(struct mtd_info *mtd, + struct mtd_partition **pparts, + unsigned long origin) { struct mtd_partition *parts; u_int mask, off, idx, sz; @@ -150,12 +190,14 @@ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); if (ret < 0) break; - if (ret == 1) + if (ret == 0) continue; ret = afs_read_iis(mtd, &iis, iis_ptr); if (ret < 0) break; + if (ret == 0) + continue; sz += sizeof(struct mtd_partition); sz += strlen(iis.name) + 1; @@ -183,13 +225,15 @@ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); if (ret < 0) break; - if (ret == 1) + if (ret == 0) continue; /* Read the image info block */ ret = afs_read_iis(mtd, &iis, iis_ptr); if (ret < 0) break; + if (ret == 0) + continue; strcpy(str, iis.name); size = mtd->erasesize + off - img_ptr; @@ -227,7 +271,25 @@ return idx ? idx : ret; } -EXPORT_SYMBOL(parse_afs_partitions); +static struct mtd_part_parser afs_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_afs_partitions, + .name = "afs", +}; + +static int __init afs_parser_init(void) +{ + return register_mtd_parser(&afs_parser); +} + +static void __exit afs_parser_exit(void) +{ + deregister_mtd_parser(&afs_parser); +} + +module_init(afs_parser_init); +module_exit(afs_parser_exit); + MODULE_AUTHOR("ARM Ltd"); MODULE_DESCRIPTION("ARM Firmware Suite partition parser"); --- linux-2.4.21/drivers/mtd/chips/Config.in~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/Config.in @@ -1,6 +1,6 @@ # drivers/mtd/chips/Config.in -# $Id: Config.in,v 1.16 2002/09/03 13:30:43 joern Exp $ +# $Id: Config.in,v 1.20 2004/08/09 18:46:03 dmarlin Exp $ mainmenu_option next_comment @@ -18,6 +18,7 @@ define_bool CONFIG_MTD_GEN_PROBE n fi fi + if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then @@ -27,11 +28,13 @@ LITTLE_ENDIAN_BYTE CONFIG_MTD_CFI_LE_BYTE_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 - bool ' Support 64-bit buswidth' CONFIG_MTD_CFI_B8 - if [ "$CONFIG_MTD_CFI_B1" = "y" ]; then + bool ' Support 8-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_1 + bool ' Support 16-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_2 + bool ' Support 32-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_4 + bool ' Support 64-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_8 + bool ' Support 128-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_16 + bool ' Support 256-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_32 + if [ "$CONFIG_MTD_MAP_BANK_WIDTH_1" = "y" ]; then define_bool CONFIG_MTD_CFI_I1 y else bool ' Support 1-chip flash interleave' CONFIG_MTD_CFI_I1 @@ -46,6 +49,20 @@ dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE +if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \ + -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \ + -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then + define_bool CONFIG_MTD_CFI_UTIL y +else + if [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \ + -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \ + -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then + define_bool CONFIG_MTD_CFI_UTIL m + else + define_bool CONFIG_MTD_CFI_UTIL n + fi +fi + 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 ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD --- linux-2.4.21/drivers/mtd/chips/Makefile~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/Makefile @@ -1,31 +1,12 @@ # -# linux/drivers/chips/Makefile +# linux/drivers/chips/Makefile.24 +# Makefile for obsolete kernels. # -# $Id: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $ +# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $ O_TARGET := chipslink.o +export-objs := chipreg.o gen_probe.o cfi_util.o -export-objs := chipreg.o gen_probe.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 -obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o -obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o -obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o -obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o -obj-$(CONFIG_MTD_JEDEC) += jedec.o -obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o -obj-$(CONFIG_MTD_RAM) += map_ram.o -obj-$(CONFIG_MTD_ROM) += map_rom.o -obj-$(CONFIG_MTD_SHARP) += sharp.o -obj-$(CONFIG_MTD_ABSENT) += map_absent.o +include Makefile.common include $(TOPDIR)/Rules.make --- /dev/null +++ linux-2.4.21/drivers/mtd/chips/Makefile.common @@ -0,0 +1,26 @@ +# +# linux/drivers/chips/Makefile +# +# $Id: Makefile.common,v 1.4 2004/07/12 16:07:30 dwmw2 Exp $ + +# *** 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 gen_probe.o + +obj-$(CONFIG_MTD) += chipreg.o +obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o +obj-$(CONFIG_MTD_CFI) += cfi_probe.o +obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o +obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o +obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o +obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o +obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o +obj-$(CONFIG_MTD_JEDEC) += jedec.o +obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o +obj-$(CONFIG_MTD_RAM) += map_ram.o +obj-$(CONFIG_MTD_ROM) += map_rom.o +obj-$(CONFIG_MTD_SHARP) += sharp.o +obj-$(CONFIG_MTD_ABSENT) += map_absent.o --- linux-2.4.21/drivers/mtd/chips/amd_flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/amd_flash.c @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg * - * $Id: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,6 @@ #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 #define AM29BDS323D 0x22D1 -#define AM29BDS643D 0x227E /* Atmel */ #define AT49xV16x 0x00C0 @@ -125,10 +125,10 @@ static struct mtd_chip_driver amd_flash_chipdrv = { - probe: amd_flash_probe, - destroy: amd_flash_destroy, - name: "amd_flash", - module: THIS_MODULE + .probe = amd_flash_probe, + .destroy = amd_flash_destroy, + .name = "amd_flash", + .module = THIS_MODULE }; @@ -140,11 +140,11 @@ static inline __u32 wide_read(struct map_info *map, __u32 addr) { if (map->buswidth == 1) { - return map->read8(map, addr); + return map_read8(map, addr); } else if (map->buswidth == 2) { - return map->read16(map, addr); + return map_read16(map, addr); } else if (map->buswidth == 4) { - return map->read32(map, addr); + return map_read32(map, addr); } return 0; @@ -153,11 +153,11 @@ static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) { if (map->buswidth == 1) { - map->write8(map, val, addr); + map_write8(map, val, addr); } else if (map->buswidth == 2) { - map->write16(map, val, addr); + map_write16(map, val, addr); } else if (map->buswidth == 4) { - map->write32(map, val, addr); + map_write32(map, val, addr); } } @@ -424,231 +424,217 @@ 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[] = { + static 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_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_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_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_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 = 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_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 = 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_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_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_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_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: 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 = 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: 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 = 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: 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 = 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: 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_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_FUJITSU, - dev_id: MBM29LV800BB, - name: "Fujitsu MBM29LV800BB", - 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 = 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_FUJITSU, + .dev_id = MBM29LV800BB, + .name = "Fujitsu MBM29LV800BB", + .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_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 = 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: 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 } + .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_AMD, - dev_id: AM29BDS323D, - name: "AMD AM29BDS323D", - size: 0x00400000, - numeraseregions: 3, - regions: { - { offset: 0x000000, erasesize: 0x10000, numblocks: 48 }, - { offset: 0x300000, erasesize: 0x10000, numblocks: 15 }, - { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 }, + .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 } } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29BDS643D, - name: "AMD AM29BDS643D", - size: 0x00800000, - numeraseregions: 3, - regions: { - { offset: 0x000000, erasesize: 0x10000, numblocks: 96 }, - { offset: 0x600000, erasesize: 0x10000, numblocks: 31 }, - { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 }, + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29BDS323D, + .name = "AMD AM29BDS323D", + .size = 0x00400000, + .numeraseregions = 3, + .regions = { + { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, + { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, + { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49xV16x, - name: "Atmel AT49xV16x", - size: 0x00200000, - numeraseregions: 2, - regions: { - { offset: 0x000000, erasesize: 0x02000, numblocks: 8 }, - { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49xV16x, + .name = "Atmel AT49xV16x", + .size = 0x00200000, + .numeraseregions = 2, + .regions = { + { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, + { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49xV16xT, - name: "Atmel AT49xV16xT", - size: 0x00200000, - numeraseregions: 2, - regions: { - { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, - { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 } + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49xV16xT, + .name = "Atmel AT49xV16xT", + .size = 0x00200000, + .numeraseregions = 2, + .regions = { + { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, + { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } } } }; @@ -720,7 +706,7 @@ "memory for MTD erase region info\n", map->name); kfree(mtd); map->fldrv_priv = NULL; - return 0; + return NULL; } reg_idx = 0; @@ -782,8 +768,8 @@ map->fldrv_priv = private; map->fldrv = &amd_flash_chipdrv; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } @@ -822,7 +808,7 @@ chip->state = FL_READY; - map->copy_from(map, buf, adr, len); + map_copy_from(map, buf, adr, len); wake_up(&chip->wq); spin_unlock_bh(chip->mutex); @@ -984,7 +970,7 @@ u_char tmp_buf[4]; __u32 datum; - map->copy_from(map, tmp_buf, + map_copy_from(map, tmp_buf, bus_ofs + private->chips[chipnum].start, map->buswidth); while (len && i < map->buswidth) @@ -1057,7 +1043,7 @@ u_char tmp_buf[2]; __u32 datum; - map->copy_from(map, tmp_buf, + map_copy_from(map, tmp_buf, ofs + private->chips[chipnum].start, map->buswidth); while (len--) { @@ -1124,7 +1110,7 @@ timeo = jiffies + (HZ * 20); spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); while (flash_is_busy(map, adr, private->interleave)) { @@ -1178,7 +1164,7 @@ __u8 verify; for (address = adr; address < (adr + size); address++) { - if ((verify = map->read8(map, address)) != 0xFF) { + if ((verify = map_read8(map, address)) != 0xFF) { error = 1; break; } @@ -1309,9 +1295,7 @@ } instr->state = MTD_ERASE_DONE; - if (instr->callback) { - instr->callback(instr); - } + mtd_erase_callback(instr); return 0; } --- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0001.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.168 2005/02/17 20:34:59 nico Exp $ * * * 10/10/2000 Nicolas Pitre @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,21 +29,39 @@ #include #include #include +#include #include -#include +#include #include +#include -// debugging, turns off buffer write mode #define FORCE_WORD_WRITE +/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ +/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ + +// debugging, turns off buffer write mode if set to 1 +#define FORCE_WORD_WRITE 0 + +#define MANUFACTURER_INTEL 0x0089 +#define I82802AB 0x00ad +#define I82802AC 0x00ac +#define MANUFACTURER_ST 0x0020 +#define M50LPW080 0x002F static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_read_fact_prot_reg (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_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); +static int cfi_intelext_get_fact_prot_info (struct mtd_info *, + struct otp_info *, size_t); +static int cfi_intelext_get_user_prot_info (struct mtd_info *, + struct otp_info *, size_t); static int cfi_intelext_suspend (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *); @@ -50,18 +69,29 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *, int); -static struct mtd_info *cfi_intelext_setup (struct map_info *); +static struct mtd_info *cfi_intelext_setup (struct mtd_info *); +static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **); -static int do_point (struct mtd_info *mtd, loff_t from, size_t len, +static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); -static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, +static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" + + + +/* + * *********** SETUP AND PROBE BITS *********** + */ + static struct mtd_chip_driver cfi_intelext_chipdrv = { - probe: NULL, /* Not usable directly */ - destroy: cfi_intelext_destroy, - name: "cfi_cmdset_0001", - module: THIS_MODULE + .probe = NULL, /* Not usable directly */ + .destroy = cfi_intelext_destroy, + .name = "cfi_cmdset_0001", + .module = THIS_MODULE }; /* #define DEBUG_LOCK_BITS */ @@ -81,7 +111,8 @@ 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++) { + printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported"); + for (i=10; i<32; i++) { if (extp->FeatureSupport & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); + extp->VccOptimal >> 4, extp->VccOptimal & 0xf); if (extp->VppOptimal) printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", - extp->VppOptimal >> 8, extp->VppOptimal & 0xf); + extp->VppOptimal >> 4, extp->VppOptimal & 0xf); } #endif +#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ +static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + + printk(KERN_WARNING "cfi_cmdset_0001: Suspend " + "erase on write disabled.\n"); + extp->SuspendCmdSupport &= ~1; +} +#endif + +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND +static void fixup_no_write_suspend(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + if (cfip && (cfip->FeatureSupport&4)) { + cfip->FeatureSupport &= ~4; + printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); + } +} +#endif + +static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ + cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ +} + +static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* Note this is done after the region info is endian swapped */ + cfi->cfiq->EraseRegionInfo[1] = + (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; +}; + +static void fixup_use_point(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + if (!mtd->point && map_is_linear(map)) { + mtd->point = cfi_intelext_point; + mtd->unpoint = cfi_intelext_unpoint; + } +} + +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + printk(KERN_INFO "Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } +} + +static struct cfi_fixup cfi_fixup_table[] = { +#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, +#endif +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND + { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL }, +#endif +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, +#endif + { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, + { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL }, + { 0, 0, NULL, NULL } +}; + +static inline struct cfi_pri_intelext * +read_pri_intelext(struct map_info *map, __u16 adr) +{ + struct cfi_pri_intelext *extp; + unsigned int extp_size = sizeof(*extp); + + again: + extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp"); + if (!extp) + return NULL; + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); + extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + + if (extp->MajorVersion == '1' && extp->MinorVersion == '3') { + unsigned int extra_size = 0; + int nb_parts, i; + + /* Protection Register info */ + extra_size += (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); + + /* Burst Read info */ + extra_size += 6; + + /* Number of hardware-partitions */ + extra_size += 1; + if (extp_size < sizeof(*extp) + extra_size) + goto need_more; + nb_parts = extp->extra[extra_size - 1]; + + for (i = 0; i < nb_parts; i++) { + struct cfi_intelext_regioninfo *rinfo; + rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size]; + extra_size += sizeof(*rinfo); + if (extp_size < sizeof(*extp) + extra_size) + goto need_more; + rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions); + extra_size += (rinfo->NumBlockTypes - 1) + * sizeof(struct cfi_intelext_blockinfo); + } + + if (extp_size < sizeof(*extp) + extra_size) { + need_more: + extp_size = sizeof(*extp) + extra_size; + kfree(extp); + if (extp_size > 4096) { + printk(KERN_ERR + "%s: cfi_pri_intelext is too fat\n", + __FUNCTION__); + return NULL; + } + goto again; + } + } + + return extp; +} + /* 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 @@ -119,8 +308,29 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; int i; - __u32 base = cfi->chips[0].start; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + mtd->write = cfi_intelext_write_words; + mtd->sync = cfi_intelext_sync; + mtd->lock = cfi_intelext_lock; + mtd->unlock = cfi_intelext_unlock; + mtd->suspend = cfi_intelext_suspend; + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; if (cfi->cfi_mode == CFI_MODE_CFI) { /* @@ -130,40 +340,17 @@ */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_intelext *extp; - int ofs_factor = cfi->interleave * cfi->device_type; - - //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); - if (!adr) - return NULL; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - - extp = kmalloc(sizeof(*extp), GFP_KERNEL); + extp = read_pri_intelext(map, adr); if (!extp) { - printk(KERN_ERR "Failed to allocate memory\n"); + kfree(mtd); return NULL; } - /* Read in the Extended Query Table */ - for (i=0; iMajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { - printk(KERN_WARNING " Unknown IntelExt Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - return NULL; - } + /* Install our own private info structure */ + cfi->cmdset_priv = extp; - /* Do some byteswapping if necessary */ - extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); - extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); - extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ @@ -171,19 +358,15 @@ #endif if(extp->SuspendCmdSupport & 1) { -//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE -#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE -/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ - printk(KERN_WARNING "cfi_cmdset_0001: Suspend " - "erase on write disabled.\n"); - extp->SuspendCmdSupport &= ~1; -#else printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); -#endif } - /* Install our own private info structure */ - cfi->cmdset_priv = extp; } + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -194,30 +377,19 @@ map->fldrv = &cfi_intelext_chipdrv; - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); - return cfi_intelext_setup(map); + return cfi_intelext_setup(mtd); } -static struct mtd_info *cfi_intelext_setup(struct map_info *map) +static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long offset = 0; int i,j; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; @@ -257,37 +429,21 @@ mtd->eraseregions[i].numblocks); } - /* Also select the correct geometry setup too */ - mtd->erase = cfi_intelext_erase_varsize; - mtd->read = cfi_intelext_read; +#ifdef CONFIG_MTD_OTP + mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; + mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg; + mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; + mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info; + mtd->get_user_prot_info = cfi_intelext_get_user_prot_info; +#endif - if(map->point && map->unpoint){ - mtd->point = do_point; - mtd->unpoint = do_unpoint; - } + /* This function has the potential to distort the reality + a bit and therefore should be called last. */ + if (cfi_intelext_partition_fixup(mtd, &cfi) != 0) + goto setup_err; -#ifndef FORCE_WORD_WRITE - if ( cfi->cfiq->BufWriteTimeoutTyp ) { - printk("Using buffer write method\n" ); - mtd->write = cfi_intelext_write_buffers; - } else { -#else - { -#endif - printk("Using word write method\n" ); - mtd->write = cfi_intelext_write_words; - } - mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; - mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; - 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; + __module_get(THIS_MODULE); return mtd; setup_err: @@ -297,82 +453,584 @@ kfree(mtd); } kfree(cfi->cmdset_priv); - kfree(cfi->cfiq); return NULL; } -static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) +static int cfi_intelext_partition_fixup(struct mtd_info *mtd, + struct cfi_private **pcfi) { - cfi_word status, status_OK; - unsigned long timeo; - DECLARE_WAITQUEUE(wait, current); - unsigned long cmd_addr; - struct cfi_private *cfi = map->fldrv_priv; + struct map_info *map = mtd->priv; + struct cfi_private *cfi = *pcfi; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; - adr += chip->start; + /* + * Probing of multi-partition flash ships. + * + * To support multiple partitions when available, we simply arrange + * for each of them to have their own flchip structure even if they + * are on the same physical chip. This means completely recreating + * a new cfi_private structure right here which is a blatent code + * layering violation, but this is still the least intrusive + * arrangement at this point. This can be rearranged in the future + * if someone feels motivated enough. --nico + */ + if (extp && extp->MajorVersion == '1' && extp->MinorVersion == '3' + && extp->FeatureSupport & (1 << 9)) { + struct cfi_private *newcfi; + struct flchip *chip; + struct flchip_shared *shared; + int offs, numregions, numparts, partshift, numvirtchips, i, j; - /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + /* Protection Register info */ + offs = (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); - /* Let's determine this according to the interleave only once */ - status_OK = CMD(0x80); + /* Burst Read info */ + offs += 6; + /* Number of partition regions */ + numregions = extp->extra[offs]; + offs += 1; + + /* Number of hardware partitions */ + numparts = 0; + for (i = 0; i < numregions; i++) { + struct cfi_intelext_regioninfo *rinfo; + rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs]; + numparts += rinfo->NumIdentPartitions; + offs += sizeof(*rinfo) + + (rinfo->NumBlockTypes - 1) * + sizeof(struct cfi_intelext_blockinfo); + } + + /* + * All functions below currently rely on all chips having + * the same geometry so we'll just assume that all hardware + * partitions are of the same size too. + */ + partshift = cfi->chipshift - __ffs(numparts); + + if ((1 << partshift) < mtd->erasesize) { + printk( KERN_ERR + "%s: bad number of hw partitions (%d)\n", + __FUNCTION__, numparts); + return -EINVAL; + } + + numvirtchips = cfi->numchips * numparts; + newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); + if (!newcfi) + return -ENOMEM; + shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL); + if (!shared) { + kfree(newcfi); + return -ENOMEM; + } + memcpy(newcfi, cfi, sizeof(struct cfi_private)); + newcfi->numchips = numvirtchips; + newcfi->chipshift = partshift; + + chip = &newcfi->chips[0]; + for (i = 0; i < cfi->numchips; i++) { + shared[i].writing = shared[i].erasing = NULL; + spin_lock_init(&shared[i].lock); + for (j = 0; j < numparts; j++) { + *chip = cfi->chips[i]; + chip->start += j << partshift; + chip->priv = &shared[i]; + /* those should be reset too since + they create memory references. */ + init_waitqueue_head(&chip->wq); + spin_lock_init(&chip->_spinlock); + chip->mutex = &chip->_spinlock; + chip++; + } + } + + printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips " + "--> %d partitions of %d KiB\n", + map->name, cfi->numchips, cfi->interleave, + newcfi->numchips, 1<<(newcfi->chipshift-10)); + + map->fldrv_priv = newcfi; + *pcfi = newcfi; + kfree(cfi); + } + + return 0; +} + +/* + * *********** CHIP ACCESS FUNCTIONS *********** + */ + +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) +{ + DECLARE_WAITQUEUE(wait, current); + struct cfi_private *cfi = map->fldrv_priv; + map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); + unsigned long timeo; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + resettime: timeo = jiffies + HZ; retry: + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { + /* + * OK. We have possibility for contension on the write/erase + * operations which are global to the real chip and not per + * partition. So let's fight it over in the partition which + * currently has authority on the operation. + * + * The rules are as follows: + * + * - any write operation must own shared->writing. + * + * - any erase operation must own _both_ shared->writing and + * shared->erasing. + * + * - contension arbitration is handled in the owner's context. + * + * The 'shared' struct can be read when its lock is taken. + * However any writes to it can only be made when the current + * owner's lock is also held. + */ + struct flchip_shared *shared = chip->priv; + struct flchip *contender; + spin_lock(&shared->lock); + contender = shared->writing; + if (contender && contender != chip) { + /* + * The engine to perform desired operation on this + * partition is already in use by someone else. + * Let's fight over it in the context of the chip + * currently using it. If it is possible to suspend, + * that other partition will do just that, otherwise + * it'll happily send us to sleep. In any case, when + * get_chip returns success we're clear to go ahead. + */ + int ret = spin_trylock(contender->mutex); + spin_unlock(&shared->lock); + if (!ret) + goto retry; + spin_unlock(chip->mutex); + ret = get_chip(map, contender, contender->start, mode); spin_lock(chip->mutex); + if (ret) { + spin_unlock(contender->mutex); + return ret; + } + timeo = jiffies + HZ; + spin_lock(&shared->lock); + } + + /* We now own it */ + shared->writing = chip; + if (mode == FL_ERASING) + shared->erasing = chip; + if (contender && contender != chip) + spin_unlock(contender->mutex); + spin_unlock(&shared->lock); + } - /* 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_READY: - case FL_POINT: + case FL_STATUS: + for (;;) { + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; + /* At this point we're fine with write operations + in other partitions as they don't conflict. */ + if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) + break; + + if (time_after(jiffies, timeo)) { + printk(KERN_ERR "Waiting for chip to be ready timed out. Status %lx\n", + status.x[0]); + return -EIO; + } + spin_unlock(chip->mutex); + cfi_udelay(1); + spin_lock(chip->mutex); + /* Someone else might have been playing with it. */ + goto retry; + } + + case FL_READY: case FL_CFI_QUERY: case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_addr); - chip->state = FL_STATUS; + return 0; - 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; + case FL_ERASING: + if (!cfip || + !(cfip->FeatureSupport & 2) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) + goto sleep; + + + /* Erase suspend */ + map_write(map, CMD(0xB0), adr); + + /* If the flash has finished erasing, then 'erase suspend' + * appears to make some (28F320) flash devices switch to + * 'read' mode. Make sure that we switch to 'read status' + * mode so we get the right data. --rmk + */ + map_write(map, CMD(0x70), adr); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; + chip->erase_suspended = 1; + for (;;) { + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; - } - /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); + /* Urgh. Resume and pretend we weren't here. */ + map_write(map, CMD(0xd0), adr); + /* Make sure we're in 'read status' mode if it had finished */ + map_write(map, CMD(0x70), adr); + chip->state = FL_ERASING; + chip->oldstate = FL_READY; + printk(KERN_ERR "Chip not ready after erase " + "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } - /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); cfi_udelay(1); - goto retry; + spin_lock(chip->mutex); + /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. + So we can just loop here. */ + } + chip->state = FL_STATUS; + return 0; + + case FL_XIP_WHILE_ERASING: + if (mode != FL_READY && mode != FL_POINT && + (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) + goto sleep; + chip->oldstate = chip->state; + chip->state = FL_READY; + return 0; + + case FL_POINT: + /* Only if there's no operation suspended... */ + if (mode == FL_READY && chip->oldstate == FL_READY) + return 0; default: - /* Stick ourselves on a wait queue to be woken when - someone changes the status */ + sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + spin_lock(chip->mutex); + goto resettime; + } +} + +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + + if (chip->priv) { + struct flchip_shared *shared = chip->priv; + spin_lock(&shared->lock); + if (shared->writing == chip && chip->oldstate == FL_READY) { + /* We own the ability to write, but we're done */ + shared->writing = shared->erasing; + if (shared->writing && shared->writing != chip) { + /* give back ownership to who we loaned it from */ + struct flchip *loaner = shared->writing; + spin_lock(loaner->mutex); + spin_unlock(&shared->lock); + spin_unlock(chip->mutex); + put_chip(map, loaner, loaner->start); + spin_lock(chip->mutex); + spin_unlock(loaner->mutex); + wake_up(&chip->wq); + return; + } + shared->erasing = NULL; + shared->writing = NULL; + } else if (shared->erasing == chip && shared->writing != chip) { + /* + * We own the ability to erase without the ability + * to write, which means the erase was suspended + * and some other partition is currently writing. + * Don't let the switch below mess things up since + * we don't have ownership to resume anything. + */ + spin_unlock(&shared->lock); + wake_up(&chip->wq); + return; + } + spin_unlock(&shared->lock); + } + + switch(chip->oldstate) { + case FL_ERASING: + 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. */ + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), adr); + chip->oldstate = FL_READY; + chip->state = FL_ERASING; + break; + + case FL_XIP_WHILE_ERASING: + chip->state = chip->oldstate; + chip->oldstate = FL_READY; + break; + + case FL_READY: + case FL_STATUS: + case FL_JEDEC_QUERY: + /* We should really make set_vpp() count, rather than doing this */ + DISABLE_VPP(map); + break; + default: + printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate); + } + wake_up(&chip->wq); +} + +#ifdef CONFIG_MTD_XIP + +/* + * No interrupt what so ever can be serviced while the flash isn't in array + * mode. This is ensured by the xip_disable() and xip_enable() functions + * enclosing any code path where the flash is known not to be in array mode. + * And within a XIP disabled code path, only functions marked with __xipram + * may be called and nothing else (it's a good thing to inspect generated + * assembly to make sure inline functions were actually inlined and that gcc + * didn't emit calls to its own support functions). Also configuring MTD CFI + * support to a single buswidth and a single interleave is also recommended. + * Note that not only IRQs are disabled but the preemption count is also + * increased to prevent other locking primitives (namely spin_unlock) from + * decrementing the preempt count to zero and scheduling the CPU away while + * not in array mode. + */ + +static void xip_disable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + /* TODO: chips with no XIP use should ignore and return */ + (void) map_read(map, adr); /* ensure mmu mapping is up to date */ + preempt_disable(); + local_irq_disable(); +} + +static void __xipram xip_enable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xff), adr); + chip->state = FL_READY; } + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ + local_irq_enable(); + preempt_enable(); +} + +/* + * When a delay is required for the flash operation to complete, the + * xip_udelay() function is polling for both the given timeout and pending + * (but still masked) hardware interrupts. Whenever there is an interrupt + * pending then the flash erase or write operation is suspended, array mode + * restored and interrupts unmasked. Task scheduling might also happen at that + * point. The CPU eventually returns from the interrupt or the call to + * schedule() and the suspended flash operation is resumed for the remaining + * of the delay period. + * + * Warning: this function _will_ fool interrupt latency tracing tools. + */ + +static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, + unsigned long adr, int usec) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + map_word status, OK = CMD(0x80); + unsigned long suspended, start = xip_currtime(); + flstate_t oldstate, newstate; + + do { + cpu_relax(); + if (xip_irqpending() && cfip && + ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || + (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && + (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { + /* + * Let's suspend the erase or write operation when + * supported. Note that we currently don't try to + * suspend interleaved chips if there is already + * another operation suspended (imagine what happens + * when one chip was already done with the current + * operation while another chip suspended it, then + * we resume the whole thing at once). Yes, it + * can happen! + */ + map_write(map, CMD(0xb0), adr); + map_write(map, CMD(0x70), adr); + usec -= xip_elapsed_since(start); + suspended = xip_currtime(); + do { + if (xip_elapsed_since(suspended) > 100000) { + /* + * The chip doesn't want to suspend + * after waiting for 100 msecs. + * This is a critical error but there + * is not much we can do here. + */ + return; + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK)); + + /* Suspend succeeded */ + oldstate = chip->state; + if (oldstate == FL_ERASING) { + if (!map_word_bitsset(map, status, CMD(0x40))) + break; + newstate = FL_XIP_WHILE_ERASING; + chip->erase_suspended = 1; + } else { + if (!map_word_bitsset(map, status, CMD(0x04))) + break; + newstate = FL_XIP_WHILE_WRITING; + chip->write_suspended = 1; + } + chip->state = newstate; + map_write(map, CMD(0xff), adr); + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); + preempt_enable(); + asm volatile (".rep 8; nop; .endr"); + cond_resched(); + + /* + * We're back. However someone else might have + * decided to go write to the chip if we are in + * a suspended erase state. If so let's wait + * until it's done. + */ + preempt_disable(); + while (chip->state != newstate) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + preempt_enable(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + preempt_disable(); + } + /* Disallow XIP again */ + local_irq_disable(); + + /* Resume the write or erase operation */ + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), adr); + chip->state = oldstate; + start = xip_currtime(); + } else if (usec >= 1000000/HZ) { + /* + * Try to save on CPU power when waiting delay + * is at least a system timer tick period. + * No need to be extremely accurate here. + */ + xip_cpu_idle(); + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK) + && xip_elapsed_since(start) < usec); +} + +#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) + +/* + * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while + * the flash is actively programming or erasing since we have to poll for + * the operation to complete anyway. We can't do that in a generic way with + * a XIP setup so do it before the actual flash operation in this case. + */ +#undef INVALIDATE_CACHED_RANGE +#define INVALIDATE_CACHED_RANGE(x...) +#define XIP_INVAL_CACHED_RANGE(map, from, size) \ + do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) + +/* + * Extra notes: + * + * Activating this XIP support changes the way the code works a bit. For + * example the code to suspend the current process when concurrent access + * happens is never executed because xip_udelay() will always return with the + * same chip state as it was entered with. This is why there is no care for + * the presence of add_wait_queue() or schedule() calls from within a couple + * xip_disable()'d areas of code, like in do_erase_oneblock for example. + * The queueing and scheduling are always happening within xip_udelay(). + * + * Similarly, get_chip() and put_chip() just happen to always be executed + * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state + * is in array mode, therefore never executing many cases therein and not + * causing any problem with XIP. + */ + +#else + +#define xip_disable(map, chip, adr) +#define xip_enable(map, chip, adr) + +#define UDELAY(map, chip, adr, usec) cfi_udelay(usec) + +#define XIP_INVAL_CACHED_RANGE(x...) + +#endif + +static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) +{ + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(map_bankwidth(map)-1); + + spin_lock(chip->mutex); + + ret = get_chip(map, chip, cmd_addr, FL_POINT); + + if (!ret) { + if (chip->state != FL_POINT && chip->state != FL_READY) + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_POINT; chip->ref_point_counter++; + } spin_unlock(chip->mutex); - return 0; + + return ret; } -static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) + +static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; @@ -380,12 +1038,10 @@ int chipnum; int ret = 0; - if (from + len > mtd->size) + if (!map->virt || (from + len > mtd->size)) return -EINVAL; - *mtdbuf = map->point(map, from, len); - if(*mtdbuf == NULL) - return -EINVAL; /* can not point this region */ + *mtdbuf = (void *)map->virt + from; *retlen = 0; /* Now lock the chip(s) to POINT state */ @@ -418,14 +1074,13 @@ return 0; } -static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs; int chipnum; - map->unpoint(map, addr, from, len); /* Now unlock the chip(s) POINT state */ /* ofs: offset within the first chip that the first read should start */ @@ -446,13 +1101,14 @@ thislen = len; spin_lock(chip->mutex); - if(chip->state == FL_POINT){ + if (chip->state == FL_POINT) { chip->ref_point_counter--; if(chip->ref_point_counter == 0) chip->state = FL_READY; } else - printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ - wake_up(&chip->wq); + printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ + + put_chip(map, chip, chip->start); spin_unlock(chip->mutex); len -= thislen; @@ -463,136 +1119,32 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) { - cfi_word status, status_OK; - unsigned long timeo; - DECLARE_WAITQUEUE(wait, current); - int suspended = 0; unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; + int ret; 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); + cmd_addr = adr & ~(map_bankwidth(map)-1); - timeo = jiffies + HZ; - retry: spin_lock(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: - if (!cfi->cmdset_priv || - !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) - goto sleep; /* We don't support erase suspend */ - - cfi_write (map, CMD(0xb0), cmd_addr); - /* If the flash has finished erasing, then 'erase suspend' - * appears to make some (28F320) flash devices switch to - * 'read' mode. Make sure that we switch to 'read status' - * mode so we get the right data. --rmk - */ - cfi_write(map, CMD(0x70), 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); - /* make sure we're in 'read status' mode */ - cfi_write(map, CMD(0x70), cmd_addr); - chip->state = FL_ERASING; - spin_unlock(chip->mutex); - printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%llx\n", (__u64)status); - return -EIO; - } - + ret = get_chip(map, chip, cmd_addr, FL_READY); + if (ret) { spin_unlock(chip->mutex); - cfi_udelay(1); - spin_lock(chip->mutex); + return ret; } - 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: - case FL_POINT: - break; - - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_addr); - chip->state = FL_STATUS; + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xff), cmd_addr); - 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(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(chip->mutex); - cfi_udelay(1); - goto retry; - - default: - sleep: - /* 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(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; - } - - map->copy_from(map, buf, adr, len); + 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); - } + put_chip(map, chip, cmd_addr); - wake_up(&chip->wq); spin_unlock(chip->mutex); return 0; } @@ -636,232 +1188,50 @@ return ret; } -static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int ofs_factor = cfi->interleave * cfi->device_type; - int count=len; - struct flchip *chip; - int chip_num,offst; - unsigned long timeo; - DECLARE_WAITQUEUE(wait, current); - - chip=0; - /* Calculate which chip & protection register offset we need */ - chip_num=((unsigned int)from/reg_sz); - offst=from-(reg_sz*chip_num)+base_offst; - - while(count){ - - if(chip_num>=cfi->numchips) - goto out; - - /* Make sure that the chip is in the right state */ - - timeo = jiffies + HZ; - chip=&cfi->chips[chip_num]; - retry: - spin_lock(chip->mutex); - - switch (chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - break; - - 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(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; - } - - /* Now read the data required from this flash */ - - cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); - while(count && ((offst-base_offst)read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); - buf++; - offst++; - count--; - } - - chip->state=FL_CFI_QUERY; - spin_unlock(chip->mutex); - /* Move on to the next chip */ - chip_num++; - offst=base_offst; - - } - - out: - wake_up(&chip->wq); - return len-count; -} - -static int cfi_intelext_read_user_prot_reg (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; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=(1<FactProtRegSize); - reg_sz=(1<UserProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} - -static int cfi_intelext_read_fact_prot_reg (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; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=0; - reg_sz=(1<FactProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} - - -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum) +static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, + unsigned long adr, map_word datum, int mode) { struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp = cfi->cmdset_priv; - cfi_word status, status_OK; + map_word status, status_OK, write_cmd; unsigned long timeo; - DECLARE_WAITQUEUE(wait, current); - int z, suspended=0, ret=0; + int z, 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(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(chip->mutex); - printk(KERN_ERR "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(chip->mutex); - cfi_udelay(1); - goto retry; - - case FL_ERASING: - if (!extp || - !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) - goto sleep; /* We don't support erase suspend */ - - cfi_write (map, CMD(0xb0), adr); - - /* If the flash has finished erasing, then 'erase suspend' - * appears to make some (28F320) flash devices switch to - * 'read' mode. Make sure that we switch to 'read status' - * mode so we get the right data. --rmk - */ - cfi_write(map, CMD(0x70), adr); - chip->oldstate = FL_ERASING; - chip->state = FL_ERASE_SUSPENDING; - for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) - break; - - if (time_after(jiffies, timeo)) { - /* Urgh */ - cfi_write(map, CMD(0xd0), adr); - /* make sure we're in 'read status' mode */ - cfi_write(map, CMD(0x70), adr); - chip->state = FL_ERASING; - spin_unlock(chip->mutex); - printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%x\n", status); - return -EIO; + switch (mode) { + case FL_WRITING: write_cmd = CMD(0x40); break; + case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; + default: return -EINVAL; } - spin_unlock(chip->mutex); - cfi_udelay(1); spin_lock(chip->mutex); - } - suspended = 1; - chip->state = FL_STATUS; - break; - - default: - sleep: - /* 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); + ret = get_chip(map, chip, adr, mode); + if (ret) { spin_unlock(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); - cfi_write(map, CMD(0x40), adr); - cfi_write(map, datum, adr); - chip->state = FL_WRITING; + xip_disable(map, chip, adr); + map_write(map, write_cmd, adr); + map_write(map, datum, adr); + chip->state = mode; spin_unlock(chip->mutex); - cfi_udelay(chip->word_write_time); + INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); + UDELAY(map, chip, adr, chip->word_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); z = 0; for (;;) { - if (chip->state != FL_WRITING) { + if (chip->state != mode) { /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); @@ -872,14 +1242,14 @@ continue; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; - DISABLE_VPP(map); + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); ret = -EIO; goto out; @@ -888,7 +1258,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); z++; - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } if (!z) { @@ -901,34 +1271,20 @@ /* Done and happy. */ chip->state = FL_STATUS; + /* check for lock bit */ - if (status & CMD(0x02)) { + if (map_word_bitsset(map, status, CMD(0x02))) { /* clear status */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); ret = -EROFS; - goto out; } - out: - 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), adr); - cfi_write(map, CMD(0x70), adr); - } else - DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ - wake_up(&chip->wq); + xip_enable(map, chip, adr); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); + return ret; } @@ -949,35 +1305,22 @@ 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); + if (ofs & (map_bankwidth(map)-1)) { + unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); int gap = ofs - bus_ofs; - int i = 0, n = 0; - u_char tmp_buf[8]; - cfi_word 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; + int n; + map_word datum; - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else if (cfi_buswidth_is_8()) { - datum = *(__u64*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } + n = min_t(int, len, map_bankwidth(map)-gap); + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, gap, n); ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum); + bus_ofs, datum, FL_WRITING); if (ret) return ret; + len -= n; ofs += n; buf += n; (*retlen) += n; @@ -990,30 +1333,18 @@ } } - while(len >= CFIDEV_BUSWIDTH) { - cfi_word 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 if (cfi_buswidth_is_8()) { - datum = *(__u64*)buf; - } else { - return -EINVAL; - } + while(len >= map_bankwidth(map)) { + map_word datum = map_word_load(map, buf); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; - ofs += CFIDEV_BUSWIDTH; - buf += CFIDEV_BUSWIDTH; - (*retlen) += CFIDEV_BUSWIDTH; - len -= CFIDEV_BUSWIDTH; + ofs += map_bankwidth(map); + buf += map_bankwidth(map); + (*retlen) += map_bankwidth(map); + len -= map_bankwidth(map); if (ofs >> cfi->chipshift) { chipnum ++; @@ -1023,203 +1354,126 @@ } } - if (len & (CFIDEV_BUSWIDTH-1)) { - int i = 0, n = 0; - u_char tmp_buf[8]; - cfi_word datum; - - while (len--) - tmp_buf[i++] = buf[n++]; - while (i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = 0xff; + if (len & (map_bankwidth(map)-1)) { + map_word datum; - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else if (cfi_buswidth_is_8()) { - datum = *(__u64*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, 0, len); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; - (*retlen) += n; + (*retlen) += len; } return 0; } -static inline int do_write_buffer(struct map_info *map, struct flchip *chip, +static int __xipram 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; - struct cfi_pri_intelext *extp = cfi->cmdset_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long cmd_adr, timeo; - DECLARE_WAITQUEUE(wait, current); - int wbufsize, z, suspended=0, ret=0; + int wbufsize, z, ret=0, bytes, words; - wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + wbufsize = cfi_interleave(cfi) << 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(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: - 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(chip->mutex); - printk(KERN_ERR "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(chip->mutex); - cfi_udelay(1); - goto retry; - - case FL_ERASING: - if (!extp || - !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) - goto sleep; /* We don't support erase suspend */ - - cfi_write (map, CMD(0xb0), adr); - - /* If the flash has finished erasing, then 'erase suspend' - * appears to make some (28F320) flash devices switch to - * 'read' mode. Make sure that we switch to 'read status' - * mode so we get the right data. --rmk - */ - cfi_write(map, CMD(0x70), adr); - chip->oldstate = FL_ERASING; - chip->state = FL_ERASE_SUSPENDING; - for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) - break; - - if (time_after(jiffies, timeo)) { - /* Urgh */ - cfi_write(map, CMD(0xd0), adr); - /* make sure we're in 'read status' mode */ - cfi_write(map, CMD(0x70), adr); - chip->state = FL_ERASING; + ret = get_chip(map, chip, cmd_adr, FL_WRITING); + if (ret) { spin_unlock(chip->mutex); - printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%x\n", status); - return -EIO; + return ret; } - spin_unlock(chip->mutex); - cfi_udelay(1); - spin_lock(chip->mutex); - } - suspended = 1; - chip->state = FL_STATUS; - break; + XIP_INVAL_CACHED_RANGE(map, adr, len); + ENABLE_VPP(map); + xip_disable(map, chip, cmd_adr); - default: - sleep: - /* 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(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; - } - /* We know we're now in FL_STATUS mode, and 'status' is current */ /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise we're just pissing in the wind */ - if (status & CMD(0x30)) { - printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status); - cfi_write(map, CMD(0x50), cmd_adr); - cfi_write(map, CMD(0x70), cmd_adr); + if (chip->state != FL_STATUS) + map_write(map, CMD(0x70), cmd_adr); + status = map_read(map, cmd_adr); + if (map_word_bitsset(map, status, CMD(0x30))) { + xip_enable(map, chip, cmd_adr); + printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); + xip_disable(map, chip, cmd_adr); + map_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); } - ENABLE_VPP(map); + chip->state = FL_WRITING_TO_BUFFER; z = 0; for (;;) { - cfi_write(map, CMD(0xe8), cmd_adr); + map_write(map, CMD(0xe8), cmd_adr); - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); spin_lock(chip->mutex); if (++z > 20) { /* Argh. Not ready for write to buffer */ - cfi_write(map, CMD(0x70), cmd_adr); + map_word Xstatus; + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - DISABLE_VPP(map); - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr)); + Xstatus = map_read(map, cmd_adr); /* Odd. Clear status bits */ - cfi_write(map, CMD(0x50), cmd_adr); - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); + xip_enable(map, chip, cmd_adr); + printk(KERN_ERR "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n", + status.x[0], Xstatus.x[0]); ret = -EIO; goto out; } } /* Write length of data to come */ - cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); + bytes = len & (map_bankwidth(map)-1); + words = len / map_bankwidth(map); + map_write(map, CMD(words - !bytes), 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 if (cfi_buswidth_is_8()) { - map->write64 (map, *((__u64*)buf)++, adr+z); - } else { - DISABLE_VPP(map); - ret = -EINVAL; - goto out; + z = 0; + while(z < words * map_bankwidth(map)) { + map_word datum = map_word_load(map, buf); + map_write(map, datum, adr+z); + + z += map_bankwidth(map); + buf += map_bankwidth(map); } + + if (bytes) { + map_word datum; + + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, 0, bytes); + map_write(map, datum, adr+z); } + /* GO GO GO */ - cfi_write(map, CMD(0xd0), cmd_adr); + map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; spin_unlock(chip->mutex); - cfi_udelay(chip->buffer_write_time); + INVALIDATE_CACHED_RANGE(map, adr, len); + UDELAY(map, chip, cmd_adr, chip->buffer_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1227,6 +1481,7 @@ for (;;) { if (chip->state != FL_WRITING) { /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); @@ -1237,14 +1492,14 @@ continue; } - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; - DISABLE_VPP(map); + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); ret = -EIO; goto out; @@ -1252,7 +1507,7 @@ /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); z++; spin_lock(chip->mutex); } @@ -1266,33 +1521,18 @@ /* Done and happy. */ chip->state = FL_STATUS; + /* check for lock bit */ - if (status & CMD(0x02)) { + if (map_word_bitsset(map, status, CMD(0x02))) { /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); ret = -EROFS; - goto out; } - out: - 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), adr); - cfi_write(map, CMD(0x70), adr); - } else - DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ - wake_up(&chip->wq); + xip_enable(map, chip, cmd_adr); + out: put_chip(map, chip, cmd_adr); spin_unlock(chip->mutex); return ret; } @@ -1302,7 +1542,7 @@ { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; @@ -1315,8 +1555,8 @@ 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 (ofs & (map_bankwidth(map)-1)) { + size_t local_len = (-ofs)&(map_bankwidth(map)-1); if (local_len > len) local_len = len; ret = cfi_intelext_write_words(mtd, to, local_len, @@ -1335,13 +1575,12 @@ } } - /* Write buffer is worth it only if more than one word to write... */ - while(len > CFIDEV_BUSWIDTH) { + while(len) { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); if (size > len) - size = len & ~(CFIDEV_BUSWIDTH-1); + size = len; ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); if (ret) @@ -1359,116 +1598,14 @@ 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; } -typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, void *thunk); - -static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, - loff_t ofs, size_t len, void *thunk) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (ofs > mtd->size) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - 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 && ofs >= 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 (ofs & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (ofs + len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((ofs + len) & (regions[i].erasesize-1)) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - i=first; - - while(len) { - ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); - - 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; - } - } - - return 0; -} - - -static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); @@ -1479,60 +1616,30 @@ /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); - timeo = jiffies + HZ; -retry: + retry: spin_lock(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(chip->mutex); - printk(KERN_ERR "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(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); + ret = get_chip(map, chip, adr, FL_ERASING); + if (ret) { spin_unlock(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, adr); + /* Clear the status register first */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* Now erase */ - cfi_write(map, CMD(0x20), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x20), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - chip->oldstate = 0; + chip->erase_suspended = 0; spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + INVALIDATE_CACHED_RANGE(map, adr, len); + UDELAY(map, chip, adr, chip->erase_time*1000/2); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1550,84 +1657,92 @@ spin_lock(chip->mutex); continue; } - if (chip->oldstate) { + if (chip->erase_suspended) { /* This erase was suspended and resumed. Adjust the timeout */ timeo = jiffies + (HZ*20); /* FIXME */ - chip->oldstate = 0; + chip->erase_suspended = 0; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_word Xstatus; + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n", - adr, (__u64)status, (__u64)cfi_read(map, adr)); + Xstatus = map_read(map, adr); /* Clear status bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); - DISABLE_VPP(map); - spin_unlock(chip->mutex); - return -EIO; + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); + xip_enable(map, chip, adr); + printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n", + adr, status.x[0], Xstatus.x[0]); + ret = -EIO; + goto out; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(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); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - status = cfi_read(map, adr); + status = map_read(map, adr); /* check for lock bit */ - if (status & CMD(0x3a)) { - unsigned char chipstatus = status; - if (status != CMD(status & 0xff)) { - int i; - for (i = 1; i> (cfi->device_type * 8); + if (map_word_bitsset(map, status, CMD(0x3a))) { + unsigned char chipstatus; + + /* Reset the error bits */ + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); + xip_enable(map, chip, adr); + + chipstatus = status.x[0]; + if (!map_word_equal(map, status, CMD(chipstatus))) { + int i, w; + for (w=0; w> (cfi->device_type * 8); } - printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus); } - /* Reset the error bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); + printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", + status.x[0], chipstatus); + } if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); 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%llx\n", (__u64)status); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; - chip->state = FL_STATUS; + put_chip(map, chip, adr); spin_unlock(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); ret = -EIO; } + } else { + xip_enable(map, chip, adr); + ret = 0; } - wake_up(&chip->wq); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret; } @@ -1640,13 +1755,12 @@ ofs = instr->addr; len = instr->len; - ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; } @@ -1658,39 +1772,22 @@ int i; struct flchip *chip; int ret = 0; - DECLARE_WAITQUEUE(wait, current); for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; - retry: spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_SYNCING); - switch(chip->state) { - case FL_READY: - case FL_STATUS: - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: + if (!ret) { 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(chip->mutex); - break; - - default: - /* Not an idle state */ - add_wait_queue(&chip->wq, &wait); - - spin_unlock(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - - goto retry; } + spin_unlock(chip->mutex); } /* Unlock the chips again */ @@ -1709,16 +1806,21 @@ } #ifdef DEBUG_LOCK_BITS -static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - int ofs_factor = cfi->interleave * cfi->device_type; + int status, ofs_factor = cfi->interleave * cfi->device_type; + xip_disable(map, chip, adr+(2*ofs_factor)); cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + chip->state = FL_JEDEC_QUERY; + status = cfi_read_query(map, adr+(2*ofs_factor)); + xip_enable(map, chip, 0); printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - + adr, status); return 0; } #endif @@ -1726,73 +1828,41 @@ #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); + int ret; adr += chip->start; /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); - timeo = jiffies + HZ; -retry: spin_lock(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(chip->mutex); - printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(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); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { spin_unlock(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + return ret; } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); + xip_disable(map, chip, adr); + map_write(map, CMD(0x60), adr); if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { - cfi_write(map, CMD(0x01), adr); + map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; } else BUG(); spin_unlock(chip->mutex); - schedule_timeout(HZ); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1801,30 +1871,34 @@ timeo = jiffies + (HZ*20); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_word Xstatus; + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr)); - DISABLE_VPP(map); + Xstatus = map_read(map, adr); + xip_enable(map, chip, adr); + printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n", + status.x[0], Xstatus.x[0]); + put_chip(map, chip, adr); spin_unlock(chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; - DISABLE_VPP(map); - wake_up(&chip->wq); + xip_enable(map, chip, adr); + put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; } @@ -1836,17 +1910,17 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); #ifdef DEBUG_LOCK_BITS - printk(KERN_DEBUG __FUNCTION__ - "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + printk(KERN_DEBUG "%s: lock status after, ret=%d\n", + __FUNCTION__, ret); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif @@ -1860,22 +1934,281 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); #ifdef DEBUG_LOCK_BITS - printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + printk(KERN_DEBUG "%s: lock status after, ret=%d\n", + __FUNCTION__, ret); + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif return ret; } +#ifdef CONFIG_MTD_OTP + +typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, + u_long data_offset, u_char *buf, u_int size, + u_long prot_offset, u_int groupno, u_int groupsize); + +static int __xipram +do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ret; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + /* let's ensure we're not reading back cached data from array mode */ + if (map->inval_cache) + map->inval_cache(map, chip->start + offset, size); + + xip_disable(map, chip, chip->start); + if (chip->state != FL_JEDEC_QUERY) { + map_write(map, CMD(0x90), chip->start); + chip->state = FL_JEDEC_QUERY; + } + map_copy_from(map, buf, chip->start + offset, size); + xip_enable(map, chip, chip->start); + + /* then ensure we don't keep OTP data in the cache */ + if (map->inval_cache) + map->inval_cache(map, chip->start + offset, size); + + put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + return 0; +} + +static int +do_otp_write(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + int ret; + + while (size) { + unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); + int gap = offset - bus_ofs; + int n = min_t(int, size, map_bankwidth(map)-gap); + map_word datum = map_word_ff(map); + + datum = map_word_load_partial(map, datum, buf, gap, n); + ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); + if (ret) + return ret; + + offset += n; + buf += n; + size -= n; + } + + return 0; +} + +static int +do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + map_word datum; + + /* make sure area matches group boundaries */ + if (size != grpsz) + return -EXDEV; + + datum = map_word_ff(map); + datum = map_word_clr(map, datum, CMD(1 << grpno)); + return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); +} + +static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, + otp_op_t action, int user_regs) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; + struct cfi_intelext_otpinfo *otp; + u_long devsize, reg_prot_offset, data_offset; + u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; + u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; + int ret; + + *retlen = 0; + + /* Check that we actually have some OTP registers */ + if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) + return -ENODATA; + + /* we need real chips here not virtual ones */ + devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; + chip_step = devsize >> cfi->chipshift; + + for (chip_num = 0; chip_num < cfi->numchips; chip_num += chip_step) { + chip = &cfi->chips[chip_num]; + otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; + + /* first OTP region */ + field = 0; + reg_prot_offset = extp->ProtRegAddr; + reg_fact_groups = 1; + reg_fact_size = 1 << extp->FactProtRegSize; + reg_user_groups = 1; + reg_user_size = 1 << extp->UserProtRegSize; + + while (len > 0) { + /* flash geometry fixup */ + data_offset = reg_prot_offset + 1; + data_offset *= cfi->interleave * cfi->device_type; + reg_prot_offset *= cfi->interleave * cfi->device_type; + reg_fact_size *= cfi->interleave; + reg_user_size *= cfi->interleave; + + if (user_regs) { + groups = reg_user_groups; + groupsize = reg_user_size; + /* skip over factory reg area */ + groupno = reg_fact_groups; + data_offset += reg_fact_groups * reg_fact_size; + } else { + groups = reg_fact_groups; + groupsize = reg_fact_size; + groupno = 0; + } + + while (len > 0 && groups > 0) { + if (!action) { + /* + * Special case: if action is NULL + * we fill buf with otp_info records. + */ + struct otp_info *otpinfo; + map_word lockword; + len -= sizeof(struct otp_info); + if (len <= 0) + return -ENOSPC; + ret = do_otp_read(map, chip, + reg_prot_offset, + (u_char *)&lockword, + map_bankwidth(map), + 0, 0, 0); + if (ret) + return ret; + otpinfo = (struct otp_info *)buf; + otpinfo->start = from; + otpinfo->length = groupsize; + otpinfo->locked = + !map_word_bitsset(map, lockword, + CMD(1 << groupno)); + from += groupsize; + buf += sizeof(*otpinfo); + *retlen += sizeof(*otpinfo); + } else if (from >= groupsize) { + from -= groupsize; + data_offset += groupsize; + } else { + int size = groupsize; + data_offset += from; + size -= from; + from = 0; + if (size > len) + size = len; + ret = action(map, chip, data_offset, + buf, size, reg_prot_offset, + groupno, groupsize); + if (ret < 0) + return ret; + buf += size; + len -= size; + *retlen += size; + data_offset += size; + } + groupno++; + groups--; + } + + /* next OTP region */ + if (++field == extp->NumProtectionFields) + break; + reg_prot_offset = otp->ProtRegAddr; + reg_fact_groups = otp->FactGroups; + reg_fact_size = 1 << otp->FactProtRegSize; + reg_user_groups = otp->UserGroups; + reg_user_size = 1 << otp->UserProtRegSize; + otp++; + } + } + + return 0; +} + +static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 0); +} + +static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 1); +} + +static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_write, 1); +} + +static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, + loff_t from, size_t len) +{ + size_t retlen; + return cfi_intelext_otp_walk(mtd, from, len, &retlen, + NULL, do_otp_lock, 1); +} + +static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); + return ret ? : retlen; +} + +static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); + return ret ? : retlen; +} + +#endif + static int cfi_intelext_suspend(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1889,22 +2222,32 @@ spin_lock(chip->mutex); - switch(chip->state) { + switch (chip->state) { case FL_READY: case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY: + if (chip->oldstate == FL_READY) { 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: + } else { + /* There seems to be an operation pending. We must wait for it. */ + printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate); + ret = -EAGAIN; + } break; - default: + /* Should we actually wait? Once upon a time these routines weren't + allowed to. Or should we return -EAGAIN, because the upper layers + ought to have already shut down anything which was using the device + anyway? The latter for now. */ + printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate); ret = -EAGAIN; + case FL_PM_SUSPENDED: break; } spin_unlock(chip->mutex); @@ -1923,6 +2266,7 @@ because we're returning failure, and it didn't get power cycled */ chip->state = chip->oldstate; + chip->oldstate = FL_READY; wake_up(&chip->wq); } spin_unlock(chip->mutex); @@ -1947,8 +2291,8 @@ /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { - cfi_write(map, CMD(0xFF), 0); - chip->state = FL_READY; + map_write(map, CMD(0xFF), cfi->chips[i].start); + chip->oldstate = chip->state = FL_READY; wake_up(&chip->wq); } @@ -1962,6 +2306,7 @@ struct cfi_private *cfi = map->fldrv_priv; kfree(cfi->cmdset_priv); kfree(cfi->cfiq); + kfree(cfi->chips[0].priv); kfree(cfi); kfree(mtd->eraseregions); } @@ -1969,7 +2314,7 @@ static char im_name_1[]="cfi_cmdset_0001"; static char im_name_3[]="cfi_cmdset_0003"; -int __init cfi_intelext_init(void) +static int __init 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); --- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0002.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0002.c @@ -3,19 +3,26 @@ * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) * * Copyright (C) 2000 Crossnet Co. + * Copyright (C) 2004 Arcom Control Systems Ltd * * 2_by_8 routines added by Simon Munton * + * 4_by_16 work by Carolyn J. Smith + * + * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $ * */ +#include #include #include #include #include +#include #include #include @@ -23,15 +30,24 @@ #include #include #include +#include #include +#include #include #define AMD_BOOTLOC_BUG +#define FORCE_WORD_WRITE 0 + +#define MAX_WORD_RETRIES 3 + +#define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_SST 0x00BF +#define SST49LF004B 0x0060 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_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); -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 *); @@ -41,59 +57,213 @@ static void cfi_amdstd_destroy(struct mtd_info *); struct mtd_info *cfi_cmdset_0002(struct map_info *, int); -static struct mtd_info *cfi_amdstd_setup (struct map_info *); +static struct mtd_info *cfi_amdstd_setup (struct mtd_info *); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" static struct mtd_chip_driver cfi_amdstd_chipdrv = { - probe: NULL, /* Not usable directly */ - destroy: cfi_amdstd_destroy, - name: "cfi_cmdset_0002", - module: THIS_MODULE + .probe = NULL, /* Not usable directly */ + .destroy = cfi_amdstd_destroy, + .name = "cfi_cmdset_0002", + .module = THIS_MODULE }; -struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) + +/* #define DEBUG_CFI_FEATURES */ + + +#ifdef DEBUG_CFI_FEATURES +static void cfi_tell_features(struct cfi_pri_amdstd *extp) { - struct cfi_private *cfi = map->fldrv_priv; - unsigned char bootloc; - int ofs_factor = cfi->interleave * cfi->device_type; - int i; - __u8 major, minor; - __u32 base = cfi->chips[0].start; + const char* erase_suspend[3] = { + "Not supported", "Read only", "Read/write" + }; + const char* top_bottom[6] = { + "No WP", "8x8KiB sectors at top & bottom, no WP", + "Bottom boot", "Top boot", + "Uniform, Bottom WP", "Uniform, Top WP" + }; - if (cfi->cfi_mode==CFI_MODE_CFI){ - __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); + printk(" Address sensitive unlock: %s\n", + (extp->SiliconRevision & 1) ? "Not required" : "Required"); - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) + printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); + else + printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); - major = cfi_read_query(map, base + (adr+3)*ofs_factor); - minor = cfi_read_query(map, base + (adr+4)*ofs_factor); + if (extp->BlkProt == 0) + printk(" Block protection: Not supported\n"); + else + printk(" Block protection: %d sectors per group\n", extp->BlkProt); - printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", - major, minor, adr); - cfi_send_gen_cmd(0xf0, 0x55, base, 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); + printk(" Temporary block unprotect: %s\n", + extp->TmpBlkUnprotect ? "Supported" : "Not supported"); + printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); + printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); + printk(" Burst mode: %s\n", + extp->BurstMode ? "Supported" : "Not supported"); + if (extp->PageMode == 0) + printk(" Page mode: Not supported\n"); + else + printk(" Page mode: %d word page\n", extp->PageMode << 2); + + printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", + extp->VppMin >> 4, extp->VppMin & 0xf); + printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", + extp->VppMax >> 4, extp->VppMax & 0xf); + + if (extp->TopBottom < ARRAY_SIZE(top_bottom)) + printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); + else + printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); +} +#endif - /* Wheee. Bring me the head of someone at AMD. */ #ifdef AMD_BOOTLOC_BUG +/* Wheee. Bring me the head of someone at AMD. */ +static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + __u8 major = extp->MajorVersion; + __u8 minor = extp->MinorVersion; + 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 */ + extp->TopBottom = 3; /* top boot */ } else { - bootloc = 2; /* bottom boot */ + extp->TopBottom = 2; /* bottom boot */ } - } else + } +} #endif - { - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); + +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); + mtd->write = cfi_amdstd_write_buffers; + } +} + +static void fixup_use_secsi(struct mtd_info *mtd, void *param) +{ + /* Setup for chips with a secsi area */ + mtd->read_user_prot_reg = cfi_amdstd_secsi_read; + mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; +} + +static void fixup_use_erase_chip(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if ((cfi->cfiq->NumEraseRegions == 1) && + ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { + mtd->erase = cfi_amdstd_erase_chip; + } + +} + +static struct cfi_fixup cfi_fixup_table[] = { +#ifdef AMD_BOOTLOC_BUG + { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, +#endif + { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, +#endif + { 0, 0, NULL, NULL } +}; +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL }, + { 0, 0, NULL, NULL } +}; + + +struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + int i; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_amdstd_erase_varsize; + mtd->write = cfi_amdstd_write_words; + mtd->read = cfi_amdstd_read; + mtd->sync = cfi_amdstd_sync; + mtd->suspend = cfi_amdstd_suspend; + mtd->resume = cfi_amdstd_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + + if (cfi->cfi_mode==CFI_MODE_CFI){ + unsigned char bootloc; + /* + * It's a real CFI chip, not one for which the probe + * routine faked a CFI structure. So we read the feature + * table from it. + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_amdstd *extp; + + extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); + if (!extp) { + kfree(mtd); + return NULL; + } + + /* Install our own private info structure */ + cfi->cmdset_priv = extp; + + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); + +#ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); +#endif + + bootloc = extp->TopBottom; + if ((bootloc != 2) && (bootloc != 3)) { + printk(KERN_WARNING "%s: CFI does not contain boot " + "bank location. Assuming top.\n", map->name); + bootloc = 2; } + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); @@ -106,29 +276,28 @@ cfi->cfiq->EraseRegionInfo[j] = swap; } } - switch (cfi->device_type) { - case CFI_DEVICETYPE_X8: + /* Set the default CFI lock/unlock addresses */ cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; - break; - case CFI_DEVICETYPE_X16: + /* Modify the unlock address if we are in compatibility mode */ + if ( /* x16 in x8 mode */ + ((cfi->device_type == CFI_DEVICETYPE_X8) && + (cfi->cfiq->InterfaceDesc == 2)) || + /* x32 in x16 mode */ + ((cfi->device_type == CFI_DEVICETYPE_X16) && + (cfi->cfiq->InterfaceDesc == 4))) + { 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: - printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type); - return NULL; } + } /* CFI mode */ + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -138,40 +307,26 @@ map->fldrv = &cfi_amdstd_chipdrv; - cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); - return cfi_amdstd_setup(map); + return cfi_amdstd_setup(mtd); } -static struct mtd_info *cfi_amdstd_setup(struct map_info *map) + +static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; + unsigned long offset = 0; + int i,j; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); printk(KERN_NOTICE "number of %s chips: %d\n", (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); - - if (!mtd) { - printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; - /* Also select the correct geometry setup too */ + /* Select the correct geometry setup */ 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); + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) + * mtd->numeraseregions, GFP_KERNEL); if (!mtd->eraseregions) { printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); goto setup_err; @@ -206,67 +361,12 @@ mtd->eraseregions[i].numblocks); } #endif - } - - switch (CFIDEV_BUSWIDTH) - { - case 1: - case 2: - case 4: -#if 1 - if (mtd->numeraseregions > 1) - mtd->erase = cfi_amdstd_erase_varsize; - else -#endif - if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) - mtd->erase = cfi_amdstd_erase_chip; - else - mtd->erase = cfi_amdstd_erase_onesize; - mtd->read = cfi_amdstd_read; - mtd->write = cfi_amdstd_write; - break; - - default: - printk(KERN_WARNING "Unsupported buswidth\n"); - goto setup_err; - break; - } - if (cfi->fast_prog) { - /* In cfi_amdstd_write() we frob the protection stuff - without paying any attention to the state machine. - This upsets in-progress erases. So we turn this flag - off for now till the code gets fixed. */ - printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); - cfi->fast_prog = 0; - } - - - /* does this chip have a secsi area? */ - if(cfi->mfr==1){ - - switch(cfi->id){ - case 0x50: - case 0x53: - case 0x55: - case 0x56: - case 0x5C: - case 0x5F: - /* Yes */ - mtd->read_user_prot_reg = cfi_amdstd_secsi_read; - mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; - default: - ; - } - } + /* FIXME: erase-suspend-program is broken. See + http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ + printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); - 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; + __module_get(THIS_MODULE); return mtd; setup_err: @@ -280,46 +380,182 @@ return NULL; } -static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +/* + * Return true if the chip is ready. + * + * Ready is one of: read mode, query mode, erase-suspend-read mode (in any + * non-suspended sector) and is indicated by no toggle bits toggling. + * + * Note that anything more complicated than checking if no bits are toggling + * (including checking DQ5 for an error status) is tricky to get working + * correctly and is therefore not done (particulary with interleaved chips + * as each chip must be checked independantly of the others). + */ +static int chip_ready(struct map_info *map, unsigned long addr) +{ + map_word d, t; + + d = map_read(map, addr); + t = map_read(map, addr); + + return map_word_equal(map, d, t); +} + +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) { DECLARE_WAITQUEUE(wait, current); - unsigned long timeo = jiffies + HZ; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long timeo; + struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; + resettime: + timeo = jiffies + HZ; retry: + switch (chip->state) { + + case FL_STATUS: + for (;;) { + if (chip_ready(map, adr)) + break; + + if (time_after(jiffies, timeo)) { + printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); + cfi_spin_unlock(chip->mutex); + return -EIO; + } + cfi_spin_unlock(chip->mutex); + cfi_udelay(1); cfi_spin_lock(chip->mutex); + /* Someone else might have been playing with it. */ + goto retry; + } - if (chip->state != FL_READY){ -#if 0 - printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); -#endif - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); + case FL_READY: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + return 0; + + case FL_ERASING: + if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ + goto sleep; + + if (!(mode == FL_READY || mode == FL_POINT + || !cfip + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) + goto sleep; + + /* We could check to see if we're trying to access the sector + * that is currently being erased. However, no user will try + * anything like that so we just wait for the timeout. */ + + /* Erase suspend */ + /* It's harmless to issue the Erase-Suspend and Erase-Resume + * commands when the erase algorithm isn't in progress. */ + map_write(map, CMD(0xB0), chip->in_progress_block_addr); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; + chip->erase_suspended = 1; + for (;;) { + if (chip_ready(map, adr)) + break; + + if (time_after(jiffies, timeo)) { + /* Should have suspended the erase by now. + * Send an Erase-Resume command as either + * there was an error (so leave the erase + * routine to recover from it) or we trying to + * use the erase-in-progress sector. */ + map_write(map, CMD(0x30), chip->in_progress_block_addr); + chip->state = FL_ERASING; + chip->oldstate = FL_READY; + printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); + return -EIO; + } cfi_spin_unlock(chip->mutex); + cfi_udelay(1); + cfi_spin_lock(chip->mutex); + /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. + So we can just loop here. */ + } + chip->state = FL_READY; + return 0; + case FL_POINT: + /* Only if there's no operation suspended... */ + if (mode == FL_READY && chip->oldstate == FL_READY) + return 0; + + default: + sleep: + 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; + cfi_spin_lock(chip->mutex); + goto resettime; + } +} - goto retry; + +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + + switch(chip->oldstate) { + case FL_ERASING: + chip->state = chip->oldstate; + map_write(map, CMD(0x30), chip->in_progress_block_addr); + chip->oldstate = FL_READY; + chip->state = FL_ERASING; + break; + + case FL_READY: + case FL_STATUS: + /* We should really make set_vpp() count, rather than doing this */ + DISABLE_VPP(map); + break; + default: + printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); } + wake_up(&chip->wq); +} + + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; + int ret; adr += chip->start; + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(map_bankwidth(map)-1); + + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, cmd_addr, FL_READY); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xf0), cmd_addr); chip->state = FL_READY; + } - map->copy_from(map, buf, adr, len); + map_copy_from(map, buf, adr, len); - wake_up(&chip->wq); - cfi_spin_unlock(chip->mutex); + put_chip(map, chip, cmd_addr); + 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; @@ -361,6 +597,7 @@ return ret; } + static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) { DECLARE_WAITQUEUE(wait, current); @@ -398,7 +635,7 @@ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); - map->copy_from(map, buf, adr, len); + map_copy_from(map, buf, adr, len); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); @@ -454,125 +691,118 @@ return ret; } -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) + +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) { - unsigned long timeo = jiffies + HZ; - unsigned int oldstatus, status; - unsigned int dq6, dq5; struct cfi_private *cfi = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = jiffies + HZ; + /* + * We use a 1ms + 1 jiffies generic timeout for writes (most devices + * have a max write time of a few hundreds usec). However, we should + * use the maximum timeout value given by the chip at probe time + * instead. Unfortunately, struct flchip does have a field for + * maximum timeout, only for typical which can be far too short + * depending of the conditions. The ' + 1' is to avoid having a + * timeout of 0 jiffies if HZ is smaller than 1000. + */ + unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; + map_word oldd; + int retry_cnt = 0; - retry: - cfi_spin_lock(chip->mutex); - - if (chip->state != FL_READY) { -#if 0 - printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); -#endif - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); + adr += chip->start; + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { cfi_spin_unlock(chip->mutex); - - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - printk(KERN_DEBUG "Wake up to write:\n"); - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; - - goto retry; + return ret; } - chip->state = FL_WRITING; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", + __func__, adr, datum.x[0] ); - 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); + /* + * Check for a NOP for the case when the datum to write is already + * present - it saves time and works around buggy chips that corrupt + * data at other locations when 0xff is written to a location that + * already contains 0xff. + */ + oldd = map_read(map, adr); + if (map_word_equal(map, oldd, datum)) { + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n", + __func__); + goto op_done; } - cfi_write(map, datum, adr); + ENABLE_VPP(map); + retry: + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + map_write(map, datum, adr); + chip->state = FL_WRITING; cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); cfi_spin_lock(chip->mutex); - /* Polling toggle bits instead of reading back many times - This ensures that write operation is really completed, - or tells us why it failed. */ - dq6 = CMD(1<<6); - dq5 = CMD(1<<5); - timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */ - - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - - while( (status & dq6) != (oldstatus & dq6) && - (status & dq5) != dq5 && - !time_after(jiffies, timeo) ) { + /* See comment above for timeout value. */ + timeo = jiffies + uWriteTimeout; + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); - if (need_resched()) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); - yield(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ cfi_spin_lock(chip->mutex); - } else - udelay(1); - - oldstatus = cfi_read( map, adr ); - status = cfi_read( map, adr ); + continue; } - if( (status & dq6) != (oldstatus & dq6) ) { - /* The erasing didn't stop?? */ - if( (status & dq5) == dq5 ) { - /* When DQ5 raises, we must check once again - if DQ6 is toggling. If not, the erase has been - completed OK. If not, reset chip. */ - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); + if (chip_ready(map, adr)) + goto op_done; - if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) { - printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" ); - } else { - /* DQ5 is active so we can do a reset and stop the erase */ - cfi_write(map, CMD(0xF0), chip->start); - printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" ); - } - } else { - printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword."); + if (time_after(jiffies, timeo)) + break; - chip->state = FL_READY; - wake_up(&chip->wq); + /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); - DISABLE_VPP(map); - ret = -EIO; - } + cfi_udelay(1); + cfi_spin_lock(chip->mutex); } - DISABLE_VPP(map); + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + if (++retry_cnt <= MAX_WORD_RETRIES) + goto retry; + + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); 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) + +static int cfi_amdstd_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, chipstart; + DECLARE_WAITQUEUE(wait, current); *retlen = 0; if (!len) @@ -583,33 +813,52 @@ 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); + if (ofs & (map_bankwidth(map)-1)) { + unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); int i = ofs - bus_ofs; int n = 0; - u_char tmp_buf[4]; - __u32 datum; + map_word tmp_buf; - 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--; + retry: + cfi_spin_lock(cfi->chips[chipnum].mutex); - 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 */ + if (cfi->chips[chipnum].state != FL_READY) { +#if 0 + printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&cfi->chips[chipnum].wq, &wait); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + schedule(); + remove_wait_queue(&cfi->chips[chipnum].wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + goto retry; } + /* Load 'tmp_buf' with old contents of flash */ + tmp_buf = map_read(map, bus_ofs+chipstart); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + /* Number of bytes to copy from buffer */ + n = min_t(int, len, map_bankwidth(map)-i); + + tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n); + ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum, 0); + bus_ofs, tmp_buf); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; + len -= n; if (ofs >> cfi->chipshift) { chipnum ++; @@ -619,505 +868,457 @@ } } - if (cfi->fast_prog) { - /* 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; + while(len >= map_bankwidth(map)) { + map_word datum; + + datum = map_word_load(map, buf); - 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); - } + ofs, datum); + if (ret) return ret; - } - ofs += CFIDEV_BUSWIDTH; - buf += CFIDEV_BUSWIDTH; - (*retlen) += CFIDEV_BUSWIDTH; - len -= CFIDEV_BUSWIDTH; + ofs += map_bankwidth(map); + buf += map_bankwidth(map); + (*retlen) += map_bankwidth(map); + len -= map_bankwidth(map); 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); - } - /* Write the trailing bytes if any */ - if (len & (CFIDEV_BUSWIDTH-1)) { - int i = 0, n = 0; - u_char tmp_buf[4]; - __u32 datum; + if (len & (map_bankwidth(map)-1)) { + map_word tmp_buf; - map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); - while (len--) - tmp_buf[i++] = buf[n++]; + retry1: + cfi_spin_lock(cfi->chips[chipnum].mutex); - 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 */ + if (cfi->chips[chipnum].state != FL_READY) { +#if 0 + printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&cfi->chips[chipnum].wq, &wait); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + schedule(); + remove_wait_queue(&cfi->chips[chipnum].wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + goto retry1; } + tmp_buf = map_read(map, ofs + chipstart); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); + ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum, 0); + ofs, tmp_buf); if (ret) return ret; - (*retlen) += n; + (*retlen) += len; } return 0; } -static inline int do_erase_chip(struct map_info *map, struct flchip *chip) + +/* + * FIXME: interleaved mode not tested, and probably not supported! + */ +static inline int do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) { - unsigned int oldstatus, status; - unsigned int dq6, dq5; - unsigned long timeo = jiffies + HZ; - unsigned int adr; struct cfi_private *cfi = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = jiffies + HZ; + /* see comments in do_write_oneword() regarding uWriteTimeo. */ + unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + int ret = -EIO; + unsigned long cmd_adr; + int z, words; + map_word datum; + + adr += chip->start; + cmd_adr = adr; - retry: cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } - if (chip->state != FL_READY){ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); + datum = map_word_load(map, buf); - cfi_spin_unlock(chip->mutex); + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", + __func__, adr, datum.x[0] ); - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); - goto retry; - } + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); - chip->state = FL_ERASING; + chip->state = FL_WRITING_TO_BUFFER; - /* Handle devices with one erase region, that only implement - * the chip erase command. - */ - 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_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - timeo = jiffies + (HZ*20); - adr = cfi->addr_unlock1; + /* Write length of data to come */ + words = len / map_bankwidth(map); + map_write(map, CMD(words - 1), cmd_adr); + /* Write data */ + z = 0; + while(z < words * map_bankwidth(map)) { + datum = map_word_load(map, buf); + map_write(map, datum, adr + z); - /* Wait for the end of programing/erasure by using the toggle method. - * As long as there is a programming procedure going on, bit 6 of the last - * written byte is toggling it's state with each consectuve read. - * The toggling stops as soon as the procedure is completed. - * - * If the process has gone on for too long on the chip bit 5 gets. - * After bit5 is set you can kill the operation by sending a reset - * command to the chip. - */ - dq6 = CMD(1<<6); - dq5 = CMD(1<<5); + z += map_bankwidth(map); + buf += map_bankwidth(map); + } + z -= map_bankwidth(map); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - while( ((status & dq6) != (oldstatus & dq6)) && - ((status & dq5) != dq5) && - !time_after(jiffies, timeo)) { - int wait_reps; + adr += z; + + /* Write Buffer Program Confirm: GO GO GO */ + map_write(map, CMD(0x29), cmd_adr); + chip->state = FL_WRITING; - /* an initial short sleep */ cfi_spin_unlock(chip->mutex); - schedule_timeout(HZ/100); + cfi_udelay(chip->buffer_write_time); cfi_spin_lock(chip->mutex); - if (chip->state != FL_ERASING) { - /* Someone's suspended the erase. Sleep */ + timeo = jiffies + uWriteTimeout; + + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); + 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 */ + timeo = jiffies + (HZ / 2); /* FIXME */ cfi_spin_lock(chip->mutex); continue; } - /* Busy wait for 1/10 of a milisecond */ - for(wait_reps = 0; - (wait_reps < 100) && - ((status & dq6) != (oldstatus & dq6)) && - ((status & dq5) != dq5); - wait_reps++) { + if (chip_ready(map, adr)) + goto op_done; + + if( time_after(jiffies, timeo)) + break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); - cfi_udelay(1); - cfi_spin_lock(chip->mutex); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); } - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - } - if ((status & dq6) != (oldstatus & dq6)) { - /* The erasing didn't stop?? */ - if ((status & dq5) == dq5) { - /* dq5 is active so we can do a reset and stop the erase */ - cfi_write(map, CMD(0xF0), chip->start); - } - chip->state = FL_READY; - wake_up(&chip->wq); - cfi_spin_unlock(chip->mutex); - printk("waiting for erase to complete timed out."); - DISABLE_VPP(map); - return -EIO; - } - DISABLE_VPP(map); + + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); + + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); - return 0; + return ret; } -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + +static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - unsigned int oldstatus, status; - unsigned int dq6, dq5; - unsigned long timeo = jiffies + HZ; + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + int ret = 0; + int chipnum; + unsigned long ofs; - retry: - cfi_spin_lock(chip->mutex); + *retlen = 0; + if (!len) + return 0; - if (chip->state != FL_READY){ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); - cfi_spin_unlock(chip->mutex); + /* If it's not bus-aligned, do the first word write */ + if (ofs & (map_bankwidth(map)-1)) { + size_t local_len = (-ofs)&(map_bankwidth(map)-1); + if (local_len > len) + local_len = len; + ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), + local_len, retlen, buf); + if (ret) + return ret; + ofs += local_len; + buf += local_len; + len -= local_len; - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } - goto retry; + /* Write buffer is worth it only if more than one word to write... */ + while (len >= map_bankwidth(map) * 2) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) + size = len; + if (size % map_bankwidth(map)) + size -= size % map_bankwidth(map); + + 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; + } } - chip->state = FL_ERASING; + if (len) { + size_t retlen_dregs = 0; - 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); + ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<chipshift), + len, &retlen_dregs, buf); - timeo = jiffies + (HZ*20); + *retlen += retlen_dregs; + return ret; + } - /* Wait for the end of programing/erasure by using the toggle method. - * As long as there is a programming procedure going on, bit 6 of the last - * written byte is toggling it's state with each consectuve read. - * The toggling stops as soon as the procedure is completed. - * - * If the process has gone on for too long on the chip bit 5 gets. - * After bit5 is set you can kill the operation by sending a reset - * command to the chip. + return 0; +} + + +/* + * Handle devices with one erase region, that only implement + * the chip erase command. */ - dq6 = CMD(1<<6); - dq5 = CMD(1<<5); +static inline int do_erase_chip(struct map_info *map, struct flchip *chip) +{ + struct cfi_private *cfi = map->fldrv_priv; + unsigned long timeo = jiffies + HZ; + unsigned long int adr; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - while( ((status & dq6) != (oldstatus & dq6)) && - ((status & dq5) != dq5) && - !time_after(jiffies, timeo)) { - int wait_reps; + adr = cfi->addr_unlock1; - /* an initial short sleep */ + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { cfi_spin_unlock(chip->mutex); - schedule_timeout(HZ/100); + return ret; + } + + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", + __func__, chip->start ); + + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + + chip->state = FL_ERASING; + chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; + + cfi_spin_unlock(chip->mutex); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); + 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); - cfi_spin_unlock(chip->mutex); - printk(KERN_DEBUG "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; } + if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ + chip->erase_suspended = 0; + } - /* Busy wait for 1/10 of a milisecond */ - for(wait_reps = 0; - (wait_reps < 100) && - ((status & dq6) != (oldstatus & dq6)) && - ((status & dq5) != dq5); - wait_reps++) { + if (chip_ready(map, adr)) + goto op_done; + + if (time_after(jiffies, timeo)) + break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); - - cfi_udelay(1); - + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); cfi_spin_lock(chip->mutex); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); } - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - } - if( (status & dq6) != (oldstatus & dq6) ) - { - /* The erasing didn't stop?? */ - if( ( status & dq5 ) == dq5 ) - { - /* When DQ5 raises, we must check once again if DQ6 is toggling. - If not, the erase has been completed OK. If not, reset chip. */ - oldstatus = cfi_read( map, adr ); - status = cfi_read( map, adr ); - if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) ) - { - printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" ); - } - else - { - /* DQ5 is active so we can do a reset and stop the erase */ - cfi_write(map, CMD(0xF0), chip->start); - printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" ); - } - } - else - { - printk( "Waiting for erase to complete timed out in do_erase_oneblock."); + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); - chip->state = FL_READY; - wake_up(&chip->wq); - cfi_spin_unlock(chip->mutex); - DISABLE_VPP(map); - return -EIO; - } - } + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ - DISABLE_VPP(map); + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); - return 0; + + return ret; } -static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { - 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. - */ + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; - i = 0; + adr += chip->start; - /* 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. - */ + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_ERASING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } - while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) - i++; - i--; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", + __func__, adr ); - /* 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. - */ + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + map_write(map, CMD(0x30), adr); - if (instr->addr & (regions[i].erasesize-1)) - return -EINVAL; + chip->state = FL_ERASING; + chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; - /* Remember the erase region we start on */ - first = i; + cfi_spin_unlock(chip->mutex); + msleep(chip->erase_time/2); + cfi_spin_lock(chip->mutex); - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ + timeo = jiffies + (HZ*20); - while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) - i++; + for (;;) { + 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); + schedule(); + remove_wait_queue(&chip->wq, &wait); + cfi_spin_lock(chip->mutex); + continue; + } + if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ + chip->erase_suspended = 0; + } - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; + if (chip_ready(map, adr)) + goto op_done; - if ((instr->addr + instr->len) & (regions[i].erasesize-1)) - return -EINVAL; + if (time_after(jiffies, timeo)) + break; - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); - len = instr->len; + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + cfi_spin_lock(chip->mutex); + } - i=first; + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ - if (ret) + ret = -EIO; + op_done: + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); 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; +int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ + unsigned long ofs, len; + int ret; - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); + ofs = instr->addr; len = instr->len; - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); - + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); 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); + mtd_erase_callback(instr); return 0; } + static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) { struct map_info *map = mtd->priv; @@ -1135,12 +1336,12 @@ return ret; instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; } + static void cfi_amdstd_sync (struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1254,6 +1455,7 @@ return ret; } + static void cfi_amdstd_resume(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1269,7 +1471,7 @@ if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; - cfi_write(map, CMD(0xF0), chip->start); + map_write(map, CMD(0xF0), chip->start); wake_up(&chip->wq); } else @@ -1291,21 +1493,23 @@ static char im_name[]="cfi_cmdset_0002"; -int __init cfi_amdstd_init(void) + +static int __init cfi_amdstd_init(void) { inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); return 0; } + static void __exit cfi_amdstd_exit(void) { inter_module_unregister(im_name); } + module_init(cfi_amdstd_init); module_exit(cfi_amdstd_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); - --- linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0020.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,6 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * + * $Id: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $ * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and @@ -17,10 +18,12 @@ * - added a writev function */ +#include #include #include #include #include +#include #include #include @@ -30,12 +33,13 @@ #include #include #include +#include #include static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs, +static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_staa_sync (struct mtd_info *); @@ -51,10 +55,10 @@ static struct mtd_info *cfi_staa_setup (struct map_info *); static struct mtd_chip_driver cfi_staa_chipdrv = { - probe: NULL, /* Not usable directly */ - destroy: cfi_staa_destroy, - name: "cfi_cmdset_0020", - module: THIS_MODULE + .probe = NULL, /* Not usable directly */ + .destroy = cfi_staa_destroy, + .name = "cfi_cmdset_0020", + .module = THIS_MODULE }; /* #define DEBUG_LOCK_BITS */ @@ -113,7 +117,6 @@ { struct cfi_private *cfi = map->fldrv_priv; int i; - __u32 base = cfi->chips[0].start; if (cfi->cfi_mode) { /* @@ -123,35 +126,10 @@ */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_intelext *extp; - int ofs_factor = cfi->interleave * cfi->device_type; - - printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); - if (!adr) - return NULL; - - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - - extp = kmalloc(sizeof(*extp), GFP_KERNEL); - if (!extp) { - printk(KERN_ERR "Failed to allocate memory\n"); - return NULL; - } - /* Read in the Extended Query Table */ - for (i=0; iMajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { - printk(KERN_WARNING " Unknown staa Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); + extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); + if (!extp) return NULL; - } /* Do some byteswapping if necessary */ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); @@ -172,11 +150,6 @@ cfi->chips[i].erase_time = 1024; } - map->fldrv = &cfi_staa_chipdrv; - MOD_INC_USE_COUNT; - - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_staa_setup(map); } @@ -208,6 +181,7 @@ if (!mtd->eraseregions) { printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); kfree(cfi->cmdset_priv); + kfree(mtd); return NULL; } @@ -232,6 +206,7 @@ printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); kfree(mtd->eraseregions); kfree(cfi->cmdset_priv); + kfree(mtd); return NULL; } @@ -256,7 +231,7 @@ mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */ mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ map->fldrv = &cfi_staa_chipdrv; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); mtd->name = map->name; return mtd; } @@ -264,7 +239,7 @@ 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; + map_word status, status_OK; unsigned long timeo; DECLARE_WAITQUEUE(wait, current); int suspended = 0; @@ -274,7 +249,7 @@ adr += chip->start; /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + cmd_addr = adr & ~(map_bankwidth(map)-1); /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); @@ -288,33 +263,33 @@ */ switch (chip->state) { case FL_ERASING: - if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) + if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) goto sleep; /* We don't support erase suspend */ - cfi_write (map, CMD(0xb0), cmd_addr); + map_write (map, CMD(0xb0), cmd_addr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to * 'read' mode. Make sure that we switch to 'read status' * mode so we get the right data. --rmk */ - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0x70), 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) + status = map_read(map, cmd_addr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; if (time_after(jiffies, timeo)) { /* Urgh */ - cfi_write(map, CMD(0xd0), cmd_addr); + map_write(map, CMD(0xd0), cmd_addr); /* make sure we're in 'read status' mode */ - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%x\n", status); + "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } @@ -324,7 +299,7 @@ } suspended = 1; - cfi_write(map, CMD(0xff), cmd_addr); + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; @@ -338,13 +313,13 @@ case FL_CFI_QUERY: case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_addr); + map_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); + status = map_read(map, cmd_addr); + if (map_word_andequal(map, status, status_OK, status_OK)) { + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; } @@ -352,7 +327,7 @@ /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status); + printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO; } @@ -374,7 +349,7 @@ goto retry; } - map->copy_from(map, buf, adr, len); + map_copy_from(map, buf, adr, len); if (suspended) { chip->state = chip->oldstate; @@ -387,8 +362,8 @@ 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); + map_write(map, CMD(0xd0), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); } wake_up(&chip->wq); @@ -439,16 +414,16 @@ unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + map_word status, status_OK; unsigned long cmd_adr, timeo; DECLARE_WAITQUEUE(wait, current); int wbufsize, z; /* M58LW064A requires bus alignment for buffer wriets -- saw */ - if (adr & (CFIDEV_BUSWIDTH-1)) + if (adr & (map_bankwidth(map)-1)) return -EINVAL; - wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); @@ -474,21 +449,21 @@ case FL_CFI_QUERY: case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; #ifdef DEBUG_CFI_FEATURES - printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr)); + printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); #endif case FL_STATUS: - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, 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(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %x\n", - status, cfi_read(map, cmd_adr)); + printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", + status.x[0], map_read(map, cmd_adr).x[0]); return -EIO; } @@ -510,13 +485,13 @@ } ENABLE_VPP(map); - cfi_write(map, CMD(0xe8), cmd_adr); + map_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) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; spin_unlock_bh(chip->mutex); @@ -526,32 +501,26 @@ if (++z > 100) { /* Argh. Not ready for write to buffer */ DISABLE_VPP(map); - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; spin_unlock_bh(chip->mutex); - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO; } } /* Write length of data to come */ - cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); + map_write(map, CMD(len/map_bankwidth(map)-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; - } + for (z = 0; z < len; + z += map_bankwidth(map), buf += map_bankwidth(map)) { + map_word d; + d = map_word_load(map, buf); + map_write(map, d, adr+z); } /* GO GO GO */ - cfi_write(map, CMD(0xd0), cmd_adr); + map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; spin_unlock_bh(chip->mutex); @@ -573,16 +542,16 @@ continue; } - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; DISABLE_VPP(map); spin_unlock_bh(chip->mutex); @@ -609,18 +578,17 @@ chip->state = FL_STATUS; /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ - if ((status & CMD(0x02)) || (status & CMD(0x08)) || - (status & CMD(0x10)) || (status & CMD(0x20))) { + if (map_word_bitsset(map, status, CMD(0x3a))) { #ifdef DEBUG_CFI_FEATURES - printk("%s: 2 status[%x]\n", __FUNCTION__, status); + printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); #endif /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); wake_up(&chip->wq); spin_unlock_bh(chip->mutex); - return (status & CMD(0x02)) ? -EROFS : -EIO; + return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); @@ -633,7 +601,7 @@ { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; @@ -646,7 +614,7 @@ ofs = to - (chipnum << cfi->chipshift); #ifdef DEBUG_CFI_FEATURES - printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH); + printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); #endif @@ -689,7 +657,7 @@ #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) static int -cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs, +cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { unsigned long i; @@ -758,7 +726,7 @@ 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; + map_word status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); @@ -778,12 +746,12 @@ case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -812,15 +780,15 @@ ENABLE_VPP(map); /* Clear the status register first */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* Now erase */ - cfi_write(map, CMD(0x20), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x20), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -840,15 +808,15 @@ continue; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -864,43 +832,46 @@ ret = 0; /* We've broken this before. It doesn't hurt to be safe */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - status = cfi_read(map, adr); + status = map_read(map, adr); /* check for lock bit */ - if (status & CMD(0x3a)) { - unsigned char chipstatus = status; - if (status != CMD(status & 0xff)) { - int i; - for (i = 1; i> (cfi->device_type * 8); + if (map_word_bitsset(map, status, CMD(0x3a))) { + unsigned char chipstatus = status.x[0]; + if (!map_word_equal(map, status, CMD(chipstatus))) { + int i, w; + for (w=0; w> (cfi->device_type * 8); } - printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); + } + printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", + status.x[0], chipstatus); } /* Reset the error bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); 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); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); 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); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); ret = -EIO; } } @@ -995,8 +966,7 @@ } instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; } @@ -1061,7 +1031,7 @@ 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; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); @@ -1079,12 +1049,12 @@ case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -1112,12 +1082,12 @@ } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0x01), adr); + map_write(map, CMD(0x60), adr); + map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1126,15 +1096,15 @@ timeo = jiffies + (HZ*2); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -1210,7 +1180,7 @@ 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; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); @@ -1228,12 +1198,12 @@ case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -1261,12 +1231,12 @@ } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x60), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1275,15 +1245,15 @@ timeo = jiffies + (HZ*2); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -1412,7 +1382,7 @@ /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { - cfi_write(map, CMD(0xFF), 0); + map_write(map, CMD(0xFF), 0); chip->state = FL_READY; wake_up(&chip->wq); } @@ -1429,23 +1399,20 @@ kfree(cfi); } -#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) -#define cfi_staa_init init_module -#define cfi_staa_exit cleanup_module -#endif - static char im_name[]="cfi_cmdset_0020"; -mod_init_t cfi_staa_init(void) +static int __init cfi_staa_init(void) { inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); return 0; } -mod_exit_t cfi_staa_exit(void) +static void __exit cfi_staa_exit(void) { inter_module_unregister(im_name); } module_init(cfi_staa_init); module_exit(cfi_staa_exit); + +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/chips/cfi_probe.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/cfi_probe.c @@ -1,19 +1,21 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.69 2002/05/11 22:13:03 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $ */ #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,30 +27,80 @@ #endif static int cfi_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi); + unsigned long *chip_map, struct cfi_private *cfi); static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); struct mtd_info *cfi_probe(struct map_info *map); +#ifdef CONFIG_MTD_XIP + +/* only needed for short periods, so this is rather simple */ +#define xip_disable() local_irq_disable() + +#define xip_allowed(base, map) \ +do { \ + (void) map_read(map, base); \ + asm volatile (".rep 8; nop; .endr"); \ + local_irq_enable(); \ +} while (0) + +#define xip_enable(base, map, cfi) \ +do { \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + xip_allowed(base, map); \ +} while (0) + +#define xip_disable_qry(base, map, cfi) \ +do { \ + xip_disable(); \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ +} while (0) + +#else + +#define xip_disable() do { } while (0) +#define xip_allowed(base, map) do { } while (0) +#define xip_enable(base, map, cfi) do { } while (0) +#define xip_disable_qry(base, map, cfi) do { } while (0) + +#endif + /* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ -static inline int qry_present(struct map_info *map, __u32 base, +static int __xipram qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor + map_word val[3]; + map_word qry[3]; - 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 1; // ok ! + qry[0] = cfi_build_cmd('Q', map, cfi); + qry[1] = cfi_build_cmd('R', map, cfi); + qry[2] = cfi_build_cmd('Y', map, cfi); - return 0; // nothing found + val[0] = map_read(map, base + osf*0x10); + val[1] = map_read(map, base + osf*0x11); + val[2] = map_read(map, base + osf*0x12); + + if (!map_word_equal(map, qry[0], val[0])) + return 0; + + if (!map_word_equal(map, qry[1], val[1])) + return 0; + + if (!map_word_equal(map, qry[2], val[2])) + return 0; + + return 1; // "QRY" found } -static int cfi_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi) +static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, + unsigned long *chip_map, struct cfi_private *cfi) { int i; @@ -64,15 +116,16 @@ (unsigned long)base + 0x55, map->size -1); return 0; } - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* some devices don't respond to 0xF0, so send 0xFF to be sure */ + xip_disable(); + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - if (!qry_present(map,base,cfi)) + if (!qry_present(map,base,cfi)) { + xip_enable(base, map, cfi); return 0; + } if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI @@ -81,20 +134,26 @@ } /* Check each previous chip to see if it's an alias */ - for (i=0; inumchips; i++) { + for (i=0; i < (base >> cfi->chipshift); i++) { + unsigned long start; + if(!test_bit(i, chip_map)) { + /* Skip location; no valid chip at this address */ + continue; + } + start = i << cfi->chipshift; /* This chip should be in read mode if it's one we've already touched. */ - if (qry_present(map,chips[i].start,cfi)) { + if (qry_present(map, start, cfi)) { /* Eep. This chip also had the QRY marker. * Is it an alias for the new one? */ - cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); - /* some devices don't respond to 0xF0, so send 0xFF to be sure */ - cfi_send_gen_cmd(0xFF, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); /* If the QRY marker goes away, it's an alias */ - if (!qry_present(map, chips[i].start, cfi)) { + if (!qry_present(map, start, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } /* Yes, it's actually got QRY for data. Most @@ -102,11 +161,12 @@ * too and if it's the same, assume it's an alias. */ /* FIXME: Use other modes to do a proper check */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* some devices don't respond to 0xF0, so send 0xFF to be sure */ - cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); + if (qry_present(map, base, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } } @@ -114,30 +174,22 @@ /* 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; + set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ cfi->numchips++; /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - - /* some devices don't respond to 0xF0, so send 0xFF to be sure */ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); - - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, - map->buswidth*8); + map->bankwidth*8); return 1; } -static int cfi_chip_setup(struct map_info *map, +static int __xipram cfi_chip_setup(struct map_info *map, struct cfi_private *cfi) { int ofs_factor = cfi->interleave*cfi->device_type; @@ -145,6 +197,7 @@ int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + xip_enable(base, map, cfi); #ifdef DEBUG_CFI printk("Number of erase regions: %d\n", num_erase_regions); #endif @@ -160,12 +213,31 @@ memset(cfi->cfiq,0,sizeof(struct cfi_ident)); cfi->cfi_mode = CFI_MODE_CFI; - cfi->fast_prog=1; /* CFI supports fast programming */ /* Read the CFI info structure */ - for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { + xip_disable_qry(base, map, cfi); + 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); - } + + /* Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, 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); + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* ... even if it's an Intel chip */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); @@ -176,20 +248,6 @@ cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc); cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize); - /* - * ST screwed up the CFI interface for buffer writes on their parts, - * so this needs to be fixed up by hand here. - * - * A possible enhancment is that instead of just reverting back - * to word write (as this does), we could use the ST specific double - * word write instead. - */ - - if (cfi_read_query(map,base) == 0x20){ - cfi->cfiq->BufWriteTimeoutTyp = 0; - cfi->cfiq->BufWriteTimeoutMax = 0; - } - #ifdef DEBUG_CFI /* Dump the information therein */ print_cfi_ident(cfi->cfiq); @@ -204,11 +262,10 @@ (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1); #endif } - /* Put it back into Read Mode */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* some devices don't respond to 0xF0, so send 0xFF to be sure */ - cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", + map->name, cfi->interleave, cfi->device_type*8, base, + map->bankwidth*8); return 1; } @@ -232,12 +289,27 @@ case P_ID_AMD_EXT: return "AMD/Fujitsu Extended"; + case P_ID_WINBOND: + return "Winbond Standard"; + + case P_ID_ST_ADV: + return "ST Advanced"; + case P_ID_MITSUBISHI_STD: return "Mitsubishi Standard"; case P_ID_MITSUBISHI_EXT: return "Mitsubishi Extended"; + case P_ID_SST_PAGE: + return "SST Page Write"; + + case P_ID_INTEL_PERFORMANCE: + return "Intel Performance Code"; + + case P_ID_INTEL_DATA: + return "Intel Data"; + case P_ID_RESERVED: return "Not Allowed / Reserved for Future Use"; @@ -268,11 +340,11 @@ 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); + printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); + printk("Vcc Maximum: %2d.%d 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); + printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); + printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); } else printk("No Vpp line\n"); @@ -315,6 +387,10 @@ printk(" - x32-only asynchronous interface\n"); break; + case 4: + printk(" - supports x16 and x32 via Word# with asynchronous interface\n"); + break; + case 65535: printk(" - Not Allowed / Reserved\n"); break; @@ -331,8 +407,8 @@ #endif /* DEBUG_CFI */ static struct chip_probe cfi_chip_probe = { - name: "CFI", - probe_chip: cfi_probe_chip + .name = "CFI", + .probe_chip = cfi_probe_chip }; struct mtd_info *cfi_probe(struct map_info *map) @@ -345,9 +421,9 @@ } static struct mtd_chip_driver cfi_chipdrv = { - probe: cfi_probe, - name: "cfi_probe", - module: THIS_MODULE + .probe = cfi_probe, + .name = "cfi_probe", + .module = THIS_MODULE }; int __init cfi_probe_init(void) --- /dev/null +++ linux-2.4.21/drivers/mtd/chips/cfi_util.c @@ -0,0 +1,196 @@ +/* + * Common Flash Interface support: + * Generic utility functions not dependant on command set + * + * Copyright (C) 2002 Red Hat + * Copyright (C) 2003 STMicroelectronics Limited + * + * This code is covered by the GPL. + * + * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cfi_extquery * +__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 base = 0; // cfi->chips[0].start; + int ofs_factor = cfi->interleave * cfi->device_type; + int i; + struct cfi_extquery *extp = NULL; + + printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); + if (!adr) + goto out; + + extp = kmalloc(size, GFP_KERNEL); + if (!extp) { + printk(KERN_ERR "Failed to allocate memory\n"); + goto out; + } + +#ifdef CONFIG_MTD_XIP + local_irq_disable(); +#endif + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + + /* Read in the Extended Query Table */ + for (i=0; idevice_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + +#ifdef CONFIG_MTD_XIP + (void) map_read(map, base); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); +#endif + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { + printk(KERN_WARNING " Unknown %s Extended Query " + "version %c.%c.\n", name, extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + extp = NULL; + } + + out: return extp; +} + +EXPORT_SYMBOL(cfi_read_pri); + +void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_fixup *f; + + for (f=fixups; f->fixup; f++) { + if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && + ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { + f->fixup(mtd, f->param); + } + } +} + +EXPORT_SYMBOL(cfi_fixup); + +int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, + loff_t ofs, size_t len, void *thunk) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (ofs > mtd->size) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + 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 && ofs >= 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 (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + i=first; + + while(len) { + int size = regions[i].erasesize; + + ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); + + if (ret) + return ret; + + adr += size; + ofs += size; + len -= size; + + if (ofs == regions[i].offset + size * regions[i].numblocks) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + +EXPORT_SYMBOL(cfi_varsize_frob); + +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/chips/chipreg.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/chipreg.c @@ -1,5 +1,5 @@ /* - * $Id: chipreg.c,v 1.13 2002/02/21 08:26:58 dwmw2 Exp $ + * $Id: chipreg.c,v 1.18 2005/01/12 22:34:34 gleixner Exp $ * * Registration for chip drivers * @@ -7,12 +7,15 @@ #include #include +#include #include #include -#include +#include #include +#include +#include -spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(chip_drvs_lock); static LIST_HEAD(chip_drvs_list); void register_mtd_chip_driver(struct mtd_chip_driver *drv) @@ -44,10 +47,8 @@ break; } } - if (ret && !try_inc_mod_count(ret->module)) { - /* Eep. Failed. */ + if (ret && !try_module_get(ret->module)) ret = NULL; - } spin_unlock(&chip_drvs_lock); @@ -64,32 +65,46 @@ drv = get_mtd_chip_driver(name); - if (!drv && !request_module(name)) + if (!drv && !request_module("%s", 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 + module_put(drv->module); if (ret) return ret; return NULL; } +/* + * Destroy an MTD device which was created for a map device. + * Make sure the MTD device is already unregistered before calling this + */ +void map_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + + if (map->fldrv->destroy) + map->fldrv->destroy(mtd); + + module_put(map->fldrv->module); + + kfree(mtd); +} EXPORT_SYMBOL(register_mtd_chip_driver); EXPORT_SYMBOL(unregister_mtd_chip_driver); EXPORT_SYMBOL(do_map_probe); +EXPORT_SYMBOL(map_destroy); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); --- /dev/null +++ linux-2.4.21/drivers/mtd/chips/fwh_lock.h @@ -0,0 +1,107 @@ +#ifndef FWH_LOCK_H +#define FWH_LOCK_H + + +enum fwh_lock_state { + FWH_UNLOCKED = 0, + FWH_DENY_WRITE = 1, + FWH_IMMUTABLE = 2, + FWH_DENY_READ = 4, +}; + +struct fwh_xxlock_thunk { + enum fwh_lock_state val; + flstate_t state; +}; + + +#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) +#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) + +/* + * This locking/unlock is specific to firmware hub parts. Only one + * is known that supports the Intel command set. Firmware + * hub parts cannot be interleaved as they are on the LPC bus + * so this code has not been tested with interleaved chips, + * and will likely fail in that context. + */ +static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; + int ret; + + /* Refuse the operation if the we cannot look behind the chip */ + if (chip->start < 0x400000) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): chip->start: %lx wanted >= 0x400000\n", + __func__, chip->start ); + return -EIO; + } + /* + * lock block registers: + * - on 64k boundariesand + * - bit 1 set high + * - block lock registers are 4MiB lower - overflow subtract (danger) + * + * The address manipulation is first done on the logical address + * which is 0 at the start of the chip, and then the offset of + * the individual chip is addted to it. Any other order a weird + * map offset could cause problems. + */ + adr = (adr & ~0xffffUL) | 0x2; + adr += chip->start - 0x400000; + + /* + * This is easy because these are writes to registers and not writes + * to flash memory - that means that we don't have to check status + * and timeout. + */ + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + chip->state = xxlt->state; + map_write(map, CMD(xxlt->val), adr); + + /* Done and happy. */ + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + return 0; +} + + +static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); + + return ret; +} + + +static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); + + return ret; +} + +static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) +{ + printk(KERN_NOTICE "using fwh lock/unlock method\n"); + /* Setup for the chips with the fwh lock method */ + mtd->lock = fwh_lock_varsize; + mtd->unlock = fwh_unlock_varsize; +} +#endif /* FWH_LOCK_H */ --- linux-2.4.21/drivers/mtd/chips/gen_probe.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/gen_probe.c @@ -1,11 +1,13 @@ /* * Routines common to all CFI-type probes. - * (C) 2001, 2001 Red Hat, Inc. + * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.9 2002/09/05 05:15:32 acurtis Exp $ + * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $ */ #include +#include +#include #include #include #include @@ -48,13 +50,13 @@ EXPORT_SYMBOL(mtd_do_chip_probe); -struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) +static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) { - unsigned long base=0; struct cfi_private cfi; struct cfi_private *retcfi; - struct flchip chip[MAX_CFI_CHIPS]; - int i; + unsigned long *chip_map; + int i, j, mapsize; + int max_chips; memset(&cfi, 0, sizeof(cfi)); @@ -62,7 +64,7 @@ interleave and device type, etc. */ if (!genprobe_new_chip(map, cp, &cfi)) { /* The probe didn't like it */ - printk(KERN_WARNING "%s: Found no %s device at location zero\n", + printk(KERN_DEBUG "%s: Found no %s device at location zero\n", cp->name, map->name); return NULL; } @@ -77,46 +79,47 @@ return NULL; } #endif - chip[0].start = 0; - chip[0].state = FL_READY; cfi.chipshift = cfi.cfiq->DevSize; - switch(cfi.interleave) { -#ifdef CFIDEV_INTERLEAVE_1 - case 1: - break; -#endif -#ifdef CFIDEV_INTERLEAVE_2 - case 2: + if (cfi_interleave_is_1(&cfi)) { + ; + } else if (cfi_interleave_is_2(&cfi)) { cfi.chipshift++; - break; -#endif -#ifdef CFIDEV_INTERLEAVE_4 - case 4: - cfi.chipshift+=2; - break; -#endif - default: + } else if (cfi_interleave_is_4((&cfi))) { + cfi.chipshift += 2; + } else if (cfi_interleave_is_8(&cfi)) { + cfi.chipshift += 3; + } else { BUG(); } cfi.numchips = 1; /* + * Allocate memory for bitmap of valid chips. + * Align bitmap storage size to full byte. + */ + max_chips = map->size >> cfi.chipshift; + mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0); + chip_map = kmalloc(mapsize, GFP_KERNEL); + if (!chip_map) { + printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); + kfree(cfi.cfiq); + return NULL; + } + memset (chip_map, 0, mapsize); + + set_bit(0, chip_map); /* Mark first chip valid */ + + /* * Now probe for other chips, checking sensibly for aliases while * we're at it. The new_chip probe above should have let the first * chip in read mode. - * - * NOTE: Here, we're checking if there is room for another chip - * the same size within the mapping. Therefore, - * base + chipsize <= map->size is the correct thing to do, - * because, base + chipsize would be the _first_ byte of the - * next chip, not the one we're currently pondering. */ - for (base = (1<size; - base += (1<probe_chip(map, base, &chip[0], &cfi); + for (i = 1; i < max_chips; i++) { + cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); + } /* * Now allocate the space for the structures we need to return to @@ -128,19 +131,26 @@ if (!retcfi) { printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); kfree(cfi.cfiq); + kfree(chip_map); return NULL; } memcpy(retcfi, &cfi, sizeof(cfi)); - memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); + memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); - /* Fix up the stuff that breaks when you move it */ - 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; + for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { + if(test_bit(i, chip_map)) { + struct flchip *pchip = &retcfi->chips[j++]; + + pchip->start = (i << cfi.chipshift); + pchip->state = FL_READY; + init_waitqueue_head(&pchip->wq); + spin_lock_init(&pchip->_spinlock); + pchip->mutex = &pchip->_spinlock; + } } + kfree(chip_map); return retcfi; } @@ -148,135 +158,36 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, struct cfi_private *cfi) { - switch (map->buswidth) { -#ifdef CFIDEV_BUSWIDTH_1 - case CFIDEV_BUSWIDTH_1: - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - break; -#endif /* CFIDEV_BUSWITDH_1 */ - -#ifdef CFIDEV_BUSWIDTH_2 - case CFIDEV_BUSWIDTH_2: -#ifdef CFIDEV_INTERLEAVE_1 - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_1 */ -#ifdef CFIDEV_INTERLEAVE_2 - cfi->interleave = CFIDEV_INTERLEAVE_2; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ - break; -#endif /* CFIDEV_BUSWIDTH_2 */ - -#ifdef CFIDEV_BUSWIDTH_4 - case CFIDEV_BUSWIDTH_4: -#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE) - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_1 */ -#ifdef CFIDEV_INTERLEAVE_2 - cfi->interleave = CFIDEV_INTERLEAVE_2; - -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ -#ifdef CFIDEV_INTERLEAVE_4 - cfi->interleave = CFIDEV_INTERLEAVE_4; - -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; + int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ + int max_chips = map_bankwidth(map); /* And minimum 1 */ + int nr_chips, type; - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_4 */ - break; -#endif /* CFIDEV_BUSWIDTH_4 */ + for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { -#ifdef CFIDEV_BUSWIDTH_8 - case CFIDEV_BUSWIDTH_8: -#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE) - cfi->interleave = CFIDEV_INTERLEAVE_2; + if (!cfi_interleave_supported(nr_chips)) + continue; - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ -#ifdef CFIDEV_INTERLEAVE_4 - cfi->interleave = CFIDEV_INTERLEAVE_4; + cfi->interleave = nr_chips; -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_4 */ -#ifdef CFIDEV_INTERLEAVE_8 - cfi->interleave = CFIDEV_INTERLEAVE_8; + /* Minimum device size. Don't look for one 8-bit device + in a 16-bit bus, etc. */ + type = map_bankwidth(map) / nr_chips; - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; + for (; type <= CFI_DEVICETYPE_X32; type<<=1) { + cfi->device_type = type; - cfi->device_type = CFI_DEVICETYPE_X8; if (cp->probe_chip(map, 0, NULL, cfi)) return 1; -#endif /* CFIDEV_INTERLEAVE_8 */ - break; -#endif /* CFIDEV_BUSWIDTH_8 */ - - default: - printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth); - return 0; + } } return 0; } - typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); extern cfi_cmdset_fn_t cfi_cmdset_0001; extern cfi_cmdset_fn_t cfi_cmdset_0002; +extern cfi_cmdset_fn_t cfi_cmdset_0020; static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, int primary) --- linux-2.4.21/drivers/mtd/chips/jedec.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/jedec.c @@ -11,10 +11,16 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.14 2002/06/27 02:19:12 dwmw2 Exp $ + * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $ */ +#include +#include +#include #include +#include +#include +#include static struct mtd_info *jedec_probe(struct map_info *); static int jedec_probe8(struct map_info *map,unsigned long base, @@ -33,14 +39,51 @@ /* 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 const struct JEDECTable JEDEC_table[] = { + { + .jedec = 0x013D, + .name = "AMD Am29F017D", + .size = 2*1024*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { + .jedec = 0x01AD, + .name = "AMD Am29F016", + .size = 2*1024*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { + .jedec = 0x01D5, + .name = "AMD Am29F080", + .size = 1*1024*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { + .jedec = 0x01A4, + .name = "AMD Am29F040", + .size = 512*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { + .jedec = 0x20E3, + .name = "AMD Am29W040B", + .size = 512*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { + .jedec = 0xC2AD, + .name = "Macronix MX29F016", + .size = 2*1024*1024, + .sectorsize = 64*1024, + .capabilities = MTD_CAP_NORFLASH + }, + { .jedec = 0x0 } +}; static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id); static void jedec_sync(struct mtd_info *mtd) {}; @@ -54,9 +97,9 @@ static struct mtd_chip_driver jedec_chipdrv = { - probe: jedec_probe, - name: "jedec", - module: THIS_MODULE + .probe = jedec_probe, + .name = "jedec", + .module = THIS_MODULE }; /* Probe entry point */ @@ -85,7 +128,7 @@ { printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n"); kfree(MTD); - return 0; + return NULL; } for (Base = 0; Base < map->size; Base += my_bank_size) @@ -98,7 +141,7 @@ if (jedec_probe8(map,Base,priv) == 0) { printk("did recognize jedec chip\n"); kfree(MTD); - return 0; + return NULL; } } if (map->buswidth == 2) @@ -124,15 +167,14 @@ { printk("mtd: Failed. Device has incompatible mixed sector sizes\n"); kfree(MTD); - return 0; + return NULL; } } /* 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; + strlcpy(Part,map->name,sizeof(Part)-10); strcat(Part," "); Uniq = 0; for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) @@ -151,7 +193,7 @@ { printk("mtd: Internal Error, JEDEC not set\n"); kfree(MTD); - return 0; + return NULL; } if (Uniq != 0) @@ -179,7 +221,7 @@ if (!priv->size) { printk("priv->size is zero\n"); kfree(MTD); - return 0; + return NULL; } if (priv->size/my_bank_size) { if (priv->size/my_bank_size == 1) { @@ -198,7 +240,7 @@ { printk("mtd: Failed. Cannot handle unsymmetric banking\n"); kfree(MTD); - return 0; + return NULL; } } } @@ -209,8 +251,7 @@ // printk("Part: '%s'\n",Part); memset(MTD,0,sizeof(*MTD)); - // strncpy(MTD->name,Part,sizeof(MTD->name)); - // MTD->name[sizeof(MTD->name)-1] = 0; + // strlcpy(MTD->name,Part,sizeof(MTD->name)); MTD->name = map->name; MTD->type = MTD_NORFLASH; MTD->flags = MTD_CAP_NORFLASH; @@ -229,7 +270,7 @@ MTD->priv = map; map->fldrv_priv = priv; map->fldrv = &jedec_chipdrv; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return MTD; } @@ -344,15 +385,15 @@ for (I = 0; JEDEC_table[I].jedec != 0; I++) if (JEDEC_table[I].jedec == Id) return JEDEC_table + I; - return 0; + return NULL; } // Look for flash using an 8 bit bus interface static 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) + #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; @@ -411,8 +452,8 @@ static 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)) + #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; @@ -488,9 +529,9 @@ 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; + struct map_info *map = mtd->priv; - map->copy_from(map, buf, from, len); + map_copy_from(map, buf, from, len); *retlen = len; return 0; } @@ -500,8 +541,8 @@ 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; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; *retlen = 0; while (len > 0) @@ -514,7 +555,7 @@ get = priv->bank_fill[0] - offset; bank /= priv->bank_fill[0]; - map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); + map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); len -= get; *retlen += get; @@ -545,15 +586,15 @@ 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)<addrshift)) - #define flwrite(v,x) map->write8(map,v,chip->base+((x)<addrshift)) + #define flread(x) map_read8(map,chip->base+((x)<addrshift)) + #define flwrite(v,x) map_write8(map,v,chip->base+((x)<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; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; // Verify the arguments.. if (start + len > mtd->size || @@ -608,7 +649,7 @@ /* 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.. */ + Erasure time is dependent 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 @@ -651,19 +692,19 @@ 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); + 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); + 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); + 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; @@ -699,13 +740,13 @@ switch (map->buswidth) { case 1: - Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); + 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); + 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); + Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); break; } Count++; @@ -739,8 +780,7 @@ //printk("done\n"); instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; #undef flread @@ -755,13 +795,13 @@ 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<addrshift)-1))+((x)<addrshift)) - #define flwrite(v,x) map->write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) + base (which is divisible by the chip size) adds the necessary lower bits + of addrshift (interleave index) and then adds the control register index. */ + #define flread(x) map_read8(map,base+(off&((1<addrshift)-1))+((x)<addrshift)) + #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<addrshift)) - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; unsigned long base; unsigned long off; size_t save_len = len; @@ -794,7 +834,7 @@ // 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 oldbyte = map_read8(map,base+off); unsigned char Last[4]; unsigned long Count = 0; @@ -809,10 +849,10 @@ 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); + 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 @@ -820,7 +860,7 @@ failure */ for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++) - Last[Count % 4] = map->read8(map,base + off); + Last[Count % 4] = map_read8(map,base + off); if (Last[(Count - 1) % 4] != *buf) { jedec_flash_failed(Last[(Count - 3) % 4]); --- linux-2.4.21/drivers/mtd/chips/jedec_probe.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/jedec_probe.c @@ -1,13 +1,16 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.19 2002/11/12 13:12:10 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.63 2005/02/14 16:30:32 bjd Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. + + Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com */ #include #include +#include #include #include #include @@ -15,7 +18,9 @@ #include #include #include +#include +#include #include #include #include @@ -24,26 +29,35 @@ #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_ATMEL 0x001f #define MANUFACTURER_FUJITSU 0x0004 +#define MANUFACTURER_HYUNDAI 0x00AD #define MANUFACTURER_INTEL 0x0089 #define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_ST 0x0020 +#define MANUFACTURER_PMC 0x009D #define MANUFACTURER_SST 0x00BF +#define MANUFACTURER_ST 0x0020 #define MANUFACTURER_TOSHIBA 0x0098 +#define MANUFACTURER_WINBOND 0x00da /* AMD */ +#define AM29DL800BB 0x22C8 +#define AM29DL800BT 0x224A + #define AM29F800BB 0x2258 #define AM29F800BT 0x22D6 +#define AM29LV400BB 0x22BA +#define AM29LV400BT 0x22B9 #define AM29LV800BB 0x225B #define AM29LV800BT 0x22DA #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 #define AM29F017D 0x003D -#define AM29F016 0x00AD +#define AM29F016D 0x00AD #define AM29F080 0x00D5 #define AM29F040 0x00A4 #define AM29LV040B 0x004F #define AM29F032B 0x0041 +#define AM29F002T 0x00B0 /* Atmel */ #define AT49BV512 0x0003 @@ -54,6 +68,7 @@ #define AT49BV32XT 0x00C9 /* Fujitsu */ +#define MBM29F040C 0x00A4 #define MBM29LV650UE 0x22D7 #define MBM29LV320TE 0x22F6 #define MBM29LV320BE 0x22F9 @@ -61,6 +76,11 @@ #define MBM29LV160BE 0x2249 #define MBM29LV800BA 0x225B #define MBM29LV800TA 0x22DA +#define MBM29LV400TC 0x22B9 +#define MBM29LV400BC 0x22BA + +/* Hyundai */ +#define HY29F002T 0x00B0 /* Intel */ #define I28F004B3T 0x00d4 @@ -87,29 +107,46 @@ #define I82802AC 0x00ac /* Macronix */ +#define MX29LV040C 0x004F #define MX29LV160T 0x22C4 #define MX29LV160B 0x2249 #define MX29F016 0x00AD +#define MX29F002T 0x00B0 #define MX29F004T 0x0045 #define MX29F004B 0x0046 +/* PMC */ +#define PM49FL002 0x006D +#define PM49FL004 0x006E +#define PM49FL008 0x006A + /* ST - www.st.com */ -#define M29W800T 0x00D7 +#define M29W800DT 0x00D7 +#define M29W800DB 0x005B #define M29W160DT 0x22C4 #define M29W160DB 0x2249 #define M29W040B 0x00E3 +#define M50FW040 0x002C +#define M50FW080 0x002D +#define M50FW016 0x002E +#define M50LPW080 0x002F /* SST */ +#define SST29EE020 0x0010 +#define SST29LE020 0x0012 #define SST29EE512 0x005d #define SST29LE512 0x003d #define SST39LF800 0x2781 #define SST39LF160 0x2782 +#define SST39VF1601 0x234b #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 +#define SST49LF004B 0x0060 +#define SST49LF008A 0x005a #define SST49LF030A 0x001C #define SST49LF040A 0x0051 #define SST49LF080A 0x005B @@ -122,16 +159,93 @@ #define TC58FVT641 0x0093 #define TC58FVB641 0x0095 +/* Winbond */ +#define W49V002A 0x00b0 + + +/* + * Unlock address sets for AMD command sets. + * Intel command sets use the MTD_UADDR_UNNECESSARY. + * Each identifier, except MTD_UADDR_UNNECESSARY, and + * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[]. + * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure + * initialization need not require initializing all of the + * unlock addresses for all bit widths. + */ +enum uaddr { + MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */ + MTD_UADDR_0x0555_0x02AA, + MTD_UADDR_0x0555_0x0AAA, + MTD_UADDR_0x5555_0x2AAA, + MTD_UADDR_0x0AAA_0x0555, + MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ + MTD_UADDR_UNNECESSARY, /* Does not require any address */ +}; + + +struct unlock_addr { + u32 addr1; + u32 addr2; +}; + + +/* + * I don't like the fact that the first entry in unlock_addrs[] + * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore, + * should not be used. The problem is that structures with + * initializers have extra fields initialized to 0. It is _very_ + * desireable to have the unlock address entries for unsupported + * data widths automatically initialized - that means that + * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here + * must go unused. + */ +static const struct unlock_addr unlock_addrs[] = { + [MTD_UADDR_NOT_SUPPORTED] = { + .addr1 = 0xffff, + .addr2 = 0xffff + }, + + [MTD_UADDR_0x0555_0x02AA] = { + .addr1 = 0x0555, + .addr2 = 0x02aa + }, + + [MTD_UADDR_0x0555_0x0AAA] = { + .addr1 = 0x0555, + .addr2 = 0x0aaa + }, + + [MTD_UADDR_0x5555_0x2AAA] = { + .addr1 = 0x5555, + .addr2 = 0x2aaa + }, + + [MTD_UADDR_0x0AAA_0x0555] = { + .addr1 = 0x0AAA, + .addr2 = 0x0555 + }, + + [MTD_UADDR_DONT_CARE] = { + .addr1 = 0x0000, /* Doesn't matter which address */ + .addr2 = 0x0000 /* is used - must be last entry */ + }, + + [MTD_UADDR_UNNECESSARY] = { + .addr1 = 0x0000, + .addr2 = 0x0000 + } +}; + 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 int CmdSet; - const ulong regions[4]; + const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ + const ulong regions[6]; }; #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) @@ -145,760 +259,1434 @@ #define SIZE_4MiB 22 #define SIZE_8MiB 23 + +/* + * Please keep this list ordered by manufacturer! + * Fortunately, the list isn't searched often and so a + * slow, linear search isn't so bad. + */ static const struct amd_flash_info jedec_table[] = { { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F032B, - name: "AMD AM29F032B", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,64) + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F032B, + .name = "AMD AM29F032B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,64) } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29LV160DT, - name: "AMD AM29LV160DT", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,31), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV160DT, + .name = "AMD AM29LV160DT", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .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, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV160DB, + .name = "AMD AM29LV160DB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .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, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,31), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV400BB, + .name = "AMD AM29LV400BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7) + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV400BT, + .name = "AMD AM29LV400BT", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_TOSHIBA, - dev_id: TC58FVB160, - name: "Toshiba TC58FVB160", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV800BB, + .name = "AMD AM29LV800BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) + ERASEINFO(0x10000,15), } }, { - mfr_id: MANUFACTURER_TOSHIBA, - dev_id: TC58FVB321, - name: "Toshiba TC58FVB321", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x02000,8), - ERASEINFO(0x10000,63) +/* add DL */ + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29DL800BB, + .name = "AMD AM29DL800BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 6, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,4), + ERASEINFO(0x08000,1), + ERASEINFO(0x04000,1), + ERASEINFO(0x10000,14) } }, { - mfr_id: MANUFACTURER_TOSHIBA, - dev_id: TC58FVT321, - name: "Toshiba TC58FVT321", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x10000,63), + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29DL800BT, + .name = "AMD AM29DL800BT", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 6, + .regions = { + ERASEINFO(0x10000,14), + ERASEINFO(0x04000,1), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,4), + ERASEINFO(0x08000,1), + ERASEINFO(0x04000,1) + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F800BB, + .name = "AMD AM29F800BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .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", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .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", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F017D, + .name = "AMD AM29F017D", + .uaddr = { + [0] = MTD_UADDR_DONT_CARE /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,32), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F016D, + .name = "AMD AM29F016D", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,32), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F080, + .name = "AMD AM29F080", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F040, + .name = "AMD AM29F040", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV040B, + .name = "AMD AM29LV040B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F002T, + .name = "AMD AM29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), + } + }, { + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49BV512, + .name = "Atmel AT49BV512", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_64KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,1) + } + }, { + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT29LV512, + .name = "Atmel AT29LV512", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_64KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x80,256), + ERASEINFO(0x80,256) + } + }, { + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49BV16X, + .name = "Atmel AT49BV16X", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x02000,8), + ERASEINFO(0x10000,31) + } + }, { + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49BV16XT, + .name = "Atmel AT49BV16XT", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x10000,31), ERASEINFO(0x02000,8) } }, { - mfr_id: MANUFACTURER_TOSHIBA, - dev_id: TC58FVB641, - name: "Toshiba TC58FVB641", - DevSize: SIZE_8MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x02000,8), - ERASEINFO(0x10000,127) + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49BV32X, + .name = "Atmel AT49BV32X", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) } }, { - mfr_id: MANUFACTURER_TOSHIBA, - dev_id: TC58FVT641, - name: "Toshiba TC58FVT641", - DevSize: SIZE_8MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x10000,127), + .mfr_id = MANUFACTURER_ATMEL, + .dev_id = AT49BV32XT, + .name = "Atmel AT49BV32XT", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV650UE, - name: "Fujitsu MBM29LV650UE", - DevSize: SIZE_8MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,128) + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29F040C, + .name = "Fujitsu MBM29F040C", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV320TE, - name: "Fujitsu MBM29LV320TE", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x10000,63), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV650UE, + .name = "Fujitsu MBM29LV650UE", + .uaddr = { + [0] = MTD_UADDR_DONT_CARE /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,128) + } + }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV320TE, + .name = "Fujitsu MBM29LV320TE", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV320BE, - name: "Fujitsu MBM29LV320BE", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x02000,8), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV320BE, + .name = "Fujitsu MBM29LV320BE", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x02000,8), ERASEINFO(0x10000,63) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV160TE, - name: "Fujitsu MBM29LV160TE", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,31), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV160TE, + .name = "Fujitsu MBM29LV160TE", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV160BE, - name: "Fujitsu MBM29LV160BE", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV160BE, + .name = "Fujitsu MBM29LV160BE", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), ERASEINFO(0x10000,31) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV800BA, - name: "Fujitsu MBM29LV800BA", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV800BA, + .name = "Fujitsu MBM29LV800BA", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), ERASEINFO(0x10000,15) } }, { - mfr_id: MANUFACTURER_FUJITSU, - dev_id: MBM29LV800TA, - name: "Fujitsu MBM29LV800TA", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,15), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV800TA, + .name = "Fujitsu MBM29LV800TA", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .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, - CmdSet: P_ID_AMD_STD, - 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, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV400BC, + .name = "Fujitsu MBM29LV400BC", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .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, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,15), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x10000,7) } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F800BT, - name: "AMD AM29F800BT", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,15), + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV400TC, + .name = "Fujitsu MBM29LV400TC", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29LV800BB, - name: "AMD AM29LV800BB", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,15), + .mfr_id = MANUFACTURER_HYUNDAI, + .dev_id = HY29F002T, + .name = "Hyundai HY29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x04000,1), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F004B3B, - name: "Intel 28F004B3B", - DevSize: SIZE_512KiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F004B3B, + .name = "Intel 28F004B3B", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 7), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F004B3T, - name: "Intel 28F004B3T", - DevSize: SIZE_512KiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F004B3T, + .name = "Intel 28F004B3T", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 7), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F400B3B, - name: "Intel 28F400B3B", - DevSize: SIZE_512KiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F400B3B, + .name = "Intel 28F400B3B", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 7), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F400B3T, - name: "Intel 28F400B3T", - DevSize: SIZE_512KiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F400B3T, + .name = "Intel 28F400B3T", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 7), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F008B3B, - name: "Intel 28F008B3B", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008B3B, + .name = "Intel 28F008B3B", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 15), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F008B3T, - name: "Intel 28F008B3T", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008B3T, + .name = "Intel 28F008B3T", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F008S5, - name: "Intel 28F008S5", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_EXT, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,16), + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008S5, + .name = "Intel 28F008S5", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F016S5, - name: "Intel 28F016S5", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_EXT, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,32), + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016S5, + .name = "Intel 28F016S5", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,32), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F008SA, - name: "Intel 28F008SA", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 1, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008SA, + .name = "Intel 28F008SA", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 1, + .regions = { ERASEINFO(0x10000, 16), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F800B3B, - name: "Intel 28F800B3B", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F800B3B, + .name = "Intel 28F800B3B", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 15), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F800B3T, - name: "Intel 28F800B3T", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F800B3T, + .name = "Intel 28F800B3T", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F016B3B, - name: "Intel 28F016B3B", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016B3B, + .name = "Intel 28F016B3B", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 31), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F016S3, - name: "Intel I28F016S3", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 1, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016S3, + .name = "Intel I28F016S3", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 1, + .regions = { ERASEINFO(0x10000, 32), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F016B3T, - name: "Intel 28F016B3T", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016B3T, + .name = "Intel 28F016B3T", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 31), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F160B3B, - name: "Intel 28F160B3B", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F160B3B, + .name = "Intel 28F160B3B", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 31), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F160B3T, - name: "Intel 28F160B3T", - DevSize: SIZE_2MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F160B3T, + .name = "Intel 28F160B3T", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 31), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F320B3B, - name: "Intel 28F320B3B", - DevSize: SIZE_4MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F320B3B, + .name = "Intel 28F320B3B", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 63), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F320B3T, - name: "Intel 28F320B3T", - DevSize: SIZE_4MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F320B3T, + .name = "Intel 28F320B3T", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 63), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F640B3B, - name: "Intel 28F640B3B", - DevSize: SIZE_8MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F640B3B, + .name = "Intel 28F640B3B", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 127), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I28F640B3T, - name: "Intel 28F640B3T", - DevSize: SIZE_8MiB, - CmdSet: P_ID_INTEL_STD, - NumEraseRegions: 2, - regions: { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F640B3T, + .name = "Intel 28F640B3T", + .uaddr = { + [1] = MTD_UADDR_UNNECESSARY, /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 2, + .regions = { ERASEINFO(0x10000, 127), ERASEINFO(0x02000, 8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I82802AB, - name: "Intel 82802AB", - DevSize: SIZE_512KiB, - CmdSet: P_ID_INTEL_EXT, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,8), + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I82802AB, + .name = "Intel 82802AB", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), } }, { - mfr_id: MANUFACTURER_INTEL, - dev_id: I82802AC, - name: "Intel 82802AC", - DevSize: SIZE_1MiB, - CmdSet: P_ID_INTEL_EXT, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,16), + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I82802AC, + .name = "Intel 82802AC", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), } }, { - mfr_id: MANUFACTURER_ST, - dev_id: M29W800T, - name: "ST M29W800T", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,15), + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29LV040C, + .name = "Macronix MX29LV040C", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), + } + }, { + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29LV160T, + .name = "MXIC MX29LV160T", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_ST, - dev_id: M29W160DT, - name: "ST M29W160DT", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,31), + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29LV160B, + .name = "MXIC MX29LV160B", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F016, + .name = "Macronix MX29F016", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,32), + } + }, { + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F004T, + .name = "Macronix MX29F004T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + ERASEINFO(0x04000,1), } }, { - mfr_id: MANUFACTURER_ST, - dev_id: M29W160DB, - name: "ST M29W160DB", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F004B, + .name = "Macronix MX29F004B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) + ERASEINFO(0x10000,7), } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49BV512, - name: "Atmel AT49BV512", - DevSize: SIZE_64KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,1) + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F002T, + .name = "Macronix MX29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT29LV512, - name: "Atmel AT29LV512", - DevSize: SIZE_64KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: { - ERASEINFO(0x80,256), - ERASEINFO(0x80,256) + .mfr_id = MANUFACTURER_PMC, + .dev_id = PM49FL002, + .name = "PMC Pm49FL002", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO( 0x01000, 64 ) } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49BV16X, - name: "Atmel AT49BV16X", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x02000,8), - ERASEINFO(0x10000,31) + .mfr_id = MANUFACTURER_PMC, + .dev_id = PM49FL004, + .name = "PMC Pm49FL004", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO( 0x01000, 128 ) } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49BV16XT, - name: "Atmel AT49BV16XT", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x10000,31), - ERASEINFO(0x02000,8) + .mfr_id = MANUFACTURER_PMC, + .dev_id = PM49FL008, + .name = "PMC Pm49FL008", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO( 0x01000, 256 ) } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49BV32X, - name: "Atmel AT49BV32X", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x02000,8), - ERASEINFO(0x10000,63) + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39LF512, + .name = "SST 39LF512", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_64KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,16), } }, { - mfr_id: MANUFACTURER_ATMEL, - dev_id: AT49BV32XT, - name: "Atmel AT49BV32XT", - DevSize: SIZE_4MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 2, - regions: {ERASEINFO(0x10000,63), - ERASEINFO(0x02000,8) + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39LF010, + .name = "SST 39LF010", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_128KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,32), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F017D, - name: "AMD AM29F017D", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,32), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST29EE020, + .name = "SST 29EE020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_SST_PAGE, + .NumEraseRegions= 1, + regions: {ERASEINFO(0x01000,64), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F016, - name: "AMD AM29F016", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,32), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST29LE020, + .name = "SST 29LE020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_SST_PAGE, + .NumEraseRegions= 1, + regions: {ERASEINFO(0x01000,64), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F080, - name: "AMD AM29F080", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,16), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39LF020, + .name = "SST 39LF020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,64), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F040, - name: "AMD AM29F040", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,8), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39LF040, + .name = "SST 39LF040", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29LV040B, - name: "AMD AM29LV040B", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,8), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39SF010A, + .name = "SST 39SF010A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_128KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,32), } }, { - mfr_id: MANUFACTURER_ST, - dev_id: M29W040B, - name: "ST M29W040B", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,8), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39SF020A, + .name = "SST 39SF020A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,64), } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29LV160T, - name: "MXIC MX29LV160T", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,31), + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF004B, + .name = "SST 49LF004B", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF008A, + .name = "SST 49LF008A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,256), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF030A, + .name = "SST 49LF030A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,96), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF040A, + .name = "SST 49LF040A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF080A, + .name = "SST 49LF080A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,256), + } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39LF160, + .name = "SST 39LF160", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39VF1601, + .name = "SST 39VF1601", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + + }, { + .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .dev_id = M29W800DT, + .name = "ST M29W800DT", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29LV160B, - name: "MXIC MX29LV160B", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .dev_id = M29W800DB, + .name = "ST M29W800DB", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) - } - }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29F016, - name: "Macronix MX29F016", - DevSize: SIZE_2MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x10000,32), + ERASEINFO(0x10000,15) } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29F004T, - name: "Macronix MX29F004T", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,7), + .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .dev_id = M29W160DT, + .name = "ST M29W160DT", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1), + ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29F004B, - name: "Macronix MX29F004B", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 4, - regions: {ERASEINFO(0x04000,1), + .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .dev_id = M29W160DB, + .name = "ST M29W160DB", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), - ERASEINFO(0x10000,7), + ERASEINFO(0x10000,31) } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39LF512, - name: "SST 39LF512", - DevSize: SIZE_64KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,16), + .mfr_id = MANUFACTURER_ST, + .dev_id = M29W040B, + .name = "ST M29W040B", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39LF010, - name: "SST 39LF010", - DevSize: SIZE_128KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,32), + .mfr_id = MANUFACTURER_ST, + .dev_id = M50FW040, + .name = "ST M50FW040", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8), } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39LF020, - name: "SST 39LF020", - DevSize: SIZE_256KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,64), + .mfr_id = MANUFACTURER_ST, + .dev_id = M50FW080, + .name = "ST M50FW080", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39LF040, - name: "SST 39LF040", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,128), + .mfr_id = MANUFACTURER_ST, + .dev_id = M50FW016, + .name = "ST M50FW016", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,32), } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39SF010A, - name: "SST 39SF010A", - DevSize: SIZE_128KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,32), + .mfr_id = MANUFACTURER_ST, + .dev_id = M50LPW080, + .name = "ST M50LPW080", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_EXT, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,16), } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST39SF020A, - name: "SST 39SF020A", - DevSize: SIZE_256KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,64), + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVT160, + .name = "Toshiba TC58FVT160", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST49LF030A, - name: "SST 49LF030A", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,96), + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVB160, + .name = "Toshiba TC58FVB160", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST49LF040A, - name: "SST 49LF040A", - DevSize: SIZE_512KiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,128), + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVB321, + .name = "Toshiba TC58FVB321", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) } }, { - mfr_id: MANUFACTURER_SST, - dev_id: SST49LF080A, - name: "SST 49LF080A", - DevSize: SIZE_1MiB, - CmdSet: P_ID_AMD_STD, - NumEraseRegions: 1, - regions: {ERASEINFO(0x01000,256), + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVT321, + .name = "Toshiba TC58FVT321", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x10000,63), + ERASEINFO(0x02000,8) + } + }, { + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVB641, + .name = "Toshiba TC58FVB641", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x02000,8), + ERASEINFO(0x10000,127) + } + }, { + .mfr_id = MANUFACTURER_TOSHIBA, + .dev_id = TC58FVT641, + .name = "Toshiba TC58FVT641", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x10000,127), + ERASEINFO(0x02000,8) + } + }, { + .mfr_id = MANUFACTURER_WINBOND, + .dev_id = W49V002A, + .name = "Winbond W49V002A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000, 3), + ERASEINFO(0x08000, 1), + ERASEINFO(0x02000, 2), + ERASEINFO(0x04000, 1), } } }; @@ -907,48 +1695,94 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); static int jedec_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi); + unsigned long *chip_map, struct cfi_private *cfi); -struct mtd_info *jedec_probe(struct map_info *map); +static struct mtd_info *jedec_probe(struct map_info *map); static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, struct cfi_private *cfi) { - u32 result, mask; + map_word result; + unsigned long mask; + u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type); mask = (1 << (cfi->device_type * 8)) -1; - result = cfi_read(map, base); - result &= mask; - return result; + result = map_read(map, base + ofs); + return result.x[0] & mask; } static inline u32 jedec_read_id(struct map_info *map, __u32 base, struct cfi_private *cfi) { - int osf; - u32 result, mask; - osf = cfi->interleave *cfi->device_type; + map_word result; + unsigned long mask; + u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type); mask = (1 << (cfi->device_type * 8)) -1; - result = cfi_read(map, base + osf); - result &= mask; - return result; + result = map_read(map, base + ofs); + return result.x[0] & mask; } static inline void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi) { /* Reset */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + + /* after checking the datasheets for SST, MACRONIX and ATMEL + * (oh and incidentaly the jedec spec - 3.5.3.3) the reset + * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at + * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips + * as they will ignore the writes and dont care what address + * the F0 is written to */ + if(cfi->addr_unlock1) { + DEBUG( MTD_DEBUG_LEVEL3, + "reset unlock called %x %x \n", + cfi->addr_unlock1,cfi->addr_unlock2); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); + } + + cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* Some misdesigned intel chips do not respond for 0xF0 for a reset, * so ensure we're in read mode. Send both the Intel and the AMD command * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so * this should be safe. */ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + /* FIXME - should have reset delay before continuing */ +} + + +static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type) +{ + int uaddr_idx; + __u8 uaddr = MTD_UADDR_NOT_SUPPORTED; + + switch ( device_type ) { + case CFI_DEVICETYPE_X8: uaddr_idx = 0; break; + case CFI_DEVICETYPE_X16: uaddr_idx = 1; break; + case CFI_DEVICETYPE_X32: uaddr_idx = 2; break; + default: + printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n", + __func__, device_type); + goto uaddr_done; + } + + uaddr = finfo->uaddr[uaddr_idx]; + if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { + /* ASSERT("The unlock addresses for non-8-bit mode + are bollocks. We don't really need an array."); */ + uaddr = finfo->uaddr[0]; + } + + uaddr_done: + return uaddr; } + + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; + __u8 uaddr; printk("Found: %s\n",jedec_table[index].name); @@ -970,42 +1804,174 @@ for (i=0; icfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; } - p_cfi->cmdset_priv = 0; + p_cfi->cmdset_priv = NULL; + + /* This may be redundant for some cases, but it doesn't hurt */ + p_cfi->mfr = jedec_table[index].mfr_id; + p_cfi->id = jedec_table[index].dev_id; + + uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); + if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { + kfree( p_cfi->cfiq ); + return 0; + } + + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; + return 1; /* ok */ } -static int jedec_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi) + +/* + * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing + * the mapped address, unlock addresses, and proper chip ID. This function + * attempts to minimize errors. It is doubtfull that this probe will ever + * be perfect - consequently there should be some module parameters that + * could be manually specified to force the chip info. + */ +static inline int jedec_match( __u32 base, + struct map_info *map, + struct cfi_private *cfi, + const struct amd_flash_info *finfo ) { - int i; - int unlockpass = 0; + int rc = 0; /* failure until all tests pass */ + u32 mfr, id; + __u8 uaddr; - if (!cfi->numchips) { + /* + * The IDs must match. For X16 and X32 devices operating in + * a lower width ( X8 or X16 ), the device ID's are usually just + * the lower byte(s) of the larger device ID for wider mode. If + * a part is found that doesn't fit this assumption (device id for + * smaller width mode is completely unrealated to full-width mode) + * then the jedec_table[] will have to be augmented with the IDs + * for different widths. + */ switch (cfi->device_type) { case CFI_DEVICETYPE_X8: - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; + mfr = (__u8)finfo->mfr_id; + id = (__u8)finfo->dev_id; + + /* bjd: it seems that if we do this, we can end up + * detecting 16bit flashes as an 8bit device, even though + * there aren't. + */ + if (finfo->dev_id > 0xff) { + DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n", + __func__); + goto match_done; + } 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; - } + mfr = (__u16)finfo->mfr_id; + id = (__u16)finfo->dev_id; break; case CFI_DEVICETYPE_X32: - cfi->addr_unlock1 = 0x1555; - cfi->addr_unlock2 = 0xaaa; + mfr = (__u16)finfo->mfr_id; + id = (__u32)finfo->dev_id; break; default: - printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type); - return 0; + printk(KERN_WARNING + "MTD %s(): Unsupported device type %d\n", + __func__, cfi->device_type); + goto match_done; } + if ( cfi->mfr != mfr || cfi->id != id ) { + goto match_done; + } + + /* the part size must fit in the memory window */ + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", + __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); + if ( base + cfi_interleave(cfi) * ( 1 << finfo->DevSize ) > map->size ) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", + __func__, finfo->mfr_id, finfo->dev_id, + 1 << finfo->DevSize ); + goto match_done; + } + + uaddr = finfo_uaddr(finfo, cfi->device_type); + if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { + goto match_done; + } + + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", + __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); + if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr + && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 || + unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): 0x%.4x 0x%.4x did not match\n", + __func__, + unlock_addrs[uaddr].addr1, + unlock_addrs[uaddr].addr2); + goto match_done; + } + + /* + * Make sure the ID's dissappear when the device is taken out of + * ID mode. The only time this should fail when it should succeed + * is when the ID's are written as data to the same + * addresses. For this rare and unfortunate case the chip + * cannot be probed correctly. + * FIXME - write a driver that takes all of the chip info as + * module parameters, doesn't probe but forces a load. + */ + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): check ID's disappear when not in ID mode\n", + __func__ ); + jedec_reset( base, map, cfi ); + mfr = jedec_read_mfr( map, base, cfi ); + id = jedec_read_id( map, base, cfi ); + if ( mfr == cfi->mfr && id == cfi->id ) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n" + "You might need to manually specify JEDEC parameters.\n", + __func__, cfi->mfr, cfi->id ); + goto match_done; + } + + /* all tests passed - mark as success */ + rc = 1; + + /* + * Put the device back in ID mode - only need to do this if we + * were truly frobbing a real device. + */ + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); + if(cfi->addr_unlock1) { + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + /* FIXME - should have a delay before continuing */ + + match_done: + return rc; +} + + +static int jedec_probe_chip(struct map_info *map, __u32 base, + unsigned long *chip_map, struct cfi_private *cfi) +{ + int i; + enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; + u32 probe_offset1, probe_offset2; retry: + if (!cfi->numchips) { + uaddr_idx++; + + if (MTD_UADDR_UNNECESSARY == uaddr_idx) + return 0; + + cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; + cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; + } + /* Make certain we aren't probing past the end of map */ if (base >= map->size) { printk(KERN_NOTICE @@ -1014,19 +1980,19 @@ return 0; } - if ((base + cfi->addr_unlock1) >= map->size) { - printk(KERN_NOTICE - "Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", - base, cfi->addr_unlock1, map->size -1); - - return 0; - } - if ((base + cfi->addr_unlock2) >= map->size) { - printk(KERN_NOTICE - "Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", - base, cfi->addr_unlock2, map->size -1); - return 0; - + /* Ensure the unlock addresses we try stay inside the map */ + probe_offset1 = cfi_build_cmd_addr( + cfi->addr_unlock1, + cfi_interleave(cfi), + cfi->device_type); + probe_offset2 = cfi_build_cmd_addr( + cfi->addr_unlock1, + cfi_interleave(cfi), + cfi->device_type); + if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || + ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) + { + goto retry; } /* Reset */ @@ -1034,10 +2000,11 @@ /* Autoselect Mode */ if(cfi->addr_unlock1) { - 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(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } - cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + /* FIXME - should have a delay before continuing */ if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI @@ -1045,26 +2012,21 @@ cfi->mfr = jedec_read_mfr(map, base, cfi); cfi->id = jedec_read_id(map, base, cfi); - printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", - cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); + DEBUG(MTD_DEBUG_LEVEL3, + "Search for id:(%02x %02x) interleave(%d) type(%d)\n", + cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type); for (i=0; imfr == jedec_table[i].mfr_id && - cfi->id == jedec_table[i].dev_id) { + if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n", + __func__, cfi->mfr, cfi->id, + cfi->addr_unlock1, cfi->addr_unlock2 ); if (!cfi_jedec_setup(cfi, i)) return 0; goto ok_out; } } - switch(unlockpass++) { - case 0: - cfi->addr_unlock1 |= cfi->addr_unlock1 << 4; - cfi->addr_unlock2 |= cfi->addr_unlock2 << 4; - goto retry; - case 1: - cfi->addr_unlock1 = cfi->addr_unlock2 = 0; goto retry; - } - return 0; } else { __u16 mfr; __u16 id; @@ -1081,21 +2043,24 @@ } } - /* Check each previous chip to see if it's an alias */ - for (i=0; inumchips; i++) { - /* This chip should be in read mode if it's one - we've already touched. */ - if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && - jedec_read_id(map, chips[i].start, cfi) == cfi->id) { + /* Check each previous chip locations to see if it's an alias */ + for (i=0; i < (base >> cfi->chipshift); i++) { + unsigned long start; + if(!test_bit(i, chip_map)) { + continue; /* Skip location; no valid chip at this address */ + } + start = i << cfi->chipshift; + if (jedec_read_mfr(map, start, cfi) == cfi->mfr && + jedec_read_id(map, start, cfi) == cfi->id) { /* Eep. This chip also looks like it's in autoselect mode. Is it an alias for the new one? */ - jedec_reset(chips[i].start, map, cfi); + jedec_reset(start, map, cfi); /* If the device IDs go away, it's an alias */ if (jedec_read_mfr(map, base, cfi) != cfi->mfr || jedec_read_id(map, base, cfi) != cfi->id) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } @@ -1107,7 +2072,7 @@ if (jedec_read_mfr(map, base, cfi) == cfi->mfr && jedec_read_id(map, base, cfi) == cfi->id) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } } @@ -1115,32 +2080,26 @@ /* 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; + set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ cfi->numchips++; ok_out: /* Put it back into Read Mode */ jedec_reset(base, map, cfi); - 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); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", + map->name, cfi_interleave(cfi), cfi->device_type*8, base, + map->bankwidth*8); return 1; } static struct chip_probe jedec_chip_probe = { - name: "JEDEC", - probe_chip: jedec_probe_chip + .name = "JEDEC", + .probe_chip = jedec_probe_chip }; -struct mtd_info *jedec_probe(struct map_info *map) +static struct mtd_info *jedec_probe(struct map_info *map) { /* * Just use the generic probe stuff to call our CFI-specific @@ -1150,12 +2109,12 @@ } static struct mtd_chip_driver jedec_chipdrv = { - probe: jedec_probe, - name: "jedec_probe", - module: THIS_MODULE + .probe = jedec_probe, + .name = "jedec_probe", + .module = THIS_MODULE }; -int __init jedec_probe_init(void) +static int __init jedec_probe_init(void) { register_mtd_chip_driver(&jedec_chipdrv); return 0; --- linux-2.4.21/drivers/mtd/chips/map_absent.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/map_absent.c @@ -1,7 +1,7 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation - * $Id: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_absent.c,v 1.5 2004/11/16 18:29:00 dwmw2 Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. @@ -23,9 +23,10 @@ #include #include #include - +#include +#include #include - +#include static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -36,10 +37,10 @@ static struct mtd_chip_driver map_absent_chipdrv = { - probe: map_absent_probe, - destroy: map_absent_destroy, - name: "map_absent", - module: THIS_MODULE + .probe = map_absent_probe, + .destroy = map_absent_destroy, + .name = "map_absent", + .module = THIS_MODULE }; static struct mtd_info *map_absent_probe(struct map_info *map) @@ -65,7 +66,7 @@ mtd->flags = 0; mtd->erasesize = PAGE_SIZE; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } @@ -97,7 +98,7 @@ /* nop */ } -int __init map_absent_init(void) +static int __init map_absent_init(void) { register_mtd_chip_driver(&map_absent_chipdrv); return 0; --- linux-2.4.21/drivers/mtd/chips/map_ram.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -11,8 +11,10 @@ #include #include #include - +#include +#include #include +#include static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -23,9 +25,9 @@ static struct mtd_chip_driver mapram_chipdrv = { - probe: map_ram_probe, - name: "map_ram", - module: THIS_MODULE + .probe = map_ram_probe, + .name = "map_ram", + .module = THIS_MODULE }; static struct mtd_info *map_ram_probe(struct map_info *map) @@ -34,21 +36,21 @@ /* Check the first byte is RAM */ #if 0 - map->write8(map, 0x55, 0); - if (map->read8(map, 0) != 0x55) + map_write8(map, 0x55, 0); + if (map_read8(map, 0) != 0x55) return NULL; - map->write8(map, 0xAA, 0); - if (map->read8(map, 0) != 0xAA) + 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) + 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) + map_write8(map, 0xAA, map->size-1); + if (map_read8(map, map->size-1) != 0xAA) return NULL; #endif /* OK. It seems to be RAM. */ @@ -74,25 +76,25 @@ while(mtd->size & (mtd->erasesize - 1)) mtd->erasesize >>= 1; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); 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; + struct map_info *map = mtd->priv; - map->copy_from(map, buf, from, len); + 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; + struct map_info *map = mtd->priv; - map->copy_to(map, to, buf, len); + map_copy_to(map, to, buf, len); *retlen = len; return 0; } @@ -101,14 +103,18 @@ { /* Yeah, it's inefficient. Who cares? It's faster than a _real_ flash erase. */ - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; + map_word allff; unsigned long i; - for (i=0; ilen; i++) - map->write8(map, 0xFF, instr->addr + i); + allff = map_word_ff(map); - if (instr->callback) - instr->callback(instr); + for (i=0; ilen; i += map_bankwidth(map)) + map_write(map, allff, instr->addr + i); + + instr->state = MTD_ERASE_DONE; + + mtd_erase_callback(instr); return 0; } @@ -118,7 +124,7 @@ /* Nothing to see here */ } -int __init map_ram_init(void) +static int __init map_ram_init(void) { register_mtd_chip_driver(&mapram_chipdrv); return 0; --- linux-2.4.21/drivers/mtd/chips/map_rom.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/map_rom.c @@ -1,10 +1,9 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ -#include #include #include #include @@ -12,21 +11,23 @@ #include #include #include - +#include +#include #include +#include 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_info *map_rom_probe(struct map_info *map); static struct mtd_chip_driver maprom_chipdrv = { - probe: map_rom_probe, - name: "map_rom", - module: THIS_MODULE + .probe = map_rom_probe, + .name = "map_rom", + .module = THIS_MODULE }; -struct mtd_info *map_rom_probe(struct map_info *map) +static struct mtd_info *map_rom_probe(struct map_info *map) { struct mtd_info *mtd; @@ -49,16 +50,16 @@ while(mtd->size & (mtd->erasesize - 1)) mtd->erasesize >>= 1; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); 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; + struct map_info *map = mtd->priv; - map->copy_from(map, buf, from, len); + map_copy_from(map, buf, from, len); *retlen = len; return 0; } @@ -74,7 +75,7 @@ return -EIO; } -int __init map_rom_init(void) +static int __init map_rom_init(void) { register_mtd_chip_driver(&maprom_chipdrv); return 0; --- linux-2.4.21/drivers/mtd/chips/sharp.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/chips/sharp.c @@ -4,7 +4,7 @@ * Copyright 2000,2001 David A. Schleef * 2000,2001 Lineo, Inc. * - * $Id: sharp.c,v 1.8 2002/05/17 08:59:19 dwmw2 Exp $ + * $Id: sharp.c,v 1.14 2004/08/09 13:19:43 dwmw2 Exp $ * * Devices supported: * LH28F016SCT Symmetrical block flash memory, 2Mx8 @@ -22,14 +22,15 @@ #include #include -#include #include #include #include #include #include +#include #include #include +#include #define CMD_RESET 0xffffffff #define CMD_READ_ID 0x90909090 @@ -98,10 +99,10 @@ 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 + .probe = sharp_probe, + .destroy = sharp_destroy, + .name = "sharp", + .module = THIS_MODULE }; @@ -116,8 +117,10 @@ return NULL; sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); - if(!sharp) + if(!sharp) { + kfree(mtd); return NULL; + } memset(mtd, 0, sizeof(*mtd)); @@ -152,7 +155,7 @@ map->fldrv = &sharp_chipdrv; map->fldrv_priv = sharp; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } @@ -163,12 +166,12 @@ u32 read0, read4; int width = 4; - tmp = map->read32(map, base+0); + tmp = map_read32(map, base+0); - map->write32(map, CMD_READ_ID, base+0); + map_write32(map, CMD_READ_ID, base+0); - read0=map->read32(map, base+0); - read4=map->read32(map, base+4); + read0=map_read32(map, base+0); + read4=map_read32(map, base+4); if(read0 == 0x89898989){ printk("Looks like sharp flash\n"); switch(read4){ @@ -196,10 +199,10 @@ printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n", read0,read4); } - }else if((map->read32(map, base+0) == CMD_READ_ID)){ + }else if((map_read32(map, base+0) == CMD_READ_ID)){ /* RAM, probably */ printk("Looks like RAM\n"); - map->write32(map, tmp, base+0); + map_write32(map, tmp, base+0); }else{ printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n", read0,read4); @@ -221,10 +224,10 @@ switch(chip->state){ case FL_READY: - map->write32(map,CMD_READ_STATUS,adr); + map_write32(map,CMD_READ_STATUS,adr); chip->state = FL_STATUS; case FL_STATUS: - status = map->read32(map,adr); + status = map_read32(map,adr); //printk("status=%08x\n",status); udelay(100); @@ -252,7 +255,7 @@ goto retry; } - map->write32(map,CMD_RESET, adr); + map_write32(map,CMD_RESET, adr); chip->state = FL_READY; @@ -293,7 +296,7 @@ if(ret<0) break; - map->copy_from(map,buf,ofs,thislen); + map_copy_from(map,buf,ofs,thislen); sharp_release(&sharp->chips[chipnum]); @@ -354,17 +357,17 @@ ret = sharp_wait(map,chip); for(try=0;try<10;try++){ - map->write32(map,CMD_BYTE_WRITE,adr); + 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); + map_write32(map,cpu_to_le32(datum),adr); chip->state = FL_WRITING; timeo = jiffies + (HZ/2); - map->write32(map,CMD_READ_STATUS,adr); + map_write32(map,CMD_READ_STATUS,adr); for(i=0;i<100;i++){ - status = map->read32(map,adr); + status = map_read32(map,adr); if((status & SR_READY)==SR_READY) break; } @@ -377,9 +380,9 @@ printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status); - map->write32(map,CMD_CLEAR_STATUS,adr); + map_write32(map,CMD_CLEAR_STATUS,adr); } - map->write32(map,CMD_RESET,adr); + map_write32(map,CMD_RESET,adr); chip->state = FL_READY; wake_up(&chip->wq); @@ -422,8 +425,7 @@ } instr->state = MTD_ERASE_DONE; - if(instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; } @@ -432,18 +434,18 @@ unsigned long adr) { int ret; - int timeo; + unsigned long timeo; int status; DECLARE_WAITQUEUE(wait, current); - map->write32(map,CMD_READ_STATUS,adr); - status = map->read32(map,adr); + map_write32(map,CMD_READ_STATUS,adr); + status = map_read32(map,adr); timeo = jiffies + HZ; while(time_before(jiffies, timeo)){ - map->write32(map,CMD_READ_STATUS,adr); - status = map->read32(map,adr); + map_write32(map,CMD_READ_STATUS,adr); + status = map_read32(map,adr); if((status & SR_READY)==SR_READY){ ret = 0; goto out; @@ -485,26 +487,26 @@ sharp_unlock_oneblock(map,chip,adr); #endif - map->write32(map,CMD_BLOCK_ERASE_1,adr); - map->write32(map,CMD_BLOCK_ERASE_2,adr); + 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); + map_write32(map,CMD_READ_STATUS,adr); + status = map_read32(map,adr); if(!(status&SR_ERRORS)){ - map->write32(map,CMD_RESET,adr); + 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); + map_write32(map,CMD_CLEAR_STATUS,adr); //spin_unlock_bh(chip->mutex); @@ -518,17 +520,17 @@ int i; int status; - map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); - map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); + 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); + 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); + //map_write32(map,CMD_READ_STATUS,adr); + status = map_read32(map,adr); if((status & SR_READY)==SR_READY) break; udelay(100); @@ -538,13 +540,13 @@ } if(!(status&SR_ERRORS)){ - map->write32(map,CMD_RESET,adr); + 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); + map_write32(map,CMD_CLEAR_STATUS,adr); } #endif --- linux-2.4.21/drivers/mtd/cmdlinepart.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/cmdlinepart.c @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $ + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * * Read flash partition table from command line * @@ -10,7 +10,7 @@ * mtdparts=[; := :[,] * := [@offset][][ro] - * := unique id used in mapping driver/device + * := unique name used in mapping driver/device (mtd->name) * := standard linux memsize OR "-" to denote all remaining space * := '(' NAME ')' * @@ -28,7 +28,6 @@ #include #include -#include #include /* error message prefix */ @@ -95,7 +94,7 @@ if (size < PAGE_SIZE) { printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); - return 0; + return NULL; } } @@ -122,7 +121,7 @@ if ((p = strchr(name, delim)) == 0) { printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); - return 0; + return NULL; } name_len = p - name; s = p + 1; @@ -149,12 +148,12 @@ if (size == SIZE_REMAINING) { printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); - return 0; + return NULL; } /* more partitions follow, parse them */ if ((parts = newpart(s + 1, &s, num_parts, this_part + 1, &extra_mem, extra_mem_size)) == 0) - return 0; + return NULL; } else { /* this is the last partition: allocate space for all */ @@ -167,7 +166,7 @@ if (!parts) { printk(KERN_ERR ERRP "out of memory\n"); - return 0; + return NULL; } memset(parts, 0, alloc_size); extra_mem = (unsigned char *)(parts + *num_parts); @@ -178,8 +177,7 @@ parts[this_part].mask_flags = mask_flags; if (name) { - strncpy(extra_mem, name, name_len); - extra_mem[name_len] = 0; + strlcpy(extra_mem, name, name_len + 1); } else { @@ -258,8 +256,7 @@ this_mtd->parts = parts; this_mtd->num_parts = num_parts; this_mtd->mtd_id = (char*)(this_mtd + 1); - strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); - this_mtd->mtd_id[mtd_id_len] = 0; + strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); /* link into chain */ this_mtd->next = partitions; @@ -291,13 +288,14 @@ * information. It returns partitions for the requested mtd device, or * the first one in the chain if a NULL mtd_id is passed in. */ -int parse_cmdline_partitions(struct mtd_info *master, +static int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, - const char *mtd_id) + unsigned long origin) { unsigned long offset; int i; struct cmdline_mtd_partition *part; + char *mtd_id = master->name; if(!cmdline) return -EINVAL; @@ -340,8 +338,10 @@ * This is the handler for our kernel parameter, called from * main.c::checksetup(). Note that we can not yet kmalloc() anything, * so we only save the commandline for later processing. + * + * This function needs to be visible for bootloaders. */ -static int __init mtdpart_setup(char *s) +int mtdpart_setup(char *s) { cmdline = s; return 1; @@ -349,7 +349,18 @@ __setup("mtdparts=", mtdpart_setup); -EXPORT_SYMBOL(parse_cmdline_partitions); +static struct mtd_part_parser cmdline_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_cmdline_partitions, + .name = "cmdlinepart", +}; + +static int __init cmdline_parser_init(void) +{ + return register_mtd_parser(&cmdline_parser); +} + +module_init(cmdline_parser_init); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marius Groeger "); --- linux-2.4.21/drivers/mtd/devices/Config.in~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/Config.in @@ -1,6 +1,6 @@ -# drivers/mtd/maps/Config.in +# drivers/mtd/devices/Config.in -# $Id: Config.in,v 1.8 2003/01/24 23:25:14 dwmw2 Exp $ +# $Id: Config.in,v 1.15 2004/10/01 21:47:13 gleixner Exp $ mainmenu_option next_comment @@ -10,22 +10,13 @@ bool ' PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX bool ' PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG fi -if [ "$CONFIG_DECSTATION" = "y" ]; then +if [ "$CONFIG_MACH__DECSTATION" = "y" ]; then dep_tristate ' DEC MS02-NV NVRAM module support' CONFIG_MTD_MS02NV $CONFIG_MTD fi dep_tristate ' Uncached system RAM' CONFIG_MTD_SLRAM $CONFIG_MTD if [ "$CONFIG_SA1100_LART" = "y" ]; then dep_tristate ' 28F160xx flash driver for LART' CONFIG_MTD_LART $CONFIG_MTD fi -if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then - dep_tristate ' SyncFlash driver for MX1ADS' CONFIG_MTD_SYNCFLASH $CONFIG_MTD -fi -if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then - dep_tristate ' AT91RM9200 DataFlash support' CONFIG_MTD_AT91_DATAFLASH $CONFIG_MTD - if [ "$CONFIG_MTD_AT91_DATAFLASH" = "y" -o "$CONFIG_MTD_AT91_DATAFLASH" = "m" ]; then - bool ' Enable DataFlash card? ' CONFIG_MTD_AT91_DATAFLASH_CARD - fi -fi 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 @@ -37,19 +28,29 @@ dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD 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 + dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)' CONFIG_MTD_DOC2001 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD + if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$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 + if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$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" ]; then + define_bool CONFIG_MTD_DOCECC y + else + if [ "$CONFIG_MTD_DOCPROBE" = "m" ]; then + define_bool CONFIG_MTD_DOCECC m + else + define_bool CONFIG_MTD_DOCECC 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 --- linux-2.4.21/drivers/mtd/devices/Makefile~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/Makefile @@ -1,27 +1,23 @@ # -# linux/drivers/devices/Makefile +# linux/drivers/maps/Makefile.24 +# Makefile for obsolete kernels # -# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $ +# $Id: Makefile.24,v 1.2 2004/08/09 18:46:04 dmarlin Exp $ O_TARGET := devlink.o +export-objs := docecc.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_DOC2001PLUS) += doc2001plus.o +obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o +obj-$(CONFIG_MTD_DOCECC) += docecc.o obj-$(CONFIG_MTD_SLRAM) += slram.o +obj-$(CONFIG_MTD_PHRAM) += phram.o obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o -obj-$(CONFIG_MTD_SYNCFLASH) += syncflash.o -obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o +obj-$(CONFIG_MTD_BLKMTD) += blkmtd-24.o include $(TOPDIR)/Rules.make --- /dev/null +++ linux-2.4.21/drivers/mtd/devices/Makefile.common @@ -0,0 +1,25 @@ +# +# linux/drivers/devices/Makefile +# +# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ + +# *** 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_DOC2000) += doc2000.o +obj-$(CONFIG_MTD_DOC2001) += doc2001.o +obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o +obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o +obj-$(CONFIG_MTD_DOCECC) += docecc.o +obj-$(CONFIG_MTD_SLRAM) += slram.o +obj-$(CONFIG_MTD_PHRAM) += phram.o +obj-$(CONFIG_MTD_PMC551) += pmc551.o +obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o +obj-$(CONFIG_MTD_MTDRAM) += mtdram.o +obj-$(CONFIG_MTD_LART) += lart.o +obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o +obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o --- /dev/null +++ linux-2.4.21/drivers/mtd/devices/blkmtd-24.c @@ -0,0 +1,1056 @@ +/* + * $Id: blkmtd-24.c,v 1.23 2004/08/09 18:49:42 dmarlin Exp $ + * + * blkmtd.c - use a block device as a fake MTD + * + * Author: Simon Evans + * + * Copyright (C) 2001,2002 Simon Evans + * + * Licence: GPL + * + * How it works: + * The driver uses raw/io to read/write the device and the page + * cache to cache access. Writes update the page cache with the + * new data and mark it dirty and add the page into a kiobuf. + * When the kiobuf becomes full or the next extry is to an earlier + * block in the kiobuf then it is flushed to disk. This allows + * writes to remained ordered and gives a small and simple outgoing + * write cache. + * + * It can be loaded Read-Only to prevent erases and writes to the + * medium. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_DEBUG +#ifdef CONFIG_PROC_FS +# include +# define BLKMTD_PROC_DEBUG + static struct proc_dir_entry *blkmtd_proc; +#endif +#endif + + +#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) +#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) + + +/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */ +#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ +#define VERSION "1.10" + +/* Info for the block device */ +struct blkmtd_dev { + struct list_head list; + struct block_device *binding; + struct mtd_info mtd_info; + struct kiobuf *rd_buf, *wr_buf; + long iobuf_locks; + struct semaphore wrbuf_mutex; +}; + + +/* Static info about the MTD, used in cleanup_module */ +static LIST_HEAD(blkmtd_device_list); + + +static void blkmtd_sync(struct mtd_info *mtd); + +#define MAX_DEVICES 4 + +/* Module parameters passed by insmod/modprobe */ +char *device[MAX_DEVICES]; /* the block device to use */ +int erasesz[MAX_DEVICES]; /* optional default erase size */ +int ro[MAX_DEVICES]; /* optional read only flag */ +int sync; + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION("Emulate an MTD using a block device"); +MODULE_PARM(device, "1-4s"); +MODULE_PARM_DESC(device, "block device to use"); +MODULE_PARM(erasesz, "1-4i"); +MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); +MODULE_PARM(ro, "1-4i"); +MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); +MODULE_PARM(sync, "i"); +MODULE_PARM_DESC(sync, "1=Synchronous writes"); + + +/** + * read_pages - read in pages via the page cache + * @dev: device to read from + * @pagenrs: list of page numbers wanted + * @pagelst: storage for struce page * pointers + * @pages: count of pages wanted + * + * Read pages, getting them from the page cache if available + * else reading them in from disk if not. pagelst must be preallocated + * to hold the page count. + */ +static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages) +{ + kdev_t kdev; + struct page *page; + int cnt = 0; + struct kiobuf *iobuf; + int err = 0; + + if(!dev) { + err("read_pages: PANIC dev == NULL"); + return -EIO; + } + kdev = to_kdev_t(dev->binding->bd_dev); + + DEBUG(2, "read_pages: reading %d pages\n", pages); + if(test_and_set_bit(0, &dev->iobuf_locks)) { + err = alloc_kiovec(1, &iobuf); + if (err) { + crit("cant allocate kiobuf"); + return -ENOMEM; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(iobuf->blocks == NULL) { + crit("cant allocate iobuf blocks"); + free_kiovec(1, &iobuf); + return -ENOMEM; + } +#endif + } else { + iobuf = dev->rd_buf; + } + + iobuf->nr_pages = 0; + iobuf->length = 0; + iobuf->offset = 0; + iobuf->locked = 1; + + for(cnt = 0; cnt < pages; cnt++) { + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); + pagelst[cnt] = page; + if(!Page_Uptodate(page)) { + iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; + iobuf->maplist[iobuf->nr_pages++] = page; + } + } + + if(iobuf->nr_pages) { + iobuf->length = iobuf->nr_pages << PAGE_SHIFT; + err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); + DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err); + if(err < 0) { + while(pages--) { + ClearPageUptodate(pagelst[pages]); + unlock_page(pagelst[pages]); + page_cache_release(pagelst[pages]); + } + } else { + while(iobuf->nr_pages--) { + SetPageUptodate(iobuf->maplist[iobuf->nr_pages]); + } + err = 0; + } + } + + + if(iobuf != dev->rd_buf) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + kfree(iobuf->blocks); +#endif + free_kiovec(1, &iobuf); + } else { + clear_bit(0, &dev->iobuf_locks); + } + DEBUG(2, "read_pages: done, err = %d\n", err); + return err; +} + + +/** + * commit_pages - commit pages in the writeout kiobuf to disk + * @dev: device to write to + * + * If the current dev has pages in the dev->wr_buf kiobuf, + * they are written to disk using brw_kiovec() + */ +static int commit_pages(struct blkmtd_dev *dev) +{ + struct kiobuf *iobuf = dev->wr_buf; + kdev_t kdev = to_kdev_t(dev->binding->bd_dev); + int err = 0; + + iobuf->length = iobuf->nr_pages << PAGE_SHIFT; + iobuf->locked = 1; + if(iobuf->length) { + int i; + DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages); + /* Check all the pages are dirty and lock them */ + for(i = 0; i < iobuf->nr_pages; i++) { + struct page *page = iobuf->maplist[i]; + BUG_ON(!PageDirty(page)); + lock_page(page); + } + err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); + DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err); + while(iobuf->nr_pages) { + struct page *page = iobuf->maplist[--iobuf->nr_pages]; + ClearPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + } + + DEBUG(2, "blkmtd: sync: end, err = %d\n", err); + iobuf->offset = 0; + iobuf->nr_pages = 0; + iobuf->length = 0; + return err; +} + + +/** + * write_pages - write block of data to device via the page cache + * @dev: device to write to + * @buf: data source or NULL if erase (output is set to 0xff) + * @to: offset into output device + * @len: amount to data to write + * @retlen: amount of data written + * + * Grab pages from the page cache and fill them with the source data. + * Non page aligned start and end result in a readin of the page and + * part of the page being modified. Pages are added to the wr_buf kiobuf + * until this becomes full or the next page written to has a lower pagenr + * then the current max pagenr in the kiobuf. + */ +static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, + size_t len, int *retlen) +{ + int pagenr, offset; + size_t start_len = 0, end_len; + int pagecnt = 0; + struct kiobuf *iobuf = dev->wr_buf; + int err = 0; + struct page *pagelst[2]; + int pagenrs[2]; + int readpages = 0; + int ignorepage = -1; + + pagenr = to >> PAGE_SHIFT; + offset = to & ~PAGE_MASK; + + DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", + buf, (long)to, len, pagenr, offset); + + *retlen = 0; + /* see if we have to do a partial write at the start */ + if(offset) { + start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; + len -= start_len; + } + + /* calculate the length of the other two regions */ + end_len = len & ~PAGE_MASK; + len -= end_len; + + if(start_len) { + pagenrs[0] = pagenr; + readpages++; + pagecnt++; + } + if(len) + pagecnt += len >> PAGE_SHIFT; + if(end_len) { + pagenrs[readpages] = pagenr + pagecnt; + readpages++; + pagecnt++; + } + + DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", + start_len, len, end_len, pagecnt); + + down(&dev->wrbuf_mutex); + + if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1]) + || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) { + + if((pagenr == iobuf->blocks[iobuf->nr_pages-1]) + && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) { + iobuf->nr_pages--; + ignorepage = pagenr; + } else { + DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n", + pagenr, iobuf->blocks[iobuf->nr_pages-1], + pagecnt, iobuf->nr_pages); + commit_pages(dev); + } + } + + if(readpages) { + err = read_pages(dev, pagenrs, pagelst, readpages); + if(err < 0) + goto readin_err; + } + + if(start_len) { + /* do partial start region */ + struct page *page; + + DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", + pagenr, start_len, offset); + page = pagelst[0]; + BUG_ON(!buf); + if(PageDirty(page) && pagenr != ignorepage) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", + to, start_len, len, end_len, pagenr, ignorepage); + BUG(); + } + memcpy(page_address(page)+offset, buf, start_len); + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + buf += start_len; + *retlen = start_len; + err = 0; + iobuf->blocks[iobuf->nr_pages] = pagenr++; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + } + + /* Now do the main loop to a page aligned, n page sized output */ + if(len) { + int pagesc = len >> PAGE_SHIFT; + DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", + pagenr, pagesc); + while(pagesc) { + struct page *page; + + /* see if page is in the page cache */ + DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenr); + if(PageDirty(page) && pagenr != ignorepage) { + BUG(); + } + if(!page) { + warn("write: cant grab cache page %d", pagenr); + err = -ENOMEM; + goto write_err; + } + if(!buf) { + memset(page_address(page), 0xff, PAGE_SIZE); + } else { + memcpy(page_address(page), buf, PAGE_SIZE); + buf += PAGE_SIZE; + } + iobuf->blocks[iobuf->nr_pages] = pagenr++; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + pagesc--; + *retlen += PAGE_SIZE; + } + } + + if(end_len) { + /* do the third region */ + struct page *page; + DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", + pagenr, end_len); + page = pagelst[readpages-1]; + BUG_ON(!buf); + if(PageDirty(page) && pagenr != ignorepage) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", + to, start_len, len, end_len, pagenr, ignorepage); + BUG(); + } + memcpy(page_address(page), buf, end_len); + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + DEBUG(3, "blkmtd: write: writing out partial end\n"); + *retlen += end_len; + err = 0; + iobuf->blocks[iobuf->nr_pages] = pagenr; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + } + + DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); + + if(sync) { +write_err: + commit_pages(dev); + } + +readin_err: + up(&dev->wrbuf_mutex); + return err; +} + + +/* erase a specified part of the device */ +static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct blkmtd_dev *dev = mtd->priv; + struct mtd_erase_region_info *einfo = mtd->eraseregions; + int numregions = mtd->numeraseregions; + size_t from; + u_long len; + int err = -EIO; + size_t retlen; + + /* check readonly */ + if(!dev->wr_buf) { + err("error: mtd%d trying to erase readonly device %s", + mtd->index, mtd->name); + instr->state = MTD_ERASE_FAILED; + goto erase_callback; + } + + instr->state = MTD_ERASING; + from = instr->addr; + len = instr->len; + + /* check erase region has valid start and length */ + DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", + bdevname(dev->binding->bd_dev), from, len); + while(numregions) { + DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", + einfo->offset, einfo->erasesize, einfo->numblocks); + if(from >= einfo->offset + && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { + if(len == einfo->erasesize + && ( (from - einfo->offset) % einfo->erasesize == 0)) + break; + } + numregions--; + einfo++; + } + + if(!numregions) { + /* Not a valid erase block */ + err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); + instr->state = MTD_ERASE_FAILED; + err = -EIO; + } + + if(instr->state != MTD_ERASE_FAILED) { + /* do the erase */ + DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); + err = write_pages(dev, NULL, from, len, &retlen); + if(err < 0) { + err("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + err = 0; + } + } + + DEBUG(3, "blkmtd: erase: checking callback\n"); + erase_callback: + mtd_erase_callback(instr); + DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); + return err; +} + + +/* read a range of the data via the page cache */ +static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct blkmtd_dev *dev = mtd->priv; + int err = 0; + int offset; + int pagenr, pages; + struct page **pagelst; + int *pagenrs; + int i; + + *retlen = 0; + + DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", + bdevname(dev->binding->bd_dev), from, len, buf); + + pagenr = from >> PAGE_SHIFT; + offset = from - (pagenr << PAGE_SHIFT); + + pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; + DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", + pagenr, offset, pages); + + pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL); + if(!pagelst) + return -ENOMEM; + pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL); + if(!pagenrs) { + kfree(pagelst); + return -ENOMEM; + } + for(i = 0; i < pages; i++) + pagenrs[i] = pagenr+i; + + err = read_pages(dev, pagenrs, pagelst, pages); + if(err) + goto readerr; + + pagenr = 0; + while(pages) { + struct page *page; + int cpylen; + + DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); + page = pagelst[pagenr]; + + cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; + if(offset+cpylen > PAGE_SIZE) + cpylen = PAGE_SIZE-offset; + + memcpy(buf + *retlen, page_address(page) + offset, cpylen); + offset = 0; + len -= cpylen; + *retlen += cpylen; + pagenr++; + pages--; + unlock_page(page); + if(!PageDirty(page)) + page_cache_release(page); + } + + readerr: + kfree(pagelst); + kfree(pagenrs); + DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", *retlen, err); + return err; +} + + +/* write data to the underlying device */ +static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct blkmtd_dev *dev = mtd->priv; + int err; + + *retlen = 0; + if(!len) + return 0; + + DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", + bdevname(dev->binding->bd_dev), to, len, buf); + + /* handle readonly and out of range numbers */ + + if(!dev->wr_buf) { + err("error: trying to write to a readonly device %s", mtd->name); + return -EROFS; + } + + if(to >= mtd->size) { + return -ENOSPC; + } + + if(to + len > mtd->size) { + len = (mtd->size - to); + } + + err = write_pages(dev, buf, to, len, retlen); + if(err < 0) + *retlen = 0; + else + err = 0; + DEBUG(2, "blkmtd: write: end, err = %d\n", err); + return err; +} + + +/* sync the device - wait until the write queue is empty */ +static void blkmtd_sync(struct mtd_info *mtd) +{ + struct blkmtd_dev *dev = mtd->priv; + struct kiobuf *iobuf = dev->wr_buf; + + DEBUG(2, "blkmtd: sync: called\n"); + if(iobuf == NULL) + return; + + DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", + iobuf->length, iobuf->nr_pages); + down(&dev->wrbuf_mutex); + if(iobuf->nr_pages) + commit_pages(dev); + up(&dev->wrbuf_mutex); +} + + +#ifdef BLKMTD_PROC_DEBUG +/* procfs stuff */ +static int blkmtd_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + struct list_head *temp1, *temp2; + + MOD_INC_USE_COUNT; + + /* Count the size of the page lists */ + + len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); + list_for_each_safe(temp1, temp2, &blkmtd_device_list) { + struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, + list); + struct list_head *temp; + struct page *pagei; + + int clean = 0, dirty = 0, locked = 0, lru = 0; + /* Count the size of the page lists */ + list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { + pagei = list_entry(temp, struct page, list); + clean++; + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { + pagei = list_entry(temp, struct page, list); + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { + pagei = list_entry(temp, struct page, list); + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + + len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", + dev->mtd_info.index, + (dev->wr_buf && dev->wr_buf->nr_pages) ? + dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, + (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, + dev->binding->bd_inode->i_mapping->nrpages, + clean, dirty, locked, lru); + } + + if(len <= count) + *eof = 1; + + MOD_DEC_USE_COUNT; + return len; +} +#endif + + +static void free_device(struct blkmtd_dev *dev) +{ + DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); + if(dev) { + del_mtd_device(&dev->mtd_info); + info("mtd%d: [%s] removed", dev->mtd_info.index, + dev->mtd_info.name + strlen("blkmtd: ")); + if(dev->mtd_info.eraseregions) + kfree(dev->mtd_info.eraseregions); + if(dev->mtd_info.name) + kfree(dev->mtd_info.name); + + if(dev->rd_buf) { + dev->rd_buf->locked = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + if(dev->rd_buf->blocks) + kfree(dev->rd_buf->blocks); +#endif + free_kiovec(1, &dev->rd_buf); + } + if(dev->wr_buf) { + dev->wr_buf->locked = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + if(dev->wr_buf->blocks) + kfree(dev->rw_buf->blocks); +#endif + free_kiovec(1, &dev->wr_buf); + } + + if(dev->binding) { + kdev_t kdev = to_kdev_t(dev->binding->bd_dev); + invalidate_inode_pages(dev->binding->bd_inode); + set_blocksize(kdev, 1 << 10); + blkdev_put(dev->binding, BDEV_RAW); + } + kfree(dev); + } +} + + +/* For a given size and initial erase size, calculate the number + * and size of each erase region. Goes round the loop twice, + * once to find out how many regions, then allocates space, + * then round the loop again to fill it in. + */ +static struct mtd_erase_region_info *calc_erase_regions( + size_t erase_size, size_t total_size, int *regions) +{ + struct mtd_erase_region_info *info = NULL; + + DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + /* Make any user specified erasesize be a power of 2 + and at least PAGE_SIZE */ + if(erase_size) { + int es = erase_size; + erase_size = 1; + while(es != 1) { + es >>= 1; + erase_size <<= 1; + } + if(erase_size < PAGE_SIZE) + erase_size = PAGE_SIZE; + } else { + erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; + } + + *regions = 0; + + do { + int tot_size = total_size; + int er_size = erase_size; + int count = 0, offset = 0, regcnt = 0; + + while(tot_size) { + count = tot_size / er_size; + if(count) { + tot_size = tot_size % er_size; + if(info) { + DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", + offset, er_size, count); + (info+regcnt)->offset = offset; + (info+regcnt)->erasesize = er_size; + (info+regcnt)->numblocks = count; + (*regions)++; + } + regcnt++; + offset += (count * er_size); + } + while(er_size > tot_size) + er_size >>= 1; + } + if(info == NULL) { + info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); + if(!info) + break; + } + } while(!(*regions)); + DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + return info; +} + + +extern kdev_t name_to_kdev_t(char *line) __init; + + +static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) +{ + int maj, min; + kdev_t kdev; + int mode; + struct blkmtd_dev *dev; + +#ifdef MODULE + struct file *file = NULL; + struct inode *inode; +#endif + + if(!devname) + return NULL; + + /* Get a handle on the device */ + mode = (readonly) ? O_RDONLY : O_RDWR; + +#ifdef MODULE + + file = filp_open(devname, mode, 0); + if(IS_ERR(file)) { + err("error: cant open device %s", devname); + DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); + return NULL; + } + + /* determine is this is a block device and + * if so get its major and minor numbers + */ + inode = file->f_dentry->d_inode; + if(!S_ISBLK(inode->i_mode)) { + err("%s not a block device", devname); + filp_close(file, NULL); + return NULL; + } + kdev = inode->i_rdev; + filp_close(file, NULL); +#else + kdev = name_to_kdev_t(devname); +#endif /* MODULE */ + + if(!kdev) { + err("bad block device: `%s'", devname); + return NULL; + } + + maj = MAJOR(kdev); + min = MINOR(kdev); + DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", + maj, min); + + if(maj == MTD_BLOCK_MAJOR) { + err("attempting to use an MTD device as a block device"); + return NULL; + } + + DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); + + dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); + if(dev == NULL) + return NULL; + + memset(dev, 0, sizeof(struct blkmtd_dev)); + if(alloc_kiovec(1, &dev->rd_buf)) { + err("cant allocate read iobuf"); + goto devinit_err; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(dev->rd_buf->blocks == NULL) { + crit("cant allocate rd_buf blocks"); + goto devinit_err; + } +#endif + + if(!readonly) { + if(alloc_kiovec(1, &dev->wr_buf)) { + err("cant allocate kiobuf - readonly enabled"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + } else { + dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(dev->wr_buf->blocks == NULL) { + crit("cant allocate wr_buf blocks - readonly enabled"); + free_kiovec(1, &iobuf); + } +#endif + } + if(dev->wr_buf) + init_MUTEX(&dev->wrbuf_mutex); + } + + /* get the block device */ + dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); + if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) + goto devinit_err; + + if(set_blocksize(kdev, PAGE_SIZE)) { + err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); + goto devinit_err; + } + + dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); + if(dev->mtd_info.name == NULL) + goto devinit_err; + + sprintf(dev->mtd_info.name, "blkmtd: %s", devname); + dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, + &dev->mtd_info.numeraseregions); + if(dev->mtd_info.eraseregions == NULL) + goto devinit_err; + + dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; + DEBUG(1, "blkmtd: init: found %d erase regions\n", + dev->mtd_info.numeraseregions); + + if(readonly) { + dev->mtd_info.type = MTD_ROM; + dev->mtd_info.flags = MTD_CAP_ROM; + } else { + dev->mtd_info.type = MTD_RAM; + dev->mtd_info.flags = MTD_CAP_RAM; + } + dev->mtd_info.erase = blkmtd_erase; + dev->mtd_info.read = blkmtd_read; + dev->mtd_info.write = blkmtd_write; + dev->mtd_info.sync = blkmtd_sync; + dev->mtd_info.point = 0; + dev->mtd_info.unpoint = 0; + dev->mtd_info.priv = dev; + dev->mtd_info.owner = THIS_MODULE; + + list_add(&dev->list, &blkmtd_device_list); + if (add_mtd_device(&dev->mtd_info)) { + /* Device didnt get added, so free the entry */ + list_del(&dev->list); + free_device(dev); + return NULL; + } else { + info("mtd%d: [%s] erase_size = %dKiB %s", + dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), + dev->mtd_info.erasesize >> 10, + (dev->wr_buf) ? "" : "(read-only)"); + } + + return dev; + + devinit_err: + free_device(dev); + return NULL; +} + + +/* Cleanup and exit - sync the device and kill of the kernel thread */ +static void __devexit cleanup_blkmtd(void) +{ + struct list_head *temp1, *temp2; +#ifdef BLKMTD_PROC_DEBUG + if(blkmtd_proc) { + remove_proc_entry("blkmtd_debug", NULL); + } +#endif + + /* Remove the MTD devices */ + list_for_each_safe(temp1, temp2, &blkmtd_device_list) { + struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, + list); + blkmtd_sync(&dev->mtd_info); + free_device(dev); + } +} + +#ifndef MODULE + +/* Handle kernel boot params */ + + +static int __init param_blkmtd_device(char *str) +{ + int i; + + for(i = 0; i < MAX_DEVICES; i++) { + device[i] = str; + DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); + strsep(&str, ","); + } + return 1; +} + + +static int __init param_blkmtd_erasesz(char *str) +{ + int i; + for(i = 0; i < MAX_DEVICES; i++) { + char *val = strsep(&str, ","); + if(val) + erasesz[i] = simple_strtoul(val, NULL, 0); + DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); + } + + return 1; +} + + +static int __init param_blkmtd_ro(char *str) +{ + int i; + for(i = 0; i < MAX_DEVICES; i++) { + char *val = strsep(&str, ","); + if(val) + ro[i] = simple_strtoul(val, NULL, 0); + DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); + } + + return 1; +} + + +static int __init param_blkmtd_sync(char *str) +{ + if(str[0] == '1') + sync = 1; + return 1; +} + +__setup("blkmtd_device=", param_blkmtd_device); +__setup("blkmtd_erasesz=", param_blkmtd_erasesz); +__setup("blkmtd_ro=", param_blkmtd_ro); +__setup("blkmtd_sync=", param_blkmtd_sync); + +#endif + + +/* Startup */ +static int __init init_blkmtd(void) +{ + int i; + + /* Check args - device[0] is the bare minimum*/ + if(!device[0]) { + err("error: missing `device' name\n"); + return -EINVAL; + } + + for(i = 0; i < MAX_DEVICES; i++) + add_device(device[i], ro[i], erasesz[i] << 10); + + if(list_empty(&blkmtd_device_list)) + goto init_err; + + info("version " VERSION); + +#ifdef BLKMTD_PROC_DEBUG + /* create proc entry */ + DEBUG(2, "Creating /proc/blkmtd_debug\n"); + blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, + NULL, blkmtd_proc_read, NULL); + if(blkmtd_proc == NULL) { + err("Cant create /proc/blkmtd_debug"); + } else { + blkmtd_proc->owner = THIS_MODULE; + } +#endif + + if(!list_empty(&blkmtd_device_list)) + /* Everything is ok if we got here */ + return 0; + + init_err: + return -EINVAL; +} + +module_init(init_blkmtd); +module_exit(cleanup_blkmtd); --- linux-2.4.21/drivers/mtd/devices/blkmtd.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/blkmtd.c @@ -1,5 +1,5 @@ /* - * $Id: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $ + * $Id: blkmtd.c,v 1.24 2004/11/16 18:29:01 dwmw2 Exp $ * * blkmtd.c - use a block device as a fake MTD * @@ -12,11 +12,8 @@ * How it works: * The driver uses raw/io to read/write the device and the page * cache to cache access. Writes update the page cache with the - * new data and mark it dirty and add the page into a kiobuf. - * When the kiobuf becomes full or the next extry is to an earlier - * block in the kiobuf then it is flushed to disk. This allows - * writes to remained ordered and gives a small and simple outgoing - * write cache. + * new data and mark it dirty and add the page into a BIO which + * is then written out. * * It can be loaded Read-Only to prevent erases and writes to the * medium. @@ -27,20 +24,12 @@ #include #include #include -#include -#include +#include #include #include +#include #include -#ifdef CONFIG_MTD_DEBUG -#ifdef CONFIG_PROC_FS -# include -# define BLKMTD_PROC_DEBUG - static struct proc_dir_entry *blkmtd_proc; -#endif -#endif - #define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) #define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) @@ -48,17 +37,15 @@ #define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) -/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */ +/* Default erase size in K, always make it a multiple of PAGE_SIZE */ #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ -#define VERSION "1.10" +#define VERSION "$Revision: 1.24 $" /* Info for the block device */ struct blkmtd_dev { struct list_head list; - struct block_device *binding; + struct block_device *blkdev; struct mtd_info mtd_info; - struct kiobuf *rd_buf, *wr_buf; - long iobuf_locks; struct semaphore wrbuf_mutex; }; @@ -72,10 +59,10 @@ #define MAX_DEVICES 4 /* Module parameters passed by insmod/modprobe */ -char *device[MAX_DEVICES]; /* the block device to use */ -int erasesz[MAX_DEVICES]; /* optional default erase size */ -int ro[MAX_DEVICES]; /* optional read only flag */ -int sync; +static char *device[MAX_DEVICES]; /* the block device to use */ +static int erasesz[MAX_DEVICES]; /* optional default erase size */ +static int ro[MAX_DEVICES]; /* optional read only flag */ +static int sync; MODULE_LICENSE("GPL"); @@ -91,136 +78,145 @@ MODULE_PARM_DESC(sync, "1=Synchronous writes"); -/** - * read_pages - read in pages via the page cache - * @dev: device to read from - * @pagenrs: list of page numbers wanted - * @pagelst: storage for struce page * pointers - * @pages: count of pages wanted - * - * Read pages, getting them from the page cache if available - * else reading them in from disk if not. pagelst must be preallocated - * to hold the page count. - */ -static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages) +/* completion handler for BIO reads */ +static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) { - kdev_t kdev; - struct page *page; - int cnt = 0; - struct kiobuf *iobuf; - int err = 0; + if (bio->bi_size) + return 1; - if(!dev) { - err("read_pages: PANIC dev == NULL"); - return -EIO; - } - kdev = to_kdev_t(dev->binding->bd_dev); + complete((struct completion*)bio->bi_private); + return 0; +} - DEBUG(2, "read_pages: reading %d pages\n", pages); - if(test_and_set_bit(0, &dev->iobuf_locks)) { - err = alloc_kiovec(1, &iobuf); - if (err) { - crit("cant allocate kiobuf"); - return -ENOMEM; - } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); - if(iobuf->blocks == NULL) { - crit("cant allocate iobuf blocks"); - free_kiovec(1, &iobuf); - return -ENOMEM; - } -#endif + +/* completion handler for BIO writes */ +static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) +{ + const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; + + if (bio->bi_size) + return 1; + + if(!uptodate) + err("bi_write_complete: not uptodate\n"); + + do { + struct page *page = bvec->bv_page; + DEBUG(3, "Cleaning up page %ld\n", page->index); + if (--bvec >= bio->bi_io_vec) + prefetchw(&bvec->bv_page->flags); + + if (uptodate) { + SetPageUptodate(page); } else { - iobuf = dev->rd_buf; + ClearPageUptodate(page); + SetPageError(page); } + ClearPageDirty(page); + unlock_page(page); + page_cache_release(page); + } while (bvec >= bio->bi_io_vec); - iobuf->nr_pages = 0; - iobuf->length = 0; - iobuf->offset = 0; - iobuf->locked = 1; + complete((struct completion*)bio->bi_private); + return 0; +} - for(cnt = 0; cnt < pages; cnt++) { - page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); - pagelst[cnt] = page; - if(!PageUptodate(page)) { - iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; - iobuf->maplist[iobuf->nr_pages++] = page; - } - } - if(iobuf->nr_pages) { - iobuf->length = iobuf->nr_pages << PAGE_SHIFT; - err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); - DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err); - if(err < 0) { - while(pages--) { - ClearPageUptodate(pagelst[pages]); - unlock_page(pagelst[pages]); - page_cache_release(pagelst[pages]); - } - } else { - while(iobuf->nr_pages--) { - SetPageUptodate(iobuf->maplist[iobuf->nr_pages]); +/* read one page from the block device */ +static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) +{ + struct bio *bio; + struct completion event; + int err = -ENOMEM; + + if(PageUptodate(page)) { + DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); + unlock_page(page); + return 0; } - err = 0; + + ClearPageUptodate(page); + ClearPageError(page); + + bio = bio_alloc(GFP_KERNEL, 1); + if(bio) { + init_completion(&event); + bio->bi_bdev = dev->blkdev; + bio->bi_sector = page->index << (PAGE_SHIFT-9); + bio->bi_private = &event; + bio->bi_end_io = bi_read_complete; + if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { + submit_bio(READ_SYNC, bio); + wait_for_completion(&event); + err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; + bio_put(bio); } } + if(err) + SetPageError(page); + else + SetPageUptodate(page); + flush_dcache_page(page); + unlock_page(page); + return err; +} - if(iobuf != dev->rd_buf) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - kfree(iobuf->blocks); -#endif - free_kiovec(1, &iobuf); - } else { - clear_bit(0, &dev->iobuf_locks); + +/* write out the current BIO and wait for it to finish */ +static int blkmtd_write_out(struct bio *bio) +{ + struct completion event; + int err; + + if(!bio->bi_vcnt) { + bio_put(bio); + return 0; } - DEBUG(2, "read_pages: done, err = %d\n", err); + + init_completion(&event); + bio->bi_private = &event; + bio->bi_end_io = bi_write_complete; + submit_bio(WRITE_SYNC, bio); + wait_for_completion(&event); + DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); + err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; + bio_put(bio); return err; } /** - * commit_pages - commit pages in the writeout kiobuf to disk - * @dev: device to write to + * blkmtd_add_page - add a page to the current BIO + * @bio: bio to add to (NULL to alloc initial bio) + * @blkdev: block device + * @page: page to add + * @pagecnt: pages left to add * - * If the current dev has pages in the dev->wr_buf kiobuf, - * they are written to disk using brw_kiovec() + * Adds a page to the current bio, allocating it if necessary. If it cannot be + * added, the current bio is written out and a new one is allocated. Returns + * the new bio to add or NULL on error */ -static int commit_pages(struct blkmtd_dev *dev) +static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, + struct page *page, int pagecnt) { - struct kiobuf *iobuf = dev->wr_buf; - kdev_t kdev = to_kdev_t(dev->binding->bd_dev); - int err = 0; - iobuf->length = iobuf->nr_pages << PAGE_SHIFT; - iobuf->locked = 1; - if(iobuf->length) { - int i; - DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages); - /* Check all the pages are dirty and lock them */ - for(i = 0; i < iobuf->nr_pages; i++) { - struct page *page = iobuf->maplist[i]; - BUG_ON(!PageDirty(page)); - lock_page(page); - } - err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); - DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err); - while(iobuf->nr_pages) { - struct page *page = iobuf->maplist[--iobuf->nr_pages]; - ClearPageDirty(page); - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - } + retry: + if(!bio) { + bio = bio_alloc(GFP_KERNEL, pagecnt); + if(!bio) + return NULL; + bio->bi_sector = page->index << (PAGE_SHIFT-9); + bio->bi_bdev = blkdev; } - DEBUG(2, "blkmtd: sync: end, err = %d\n", err); - iobuf->offset = 0; - iobuf->nr_pages = 0; - iobuf->length = 0; - return err; + if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { + blkmtd_write_out(bio); + bio = NULL; + goto retry; + } + return bio; } @@ -234,30 +230,25 @@ * * Grab pages from the page cache and fill them with the source data. * Non page aligned start and end result in a readin of the page and - * part of the page being modified. Pages are added to the wr_buf kiobuf - * until this becomes full or the next page written to has a lower pagenr - * then the current max pagenr in the kiobuf. + * part of the page being modified. Pages are added to the bio and then written + * out. */ static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, - size_t len, int *retlen) + size_t len, size_t *retlen) { int pagenr, offset; size_t start_len = 0, end_len; int pagecnt = 0; - struct kiobuf *iobuf = dev->wr_buf; int err = 0; - struct page *pagelst[2]; - int pagenrs[2]; - int readpages = 0; - int ignorepage = -1; + struct bio *bio = NULL; + size_t thislen = 0; pagenr = to >> PAGE_SHIFT; offset = to & ~PAGE_MASK; - DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n", + DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", buf, (long)to, len, pagenr, offset); - *retlen = 0; /* see if we have to do a partial write at the start */ if(offset) { start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; @@ -268,68 +259,48 @@ end_len = len & ~PAGE_MASK; len -= end_len; - if(start_len) { - pagenrs[0] = pagenr; - readpages++; + if(start_len) pagecnt++; - } + if(len) pagecnt += len >> PAGE_SHIFT; - if(end_len) { - pagenrs[readpages] = pagenr + pagecnt; - readpages++; - pagecnt++; - } - DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n", - start_len, len, end_len, pagecnt); + if(end_len) + pagecnt++; down(&dev->wrbuf_mutex); - if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1]) - || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) { - - if((pagenr == iobuf->blocks[iobuf->nr_pages-1]) - && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) { - iobuf->nr_pages--; - ignorepage = pagenr; - } else { - DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n", - pagenr, iobuf->blocks[iobuf->nr_pages-1], - pagecnt, iobuf->nr_pages); - commit_pages(dev); - } - } - - if(readpages) { - err = read_pages(dev, pagenrs, pagelst, readpages); - if(err < 0) - goto readin_err; - } + DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", + start_len, len, end_len, pagecnt); if(start_len) { /* do partial start region */ struct page *page; - DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", + DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", pagenr, start_len, offset); - page = pagelst[0]; + BUG_ON(!buf); - if(PageDirty(page) && pagenr != ignorepage) { - err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n", - to, start_len, len, end_len, pagenr, ignorepage); + page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); + lock_page(page); + if(PageDirty(page)) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", + to, start_len, len, end_len, pagenr); BUG(); } memcpy(page_address(page)+offset, buf, start_len); SetPageDirty(page); SetPageUptodate(page); - unlock_page(page); buf += start_len; - *retlen = start_len; - err = 0; - iobuf->blocks[iobuf->nr_pages] = pagenr++; - iobuf->maplist[iobuf->nr_pages] = page; - iobuf->nr_pages++; + thislen = start_len; + bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); + if(!bio) { + err = -ENOMEM; + err("bio_add_page failed\n"); + goto write_err; + } + pagecnt--; + pagenr++; } /* Now do the main loop to a page aligned, n page sized output */ @@ -342,12 +313,12 @@ /* see if page is in the page cache */ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); - page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenr); - if(PageDirty(page) && pagenr != ignorepage) { + page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); + if(PageDirty(page)) { BUG(); } if(!page) { - warn("write: cant grab cache page %d", pagenr); + warn("write: cannot grab cache page %d", pagenr); err = -ENOMEM; goto write_err; } @@ -357,50 +328,58 @@ memcpy(page_address(page), buf, PAGE_SIZE); buf += PAGE_SIZE; } - iobuf->blocks[iobuf->nr_pages] = pagenr++; - iobuf->maplist[iobuf->nr_pages] = page; - iobuf->nr_pages++; + bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); + if(!bio) { + err = -ENOMEM; + err("bio_add_page failed\n"); + goto write_err; + } + pagenr++; + pagecnt--; SetPageDirty(page); SetPageUptodate(page); - unlock_page(page); pagesc--; - *retlen += PAGE_SIZE; + thislen += PAGE_SIZE; } } if(end_len) { /* do the third region */ struct page *page; - DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", + DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", pagenr, end_len); - page = pagelst[readpages-1]; BUG_ON(!buf); - if(PageDirty(page) && pagenr != ignorepage) { - err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n", - to, start_len, len, end_len, pagenr, ignorepage); + page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); + lock_page(page); + if(PageDirty(page)) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", + to, start_len, len, end_len, pagenr); BUG(); } memcpy(page_address(page), buf, end_len); SetPageDirty(page); SetPageUptodate(page); - unlock_page(page); DEBUG(3, "blkmtd: write: writing out partial end\n"); - *retlen += end_len; - err = 0; - iobuf->blocks[iobuf->nr_pages] = pagenr; - iobuf->maplist[iobuf->nr_pages] = page; - iobuf->nr_pages++; + thislen += end_len; + bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); + if(!bio) { + err = -ENOMEM; + err("bio_add_page failed\n"); + goto write_err; } - - DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err); - - if(sync) { -write_err: - commit_pages(dev); + pagenr++; } -readin_err: + DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); + write_err: + if(bio) + blkmtd_write_out(bio); + + DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); up(&dev->wrbuf_mutex); + + if(retlen) + *retlen = thislen; return err; } @@ -414,23 +393,15 @@ size_t from; u_long len; int err = -EIO; - int retlen; - - /* check readonly */ - if(!dev->wr_buf) { - err("error: mtd%d trying to erase readonly device %s", - mtd->index, mtd->name); - instr->state = MTD_ERASE_FAILED; - goto erase_callback; - } + size_t retlen; instr->state = MTD_ERASING; from = instr->addr; len = instr->len; /* check erase region has valid start and length */ - DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n", - bdevname(dev->binding->bd_dev), from, len); + DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", + mtd->name+9, from, len); while(numregions) { DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", einfo->offset, einfo->erasesize, einfo->numblocks); @@ -446,29 +417,25 @@ if(!numregions) { /* Not a valid erase block */ - err("erase: invalid erase request 0x%lX @ 0x%08X", len, from); + err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); instr->state = MTD_ERASE_FAILED; err = -EIO; } if(instr->state != MTD_ERASE_FAILED) { /* do the erase */ - DEBUG(3, "Doing erase from = %d len = %ld\n", from, len); + DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); err = write_pages(dev, NULL, from, len, &retlen); - if(err < 0) { + if(err || retlen != len) { err("erase failed err = %d", err); instr->state = MTD_ERASE_FAILED; } else { instr->state = MTD_ERASE_DONE; - err = 0; } } DEBUG(3, "blkmtd: erase: checking callback\n"); - erase_callback: - if (instr->callback) { - (*(instr->callback))(instr); - } + mtd_erase_callback(instr); DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); return err; } @@ -482,14 +449,15 @@ int err = 0; int offset; int pagenr, pages; - struct page **pagelst; - int *pagenrs; - int i; + size_t thislen = 0; - *retlen = 0; + DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", + mtd->name+9, from, len, buf); - DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n", - bdevname(dev->binding->bd_dev), (long int)from, len, buf); + if(from > mtd->size) + return -EINVAL; + if(from + len > mtd->size) + len = mtd->size - from; pagenr = from >> PAGE_SHIFT; offset = from - (pagenr << PAGE_SHIFT); @@ -498,48 +466,35 @@ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", pagenr, offset, pages); - pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL); - if(!pagelst) - return -ENOMEM; - pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL); - if(!pagenrs) { - kfree(pagelst); - return -ENOMEM; - } - for(i = 0; i < pages; i++) - pagenrs[i] = pagenr+i; - - err = read_pages(dev, pagenrs, pagelst, pages); - if(err) - goto readerr; - - pagenr = 0; while(pages) { struct page *page; int cpylen; DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); - page = pagelst[pagenr]; + page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); + if(IS_ERR(page)) { + err = -EIO; + goto readerr; + } cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; if(offset+cpylen > PAGE_SIZE) cpylen = PAGE_SIZE-offset; - memcpy(buf + *retlen, page_address(page) + offset, cpylen); + memcpy(buf + thislen, page_address(page) + offset, cpylen); offset = 0; len -= cpylen; - *retlen += cpylen; + thislen += cpylen; pagenr++; pages--; - unlock_page(page); if(!PageDirty(page)) page_cache_release(page); } readerr: - kfree(pagelst); - kfree(pagenrs); - DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", *retlen, err); + if(retlen) + *retlen = thislen; + DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err); return err; } @@ -551,32 +506,22 @@ struct blkmtd_dev *dev = mtd->priv; int err; - *retlen = 0; if(!len) return 0; - DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n", - bdevname(dev->binding->bd_dev), (long int)to, len, buf); - - /* handle readonly and out of range numbers */ - - if(!dev->wr_buf) { - err("error: trying to write to a readonly device %s", mtd->name); - return -EROFS; - } + DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", + mtd->name+9, to, len, buf); if(to >= mtd->size) { return -ENOSPC; } if(to + len > mtd->size) { - len = (mtd->size - to); + len = mtd->size - to; } err = write_pages(dev, buf, to, len, retlen); - if(err < 0) - *retlen = 0; - else + if(err > 0) err = 0; DEBUG(2, "blkmtd: write: end, err = %d\n", err); return err; @@ -586,124 +531,22 @@ /* sync the device - wait until the write queue is empty */ static void blkmtd_sync(struct mtd_info *mtd) { - struct blkmtd_dev *dev = mtd->priv; - struct kiobuf *iobuf = dev->wr_buf; - - DEBUG(2, "blkmtd: sync: called\n"); - if(iobuf == NULL) - return; - - DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", - iobuf->length, iobuf->nr_pages); - down(&dev->wrbuf_mutex); - if(iobuf->nr_pages) - commit_pages(dev); - up(&dev->wrbuf_mutex); -} - - -#ifdef BLKMTD_PROC_DEBUG -/* procfs stuff */ -static int blkmtd_proc_read(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - struct list_head *temp1, *temp2; - - MOD_INC_USE_COUNT; - - /* Count the size of the page lists */ - - len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); - list_for_each_safe(temp1, temp2, &blkmtd_device_list) { - struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, - list); - struct list_head *temp; - struct page *pagei; - - int clean = 0, dirty = 0, locked = 0, lru = 0; - /* Count the size of the page lists */ - list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { - pagei = list_entry(temp, struct page, list); - clean++; - if(PageLocked(pagei)) - locked++; - if(PageDirty(pagei)) - dirty++; - if(PageLRU(pagei)) - lru++; - } - list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { - pagei = list_entry(temp, struct page, list); - if(PageLocked(pagei)) - locked++; - if(PageDirty(pagei)) - dirty++; - if(PageLRU(pagei)) - lru++; - } - list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { - pagei = list_entry(temp, struct page, list); - if(PageLocked(pagei)) - locked++; - if(PageDirty(pagei)) - dirty++; - if(PageLRU(pagei)) - lru++; - } - - len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", - dev->mtd_info.index, - (dev->wr_buf && dev->wr_buf->nr_pages) ? - dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, - (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, - dev->binding->bd_inode->i_mapping->nrpages, - clean, dirty, locked, lru); - } - - if(len <= count) - *eof = 1; - - MOD_DEC_USE_COUNT; - return len; + /* Currently all writes are synchronous */ } -#endif static void free_device(struct blkmtd_dev *dev) { DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); if(dev) { - del_mtd_device(&dev->mtd_info); - info("mtd%d: [%s] removed", dev->mtd_info.index, - dev->mtd_info.name + strlen("blkmtd: ")); if(dev->mtd_info.eraseregions) kfree(dev->mtd_info.eraseregions); if(dev->mtd_info.name) kfree(dev->mtd_info.name); - if(dev->rd_buf) { - dev->rd_buf->locked = 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - if(dev->rd_buf->blocks) - kfree(dev->rd_buf->blocks); -#endif - free_kiovec(1, &dev->rd_buf); - } - if(dev->wr_buf) { - dev->wr_buf->locked = 0; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - if(dev->wr_buf->blocks) - kfree(dev->rw_buf->blocks); -#endif - free_kiovec(1, &dev->wr_buf); - } - - if(dev->binding) { - kdev_t kdev = to_kdev_t(dev->binding->bd_dev); - invalidate_inode_pages(dev->binding->bd_inode); - set_blocksize(kdev, 1 << 10); - blkdev_put(dev->binding, BDEV_RAW); + if(dev->blkdev) { + invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); + close_bdev_excl(dev->blkdev); } kfree(dev); } @@ -720,7 +563,7 @@ { struct mtd_erase_region_info *info = NULL; - DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n", + DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); /* Make any user specified erasesize be a power of 2 and at least PAGE_SIZE */ @@ -768,119 +611,62 @@ break; } } while(!(*regions)); - DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n", + DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", erase_size, total_size, *regions); return info; } -extern kdev_t name_to_kdev_t(char *line) __init; - +extern dev_t __init name_to_dev_t(const char *line); static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) { - int maj, min; - kdev_t kdev; + struct block_device *bdev; int mode; struct blkmtd_dev *dev; -#ifdef MODULE - struct file *file = NULL; - struct inode *inode; -#endif - if(!devname) return NULL; /* Get a handle on the device */ - mode = (readonly) ? O_RDONLY : O_RDWR; -#ifdef MODULE - file = filp_open(devname, mode, 0); - if(IS_ERR(file)) { - err("error: cant open device %s", devname); - DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); - return NULL; - } - - /* determine is this is a block device and - * if so get its major and minor numbers - */ - inode = file->f_dentry->d_inode; - if(!S_ISBLK(inode->i_mode)) { - err("%s not a block device", devname); - filp_close(file, NULL); - return NULL; - } - kdev = inode->i_rdev; - filp_close(file, NULL); +#ifdef MODULE + mode = (readonly) ? O_RDONLY : O_RDWR; + bdev = open_bdev_excl(devname, mode, NULL); #else - kdev = name_to_kdev_t(devname); -#endif /* MODULE */ - - if(!kdev) { - err("bad block device: `%s'", devname); + mode = (readonly) ? FMODE_READ : FMODE_WRITE; + bdev = open_by_devnum(name_to_dev_t(devname), mode); +#endif + if(IS_ERR(bdev)) { + err("error: cannot open device %s", devname); + DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); return NULL; } - maj = MAJOR(kdev); - min = MINOR(kdev); DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", - maj, min); + MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); - if(maj == MTD_BLOCK_MAJOR) { + if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { err("attempting to use an MTD device as a block device"); + blkdev_put(bdev); return NULL; } - DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); - dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); - if(dev == NULL) + if(dev == NULL) { + blkdev_put(bdev); return NULL; - - memset(dev, 0, sizeof(struct blkmtd_dev)); - if(alloc_kiovec(1, &dev->rd_buf)) { - err("cant allocate read iobuf"); - goto devinit_err; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); - if(dev->rd_buf->blocks == NULL) { - crit("cant allocate rd_buf blocks"); - goto devinit_err; - } -#endif + memset(dev, 0, sizeof(struct blkmtd_dev)); + dev->blkdev = bdev; + atomic_set(&(dev->blkdev->bd_inode->i_mapping->truncate_count), 0); if(!readonly) { - if(alloc_kiovec(1, &dev->wr_buf)) { - err("cant allocate kiobuf - readonly enabled"); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) - } else { - dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); - if(dev->wr_buf->blocks == NULL) { - crit("cant allocate wr_buf blocks - readonly enabled"); - free_kiovec(1, &iobuf); - } -#endif - } - if(dev->wr_buf) init_MUTEX(&dev->wrbuf_mutex); } - /* get the block device */ - dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); - if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) - goto devinit_err; - - if(set_blocksize(kdev, PAGE_SIZE)) { - err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); - goto devinit_err; - } - - dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; + dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; /* Setup the MTD structure */ /* make the name contain the block device in */ @@ -904,27 +690,26 @@ } else { dev->mtd_info.type = MTD_RAM; dev->mtd_info.flags = MTD_CAP_RAM; - } dev->mtd_info.erase = blkmtd_erase; - dev->mtd_info.read = blkmtd_read; dev->mtd_info.write = blkmtd_write; + dev->mtd_info.writev = default_mtd_writev; dev->mtd_info.sync = blkmtd_sync; - dev->mtd_info.point = 0; - dev->mtd_info.unpoint = 0; + } + dev->mtd_info.read = blkmtd_read; + dev->mtd_info.readv = default_mtd_readv; dev->mtd_info.priv = dev; - dev->mtd_info.module = THIS_MODULE; + dev->mtd_info.owner = THIS_MODULE; list_add(&dev->list, &blkmtd_device_list); if (add_mtd_device(&dev->mtd_info)) { /* Device didnt get added, so free the entry */ list_del(&dev->list); - free_device(dev); - return NULL; + goto devinit_err; } else { info("mtd%d: [%s] erase_size = %dKiB %s", dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), dev->mtd_info.erasesize >> 10, - (dev->wr_buf) ? "" : "(read-only)"); + readonly ? "(read-only)" : ""); } return dev; @@ -939,17 +724,16 @@ static void __devexit cleanup_blkmtd(void) { struct list_head *temp1, *temp2; -#ifdef BLKMTD_PROC_DEBUG - if(blkmtd_proc) { - remove_proc_entry("blkmtd_debug", NULL); - } -#endif /* Remove the MTD devices */ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, list); blkmtd_sync(&dev->mtd_info); + del_mtd_device(&dev->mtd_info); + info("mtd%d: [%s] removed", dev->mtd_info.index, + dev->mtd_info.name + strlen("blkmtd: ")); + list_del(&dev->list); free_device(dev); } } @@ -1020,6 +804,7 @@ { int i; + info("version " VERSION); /* Check args - device[0] is the bare minimum*/ if(!device[0]) { err("error: missing `device' name\n"); @@ -1030,28 +815,9 @@ add_device(device[i], ro[i], erasesz[i] << 10); if(list_empty(&blkmtd_device_list)) - goto init_err; - - info("version " VERSION); - -#ifdef BLKMTD_PROC_DEBUG - /* create proc entry */ - DEBUG(2, "Creating /proc/blkmtd_debug\n"); - blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, - NULL, blkmtd_proc_read, NULL); - if(blkmtd_proc == NULL) { - err("Cant create /proc/blkmtd_debug"); - } else { - blkmtd_proc->owner = THIS_MODULE; - } -#endif + return -EINVAL; - if(!list_empty(&blkmtd_device_list)) - /* Everything is ok if we got here */ return 0; - - init_err: - return -EINVAL; } module_init(init_blkmtd); --- /dev/null +++ linux-2.4.21/drivers/mtd/devices/block2mtd.c @@ -0,0 +1,494 @@ +/* + * $Id: block2mtd.c,v 1.25 2005/03/07 20:29:05 joern Exp $ + * + * block2mtd.c - create an mtd from a block device + * + * Copyright (C) 2001,2002 Simon Evans + * Copyright (C) 2004,2005 Jörn Engel + * + * Licence: GPL + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "$Revision: 1.25 $" + + +#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) +#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) + + +/* Info for the block device */ +struct block2mtd_dev { + struct list_head list; + struct block_device *blkdev; + struct mtd_info mtd; + struct semaphore write_mutex; +}; + + +/* Static info about the MTD, used in cleanup_module */ +static LIST_HEAD(blkmtd_device_list); + + +#define PAGE_READAHEAD 64 +void cache_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + int i, pagei; + unsigned ret = 0; + unsigned long end_index; + struct page *page; + LIST_HEAD(page_pool); + struct inode *inode = mapping->host; + loff_t isize = i_size_read(inode); + + if (!isize) { + printk(KERN_INFO "iSize=0 in cache_readahead\n"); + return; + } + + end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); + + spin_lock_irq(&mapping->tree_lock); + for (i = 0; i < PAGE_READAHEAD; i++) { + pagei = index + i; + if (pagei > end_index) { + printk(KERN_INFO "Overrun end of disk in cache readahead\n"); + break; + } + page = radix_tree_lookup(&mapping->page_tree, pagei); + if (page && (!i)) + break; + if (page) + continue; + spin_unlock_irq(&mapping->tree_lock); + page = page_cache_alloc_cold(mapping); + spin_lock_irq(&mapping->tree_lock); + if (!page) + break; + page->index = pagei; + list_add(&page->lru, &page_pool); + ret++; + } + spin_unlock_irq(&mapping->tree_lock); + if (ret) + read_cache_pages(mapping, &page_pool, filler, NULL); +} + + +static struct page* page_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + cache_readahead(mapping, index); + return read_cache_page(mapping, index, filler, NULL); +} + + +/* erase a specified part of the device */ +static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) +{ + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + struct page *page; + int index = to >> PAGE_SHIFT; // page index + int pages = len >> PAGE_SHIFT; + u_long *p; + u_long *max; + + while (pages) { + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + max = (u_long*)page_address(page) + PAGE_SIZE; + for (p=(u_long*)page_address(page); ppriv; + size_t from = instr->addr; + size_t len = instr->len; + int err; + + instr->state = MTD_ERASING; + down(&dev->write_mutex); + err = _block2mtd_erase(dev, from, len); + up(&dev->write_mutex); + if (err) { + ERROR("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else + instr->state = MTD_ERASE_DONE; + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return err; +} + + +static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct block2mtd_dev *dev = mtd->priv; + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE-1); + int cpylen; + + if (from > mtd->size) + return -EINVAL; + if (from + len > mtd->size) + len = mtd->size - from; + + if (retlen) + *retlen = 0; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + memcpy(buf, page_address(page) + offset, cpylen); + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + + +/* write data to the underlying device */ +static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, + loff_t to, size_t len, size_t *retlen) +{ + struct page *page; + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + int index = to >> PAGE_SHIFT; // page index + int offset = to & ~PAGE_MASK; // page offset + int cpylen; + + if (retlen) + *retlen = 0; + while (len) { + if ((offset+len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + if (memcmp(page_address(page)+offset, buf, cpylen)) { + lock_page(page); + memcpy(page_address(page) + offset, buf, cpylen); + set_page_dirty(page); + unlock_page(page); + } + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + + buf += cpylen; + offset = 0; + index++; + } + return 0; +} +static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct block2mtd_dev *dev = mtd->priv; + int err; + + if (!len) + return 0; + if (to >= mtd->size) + return -ENOSPC; + if (to + len > mtd->size) + len = mtd->size - to; + + down(&dev->write_mutex); + err = _block2mtd_write(dev, buf, to, len, retlen); + up(&dev->write_mutex); + if (err > 0) + err = 0; + return err; +} + + +/* sync the device - wait until the write queue is empty */ +static void block2mtd_sync(struct mtd_info *mtd) +{ + struct block2mtd_dev *dev = mtd->priv; + sync_blockdev(dev->blkdev); + return; +} + + +static void block2mtd_free_device(struct block2mtd_dev *dev) +{ + if (!dev) + return; + + kfree(dev->mtd.name); + + if (dev->blkdev) { + invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); + close_bdev_excl(dev->blkdev); + } + + kfree(dev); +} + + +/* FIXME: ensure that mtd->size % erase_size == 0 */ +static struct block2mtd_dev *add_device(char *devname, int erase_size) +{ + struct block_device *bdev; + struct block2mtd_dev *dev; + + if (!devname) + return NULL; + + dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + /* Get a handle on the device */ + bdev = open_bdev_excl(devname, O_RDWR, NULL); + if (IS_ERR(bdev)) { + ERROR("error: cannot open device %s", devname); + goto devinit_err; + } + dev->blkdev = bdev; + + if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + ERROR("attempting to use an MTD device as a block device"); + goto devinit_err; + } + + atomic_set(&bdev->bd_inode->i_mapping->truncate_count, 0); + init_MUTEX(&dev->write_mutex); + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), + GFP_KERNEL); + if (!dev->mtd.name) + goto devinit_err; + + sprintf(dev->mtd.name, "block2mtd: %s", devname); + + dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; + dev->mtd.erasesize = erase_size; + dev->mtd.type = MTD_RAM; + dev->mtd.flags = MTD_CAP_RAM; + dev->mtd.erase = block2mtd_erase; + dev->mtd.write = block2mtd_write; + dev->mtd.writev = default_mtd_writev; + dev->mtd.sync = block2mtd_sync; + dev->mtd.read = block2mtd_read; + dev->mtd.readv = default_mtd_readv; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; + + if (add_mtd_device(&dev->mtd)) { + /* Device didnt get added, so free the entry */ + goto devinit_err; + } + list_add(&dev->list, &blkmtd_device_list); + INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); + return dev; + +devinit_err: + block2mtd_free_device(dev); + return NULL; +} + + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ + unsigned long result = simple_strtoul(cp, endp, base); + switch (**endp) { + case 'G' : + result *= 1024; + case 'M': + result *= 1024; + case 'k': + result *= 1024; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return result; +} + + +static int parse_num32(u32 *num32, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num32 = n; + return 0; +} + + +static int parse_name(char **pname, const char *token, size_t limit) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > limit) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, token); + + *pname = name; + return 0; +} + + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + +#define parse_err(fmt, args...) do { \ + ERROR("block2mtd: " fmt "\n", ## args); \ + return 0; \ +} while (0) + +static int block2mtd_setup(const char *val, struct kernel_param *kp) +{ + char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ + char *token[2]; + char *name; + size_t erase_size = PAGE_SIZE; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long"); + + strcpy(str, val); + kill_final_newline(str); + + for (i=0; i<2; i++) + token[i] = strsep(&str, ","); + + if (str) + parse_err("too many arguments"); + + if (!token[0]) + parse_err("no argument"); + + ret = parse_name(&name, token[0], 80); + if (ret == -ENOMEM) + parse_err("out of memory"); + if (ret == -ENOSPC) + parse_err("name too long"); + if (ret) + return 0; + + if (token[1]) { + ret = parse_num32(&erase_size, token[1]); + if (ret) + parse_err("illegal erase size"); + } + + add_device(name, erase_size); + + return 0; +} + + +module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); +MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); + +static int __init block2mtd_init(void) +{ + INFO("version " VERSION); + return 0; +} + + +static void __devexit block2mtd_exit(void) +{ + struct list_head *pos, *next; + + /* Remove the MTD devices */ + list_for_each_safe(pos, next, &blkmtd_device_list) { + struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); + block2mtd_sync(&dev->mtd); + del_mtd_device(&dev->mtd); + INFO("mtd%d: [%s] removed", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: ")); + list_del(&dev->list); + block2mtd_free_device(dev); + } +} + + +module_init(block2mtd_init); +module_exit(block2mtd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans and others"); +MODULE_DESCRIPTION("Emulate an MTD using a block device"); --- linux-2.4.21/drivers/mtd/devices/doc2000.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/doc2000.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2000.c,v 1.50 2002/12/10 15:05:42 gleixner Exp $ + * $Id: doc2000.c,v 1.66 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include #define DOC_SUPPORT_2000 +#define DOC_SUPPORT_2000TSOP #define DOC_SUPPORT_MILLENNIUM #ifdef DOC_SUPPORT_2000 @@ -33,7 +35,7 @@ #define DoC_is_2000(doc) (0) #endif -#ifdef DOC_SUPPORT_MILLENNIUM +#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) #else #define DoC_is_Millennium(doc) (0) @@ -53,9 +55,12 @@ 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, int oobsel); + size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); 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 oobsel); + size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); +static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen, + u_char *eccbuf, struct nand_oobinfo *oobsel); 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, @@ -84,7 +89,7 @@ /* 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; + void __iomem *docptr = doc->virtadr; unsigned long timeo = jiffies + (HZ * 10); DEBUG(MTD_DEBUG_LEVEL3, @@ -92,6 +97,10 @@ /* Out-of-line routine to wait for chip response */ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + /* issue 2 read from NOP register after reading from CDSNControl register + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 2); + if (time_after(jiffies, timeo)) { DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); return -EIO; @@ -105,7 +114,8 @@ static inline int DoC_WaitReady(struct DiskOnChip *doc) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; + /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -131,7 +141,7 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, unsigned char xtraflags) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; if (DoC_is_2000(doc)) xtraflags |= CDSN_CTRL_FLASH_IO; @@ -145,6 +155,8 @@ /* Send the command */ WriteDOC_(command, docptr, doc->ioreg); + if (DoC_is_Millennium(doc)) + WriteDOC(command, docptr, WritePipeTerm); /* Lower the CLE line */ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); @@ -161,10 +173,8 @@ 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; + void __iomem *docptr = doc->virtadr; if (DoC_is_2000(doc)) xtraflags1 |= CDSN_CTRL_FLASH_IO; @@ -206,6 +216,9 @@ } } + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, WritePipeTerm); + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ /* FIXME: The SlowIO's for millennium could be replaced by @@ -226,11 +239,9 @@ { volatile int dummy; int modulus = 0xffff; - unsigned long docptr; + void __iomem *docptr = doc->virtadr; int i; - docptr = doc->virtadr; - if (len <= 0) return; @@ -257,11 +268,9 @@ /* 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; + void __iomem *docptr = doc->virtadr; int i; - docptr = doc->virtadr; - if (len <= 0) return; @@ -278,7 +287,7 @@ static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; /* Software requirement 11.4.4 before writing DeviceSelect */ /* Deassert the CE line to eliminate glitches on the FCE# outputs */ @@ -302,7 +311,7 @@ static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) { - unsigned long docptr = doc->virtadr; + void __iomem *docptr = doc->virtadr; /* Select the floor (bank) of chips required */ WriteDOC(floor, docptr, FloorSelect); @@ -344,15 +353,25 @@ /* Read the manufacturer and device id codes from the device */ - /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ + if (DoC_is_Millennium(doc)) { + DoC_Delay(doc, 2); + dummy = ReadDOC(doc->virtadr, ReadPipeInit); + mfr = ReadDOC(doc->virtadr, LastDataRead); + + DoC_Delay(doc, 2); + dummy = ReadDOC(doc->virtadr, ReadPipeInit); + id = ReadDOC(doc->virtadr, LastDataRead); + } else { + /* CDSN Slow IO register see Software Req 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. */ + /* CDSN Slow IO register see Software Req 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) @@ -387,10 +406,9 @@ 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].chipshift > 25 ? 3 : 2; + ffs((nand_flash_ids[i].chipsize << 20)) - 1; + doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0; + doc->pageadrlen = doc->chipshift > 25 ? 3 : 2; doc->erasesize = nand_flash_ids[i].erasesize; return 1; @@ -410,20 +428,16 @@ /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ -static void DoC_ScanChips(struct DiskOnChip *this) +static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) { 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; @@ -513,39 +527,54 @@ */ static void DoC2k_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; + int maxchips; /* We must avoid being called twice for the same device. */ if (doc2klist) - old = (struct DiskOnChip *) doc2klist->priv; + old = 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); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *) old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } switch (this->ChipID) { + case DOC_ChipID_Doc2kTSOP: + mtd->name = "DiskOnChip 2000 TSOP"; + this->ioreg = DoC_Mil_CDSN_IO; + /* Pretend it's a Millennium */ + this->ChipID = DOC_ChipID_DocMil; + maxchips = MAX_CHIPS; + break; case DOC_ChipID_Doc2k: mtd->name = "DiskOnChip 2000"; this->ioreg = DoC_2k_CDSN_IO; + maxchips = MAX_CHIPS; break; case DOC_ChipID_DocMil: mtd->name = "DiskOnChip Millennium"; this->ioreg = DoC_Mil_CDSN_IO; + maxchips = MAX_CHIPS_MIL; break; + default: + printk("Unknown ChipID 0x%02x\n", this->ChipID); + kfree(mtd); + iounmap(this->virtadr); + return; } printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, @@ -553,11 +582,12 @@ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; + mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; mtd->oobblock = 512; mtd->oobsize = 16; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->erase = doc_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -565,6 +595,7 @@ mtd->write = doc_write; mtd->read_ecc = doc_read_ecc; mtd->write_ecc = doc_write_ecc; + mtd->writev_ecc = doc_writev_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; @@ -577,11 +608,11 @@ init_MUTEX(&this->lock); /* Ident all the chips present. */ - DoC_ScanChips(this); + DoC_ScanChips(this, maxchips); if (!this->totlen) { kfree(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = doc2klist; doc2klist = mtd; @@ -596,20 +627,19 @@ size_t * retlen, u_char * buf) { /* Just a special case of doc_read_ecc */ - return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 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 oobsel) + size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; - unsigned long docptr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip; unsigned char syndrome[6]; volatile char dummy; int i, len256 = 0, ret=0; - - docptr = this->virtadr; + size_t left = len; /* Don't allow read past end of device */ if (from >= this->totlen) @@ -617,6 +647,10 @@ down(&this->lock); + *retlen = 0; + while (left) { + len = left; + /* Don't allow a single read to cross a 512-byte block boundary */ if (from + len > ((from | 0x1ff) + 1)) len = ((from | 0x1ff) + 1) - from; @@ -673,7 +707,7 @@ DoC_ReadBuf(this, &buf[len256], len - len256); /* Let the caller know we completed it */ - *retlen = len; + *retlen += len; if (eccbuf) { /* Read the ECC data through the DiskOnChip ECC logic */ @@ -730,11 +764,16 @@ /* 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)) + if(0 == ((from + len) & 0x1ff)) { DoC_WaitReady(this); } + from += len; + left -= len; + buf += len; + } + up(&this->lock); return ret; @@ -744,21 +783,21 @@ size_t * retlen, const u_char * buf) { char eccbuf[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); } 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 oobsel) + u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ - unsigned long docptr; + void __iomem *docptr = this->virtadr; volatile char dummy; int len256 = 0; struct Nand *mychip; - - docptr = this->virtadr; + size_t left = len; + int status; /* Don't allow write past end of device */ if (to >= this->totlen) @@ -766,15 +805,21 @@ down(&this->lock); + *retlen = 0; + while (left) { + len = left; + /* 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 */ +/* DBB- if (len != 0x200 && eccbuf) printk(KERN_WARNING "ECC needs a full sector write (adr: %lx size %lx)\n", (long) to, (long) len); + -DBB */ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ @@ -853,6 +898,9 @@ WriteDOC_(0, docptr, this->ioreg); } + WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, + CDSNControl); + /* Read the ECC data through the DiskOnChip ECC logic */ for (di = 0; di < 6; di++) { eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); @@ -874,10 +922,16 @@ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); /* There's an implicit DoC_WaitReady() in DoC_Command */ + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming flash\n"); /* Error in programming */ *retlen = 0; @@ -886,7 +940,7 @@ } /* Let the caller know we completed it */ - *retlen = len; + *retlen += len; if (eccbuf) { unsigned char x[8]; @@ -901,25 +955,90 @@ x[7]=0x55; ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); + if (ret) { up(&this->lock); return ret; } + } + + to += len; + left -= len; + buf += len; + } + up(&this->lock); return 0; } +static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen, + u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + static char static_buf[512]; + static DECLARE_MUTEX(writev_buf_sem); + + size_t totretlen = 0; + size_t thisvecofs = 0; + int ret= 0; + + down(&writev_buf_sem); + + while(count) { + size_t thislen, thisretlen; + unsigned char *buf; + + buf = vecs->iov_base + thisvecofs; + thislen = vecs->iov_len - thisvecofs; + + + if (thislen >= 512) { + thislen = thislen & ~(512-1); + thisvecofs += thislen; + } else { + /* Not enough to fill a page. Copy into buf */ + memcpy(static_buf, buf, thislen); + buf = &static_buf[thislen]; + + while(count && thislen < 512) { + vecs++; + count--; + thisvecofs = min((512-thislen), vecs->iov_len); + memcpy(buf, vecs->iov_base, thisvecofs); + thislen += thisvecofs; + buf += thisvecofs; + } + buf = static_buf; + } + if (count && thisvecofs == vecs->iov_len) { + thisvecofs = 0; + vecs++; + count--; + } + ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); + + totretlen += thisretlen; + + if (ret || thisretlen != thislen) + break; + + to += thislen; + } + + up(&writev_buf_sem); + *retlen = totretlen; + return ret; +} + + 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; + struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; - unsigned long docptr; struct Nand *mychip; down(&this->lock); - docptr = this->virtadr; - mychip = &this->chips[ofs >> this->chipshift]; if (this->curfloor != mychip->floor) { @@ -972,11 +1091,12 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0; - unsigned long docptr = this->virtadr; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; volatile int dummy; + int status; // 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]); @@ -1025,10 +1145,16 @@ DoC_Command(this, NAND_CMD_STATUS, 0); /* DoC_WaitReady() is implicit in DoC_Command */ + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming oob data\n"); /* There was an error */ *retlen = 0; @@ -1044,10 +1170,16 @@ DoC_Command(this, NAND_CMD_STATUS, 0); /* DoC_WaitReady() is implicit in DoC_Command */ + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming oob data\n"); /* There was an error */ *retlen = 0; @@ -1062,7 +1194,7 @@ 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; + struct DiskOnChip *this = mtd->priv; int ret; down(&this->lock); @@ -1074,12 +1206,13 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; volatile int dummy; - unsigned long docptr; + void __iomem *docptr = this->virtadr; struct Nand *mychip; + int status; down(&this->lock); @@ -1090,8 +1223,6 @@ instr->state = MTD_ERASING; - docptr = this->virtadr; - /* FIXME: Do this in the background. Use timers or schedule_task() */ while(len) { mychip = &this->chips[ofs >> this->chipshift]; @@ -1111,10 +1242,16 @@ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { dummy = ReadDOC(docptr, CDSNSlowIO); DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error erasing at 0x%x\n", ofs); /* There was an error */ instr->state = MTD_ERASE_FAILED; @@ -1126,8 +1263,7 @@ instr->state = MTD_ERASE_DONE; callback: - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); up(&this->lock); return 0; @@ -1140,7 +1276,7 @@ * ****************************************************************************/ -int __init init_doc2000(void) +static int __init init_doc2000(void) { inter_module_register(im_name, THIS_MODULE, &DoC2k_init); return 0; @@ -1152,12 +1288,12 @@ struct DiskOnChip *this; while ((mtd = doc2klist)) { - this = (struct DiskOnChip *) mtd->priv; + this = mtd->priv; doc2klist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *) this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } --- linux-2.4.21/drivers/mtd/devices/doc2001.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/doc2001.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001.c,v 1.38 2002/12/10 15:05:42 gleixner Exp $ + * $Id: doc2001.c,v 1.48 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -37,9 +38,11 @@ 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, int oobsel); + size_t *retlen, u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); 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 oobsel); + size_t *retlen, const u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); 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, @@ -49,7 +52,7 @@ 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) +static void DoC_Delay(void __iomem * docptr, unsigned short cycles) { volatile char dummy; int i; @@ -59,7 +62,7 @@ } /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ -static int _DoC_WaitReady(unsigned long docptr) +static int _DoC_WaitReady(void __iomem * docptr) { unsigned short c = 0xffff; @@ -76,7 +79,7 @@ return (c == 0); } -static inline int DoC_WaitReady(unsigned long docptr) +static inline int DoC_WaitReady(void __iomem * docptr) { /* This is inline, to optimise the common case, where it's ready instantly */ int ret = 0; @@ -100,7 +103,7 @@ 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, +static inline void DoC_Command(void __iomem * docptr, unsigned char command, unsigned char xtraflags) { /* Assert the CLE (Command Latch Enable) line to the flash chip */ @@ -120,7 +123,7 @@ 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, +static inline void DoC_Address(void __iomem * docptr, int numbytes, unsigned long ofs, unsigned char xtraflags1, unsigned char xtraflags2) { /* Assert the ALE (Address Latch Enable) line to the flash chip */ @@ -158,7 +161,7 @@ } /* DoC_SelectChip: Select a given flash chip within the current floor */ -static int DoC_SelectChip(unsigned long docptr, int chip) +static int DoC_SelectChip(void __iomem * docptr, int chip) { /* Select the individual flash chip requested */ WriteDOC(chip, docptr, CDSNDeviceSelect); @@ -169,7 +172,7 @@ } /* DoC_SelectFloor: Select a given floor (bank of flash chips) */ -static int DoC_SelectFloor(unsigned long docptr, int floor) +static int DoC_SelectFloor(void __iomem * docptr, int floor) { /* Select the floor (bank) of chips required */ WriteDOC(floor, docptr, FloorSelect); @@ -226,7 +229,7 @@ mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name); doc->mfr = mfr; doc->id = id; - doc->chipshift = nand_flash_ids[i].chipshift; + doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; break; } } @@ -332,23 +335,23 @@ */ static void DoCMil_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmillist) - old = (struct DiskOnChip *)docmillist->priv; + old = 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); + iounmap(this->virtadr); kfree(mtd); return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -359,14 +362,15 @@ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; + mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; - /* FIXME: erase size is not always 8kB */ + /* FIXME: erase size is not always 8KiB */ mtd->erasesize = 0x2000; mtd->oobblock = 512; mtd->oobsize = 16; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->erase = doc_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -388,7 +392,7 @@ if (!this->totlen) { kfree(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); } else { this->nextdoc = docmillist; docmillist = mtd; @@ -402,17 +406,18 @@ size_t *retlen, u_char *buf) { /* Just a special case of doc_read_ecc */ - return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 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 oobsel) + size_t *retlen, u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel) { int i, ret; volatile char dummy; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; /* Don't allow read past end of device */ @@ -528,16 +533,17 @@ size_t *retlen, const u_char *buf) { char eccbuf[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); } 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 oobsel) + size_t *retlen, const u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel) { int i,ret = 0; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; /* Don't allow write past end of device */ @@ -671,8 +677,8 @@ int i; #endif volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; /* Find the chip which is to be used and select it */ @@ -723,8 +729,8 @@ #endif volatile char dummy; int ret = 0; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - unsigned long docptr = this->virtadr; + struct DiskOnChip *this = mtd->priv; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; /* Find the chip which is to be used and select it */ @@ -790,10 +796,10 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; - unsigned long docptr = this->virtadr; + void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; if (len != mtd->erasesize) @@ -839,8 +845,7 @@ instr->state = MTD_ERASE_DONE; dummy = ReadDOC(docptr, LastDataRead); - if (instr->callback) - instr->callback(instr); + mtd_erase_callback(instr); return 0; } @@ -851,7 +856,7 @@ * ****************************************************************************/ -int __init init_doc2001(void) +static int __init init_doc2001(void) { inter_module_register(im_name, THIS_MODULE, &DoCMil_init); return 0; @@ -863,12 +868,12 @@ struct DiskOnChip *this; while ((mtd=docmillist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmillist = this->nextdoc; del_mtd_device(mtd); - iounmap((void *)this->virtadr); + iounmap(this->virtadr); kfree(this->chips); kfree(mtd); } --- /dev/null +++ linux-2.4.21/drivers/mtd/devices/doc2001plus.c @@ -0,0 +1,1154 @@ +/* + * Linux driver for Disk-On-Chip Millennium Plus + * + * (c) 2002-2003 Greg Ungerer + * (c) 2002-2003 SnapGear Inc + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * + * $Id: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 Exp $ + * + * Released under GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* #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, + struct nand_oobinfo *oobsel); +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 nand_oobinfo *oobsel); +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 *docmilpluslist = NULL; + + +/* Perform the required delay cycles by writing to the NOP register */ +static void DoC_Delay(void __iomem * docptr, int cycles) +{ + int i; + + for (i = 0; (i < cycles); i++) + WriteDOC(0, docptr, Mplus_NOP); +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(void __iomem * docptr) +{ + unsigned int 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, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) + ; + + if (c == 0) + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + + return (c == 0); +} + +static inline int DoC_WaitReady(void __iomem * docptr) +{ + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; + + /* read form NOP register should be issued prior to the read from CDSNControl + see Software Requirement 11.4 item 2. */ + DoC_Delay(docptr, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(docptr); + + return ret; +} + +/* For some reason the Millennium Plus seems to occassionally put itself + * into reset mode. For me this happens randomly, with no pattern that I + * can detect. M-systems suggest always check this on any block level + * operation and setting to normal mode if in reset mode. + */ +static inline void DoC_CheckASIC(void __iomem * docptr) +{ + /* Make sure the DoC is in normal mode */ + if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { + WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); + WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); + } +} + +/* DoC_Command: Send a flash command to the flash chip through the Flash + * command register. Need 2 Write Pipeline Terminates to complete send. + */ +static inline void DoC_Command(void __iomem * docptr, unsigned char command, + unsigned char xtraflags) +{ + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(command, docptr, Mplus_WritePipeTerm); + WriteDOC(command, docptr, Mplus_WritePipeTerm); +} + +/* DoC_Address: Set the current address for the flash chip through the Flash + * Address register. Need 2 Write Pipeline Terminates to complete send. + */ +static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, + unsigned long ofs, unsigned char xtraflags1, + unsigned char xtraflags2) +{ + void __iomem * docptr = doc->virtadr; + + /* Allow for possible Mill Plus internal flash interleaving */ + ofs >>= doc->interleave; + + switch (numbytes) { + case 1: + /* Send single byte, bits 0-7. */ + WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); + break; + case 2: + /* Send bits 9-16 followed by 17-23 */ + WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); + break; + case 3: + /* Send 0-7, 9-16, then 17-23 */ + WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); + break; + default: + return; + } + + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); +} + +/* DoC_SelectChip: Select a given flash chip within the current floor */ +static int DoC_SelectChip(void __iomem * docptr, int chip) +{ + /* No choice for flash chip on Millennium Plus */ + return 0; +} + +/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ +static int DoC_SelectFloor(void __iomem * docptr, int floor) +{ + WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); + return 0; +} + +/* + * Translate the given offset into the appropriate command and offset. + * This does the mapping using the 16bit interleave layout defined by + * M-Systems, and looks like this for a sector pair: + * +-----------+-------+-------+-------+--------------+---------+-----------+ + * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| + * +-----------+-------+-------+-------+--------------+---------+-----------+ + * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | + * +-----------+-------+-------+-------+--------------+---------+-----------+ + */ +/* FIXME: This lives in INFTL not here. Other users of flash devices + may not want it */ +static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) +{ + struct DiskOnChip *this = mtd->priv; + + if (this->interleave) { + unsigned int ofs = *from & 0x3ff; + unsigned int cmd; + + if (ofs < 512) { + cmd = NAND_CMD_READ0; + ofs &= 0x1ff; + } else if (ofs < 1014) { + cmd = NAND_CMD_READ1; + ofs = (ofs & 0x1ff) + 10; + } else { + cmd = NAND_CMD_READOOB; + ofs = ofs - 1014; + } + + *from = (*from & ~0x3ff) | ofs; + return cmd; + } else { + /* No interleave */ + if ((*from) & 0x100) + return NAND_CMD_READ1; + return NAND_CMD_READ0; + } +} + +static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) +{ + unsigned int ofs, cmd; + + if (*from & 0x200) { + cmd = NAND_CMD_READOOB; + ofs = 10 + (*from & 0xf); + } else { + cmd = NAND_CMD_READ1; + ofs = (*from & 0xf); + } + + *from = (*from & ~0x3ff) | ofs; + return cmd; +} + +static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) +{ + unsigned int ofs, cmd; + + cmd = NAND_CMD_READ1; + ofs = (*from & 0x200) ? 8 : 6; + *from = (*from & ~0x3ff) | ofs; + return cmd; +} + +static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) +{ + unsigned int ofs, cmd; + + cmd = NAND_CMD_READOOB; + ofs = (*from & 0x200) ? 24 : 16; + *from = (*from & ~0x3ff) | ofs; + return cmd; +} + +static inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) +{ +#ifndef USE_MEMCPY + int i; + for (i = 0; i < len; i++) + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); +#else + memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); +#endif +} + +static inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) +{ +#ifndef USE_MEMCPY + int i; + for (i = 0; i < len; i++) + WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); +#else + memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); +#endif +} + +/* 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, j; + volatile char dummy; + void __iomem * docptr = doc->virtadr; + + /* Page in the required floor/chip */ + DoC_SelectFloor(docptr, floor); + DoC_SelectChip(docptr, chip); + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); + + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(docptr, NAND_CMD_RESET, 0); + DoC_WaitReady(docptr); + + /* Read the NAND chip ID: 1. Send ReadID command */ + DoC_Command(docptr, NAND_CMD_READID, 0); + + /* Read the NAND chip ID: 2. Send address byte zero */ + DoC_Address(doc, 1, 0x00, 0, 0x00); + + WriteDOC(0, docptr, Mplus_FlashControl); + DoC_WaitReady(docptr); + + /* Read the manufacturer and device id codes of the flash device through + CDSN IO register see Software Requirement 11.4 item 5.*/ + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + + mfr = ReadDOC(docptr, Mil_CDSN_IO); + if (doc->interleave) + dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ + + id = ReadDOC(docptr, Mil_CDSN_IO); + if (doc->interleave) + dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ + + dummy = ReadDOC(docptr, Mplus_LastDataRead); + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) + return 0; + + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (id == nand_flash_ids[i].id) { + /* Try to identify manufacturer */ + for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { + if (nand_manuf_ids[j].id == mfr) + break; + } + printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " + "Chip ID: %2.2X (%s:%s)\n", mfr, id, + nand_manuf_ids[j].name, nand_flash_ids[i].name); + doc->mfr = mfr; + doc->id = id; + doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; + doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; + break; + } + } + + if (nand_flash_ids[i].name == NULL) + return 0; + 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_MPLUS]; + int ret; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + + /* Work out the intended interleave setting */ + this->interleave = 0; + if (this->ChipID == DOC_ChipID_DocMilPlus32) + this->interleave = 1; + + /* Check the ASIC agrees */ + if ( (this->interleave << 2) != + (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { + u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); + printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", + this->interleave?"on (16-bit)":"off (8-bit)"); + conf ^= 4; + WriteDOC(conf, this->virtadr, Mplus_Configuration); + } + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { + numchips[floor] = 0; + for (chip = 0; chip < MAX_CHIPS_MPLUS && 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("MTD: 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_MPLUS; 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 DoCMilPlus_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, Mplus_AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); + if (tmp1 != tmp2) + return 0; + + WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, Mplus_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, Mplus_AliasResolution); + + return retval; +} + +static const char im_name[] = "DoCMilPlus_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 29 Oct 2000. + */ +static void DoCMilPlus_init(struct mtd_info *mtd) +{ + struct DiskOnChip *this = mtd->priv; + struct DiskOnChip *old = NULL; + + /* We must avoid being called twice for the same device. */ + if (docmilpluslist) + old = docmilpluslist->priv; + + while (old) { + if (DoCMilPlus_is_alias(this, old)) { + printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " + "Plus at 0x%lX - already configured\n", + this->physadr); + iounmap(this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) + old = old->nextdoc->priv; + else + old = NULL; + } + + mtd->name = "DiskOnChip Millennium Plus"; + printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " + "address 0x%lX\n", this->physadr); + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + + mtd->erasesize = 0; + mtd->oobblock = 512; + mtd->oobsize = 16; + mtd->owner = 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(this->virtadr); + } else { + this->nextdoc = docmilpluslist; + docmilpluslist = mtd; + mtd->size = this->totlen; + mtd->erasesize = this->erasesize; + add_mtd_device(mtd); + return; + } +} + +#if 0 +static int doc_dumpblk(struct mtd_info *mtd, loff_t from) +{ + int i; + loff_t fofs; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[from >> (this->chipshift)]; + unsigned char *bp, buf[1056]; + char c[32]; + + from &= ~0x3ff; + + /* Don't allow read past end of device */ + if (from >= this->totlen) + return -EINVAL; + + DoC_CheckASIC(docptr); + + /* 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); + + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(docptr, NAND_CMD_RESET, 0); + DoC_WaitReady(docptr); + + fofs = from; + DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); + DoC_Address(this, 3, fofs, 0, 0x00); + WriteDOC(0, docptr, Mplus_FlashControl); + DoC_WaitReady(docptr); + + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + /* Read the data via the internal pipeline through CDSN IO + register, see Pipelined Read Operations 11.3 */ + MemReadDOC(docptr, buf, 1054); + buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); + buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); + + memset(&c[0], 0, sizeof(c)); + printk("DUMP OFFSET=%x:\n", (int)from); + + for (i = 0, bp = &buf[0]; (i < 1056); i++) { + if ((i % 16) == 0) + printk("%08x: ", i); + printk(" %02x", *bp); + c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; + bp++; + if (((i + 1) % 16) == 0) + printk(" %s\n", c); + } + printk("\n"); + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + return 0; +} +#endif + +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, 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 nand_oobinfo *oobsel) +{ + int ret, i; + volatile char dummy; + loff_t fofs; + unsigned char syndrome[6]; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[from >> (this->chipshift)]; + + /* Don't allow read past end of device */ + if (from >= this->totlen) + return -EINVAL; + + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; + + DoC_CheckASIC(docptr); + + /* 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); + + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(docptr, NAND_CMD_RESET, 0); + DoC_WaitReady(docptr); + + fofs = from; + DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); + DoC_Address(this, 3, fofs, 0, 0x00); + WriteDOC(0, docptr, Mplus_FlashControl); + DoC_WaitReady(docptr); + + if (eccbuf) { + /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + } + + /* Let the caller know we completed it */ + *retlen = len; + ret = 0; + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + if (eccbuf) { + /* Read the data via the internal pipeline through CDSN IO + register, see Pipelined Read Operations 11.3 */ + MemReadDOC(docptr, buf, len); + + /* Read the ECC data following raw data */ + MemReadDOC(docptr, eccbuf, 4); + eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); + eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); + + /* Flush the pipeline */ + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + + /* Check the ECC Status */ + if (ReadDOC(docptr, Mplus_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, Mplus_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 */ +#ifdef ECC_DEBUG + printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", + __FILE__, __LINE__, (int)from); + printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" + "%02x\n", + syndrome[0], syndrome[1], syndrome[2], + syndrome[3], syndrome[4], syndrome[5]); + printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" + "%02x\n", + eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5]); +#endif + 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 , Mplus_ECCConf); + } else { + /* Read the data via the internal pipeline through CDSN IO + register, see Pipelined Read Operations 11.3 */ + MemReadDOC(docptr, buf, len-2); + buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); + buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); + } + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + 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, NULL); +} + +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 nand_oobinfo *oobsel) +{ + int i, before, ret = 0; + loff_t fto; + volatile char dummy; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[to >> (this->chipshift)]; + + /* Don't allow write past end of device */ + if (to >= this->totlen) + return -EINVAL; + + /* Don't allow writes which aren't exactly one block (512 bytes) */ + if ((to & 0x1ff) || (len != 0x200)) + return -EINVAL; + + /* Determine position of OOB flags, before or after data */ + before = (this->interleave && (to & 0x200)); + + DoC_CheckASIC(docptr); + + /* 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); + + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(docptr, NAND_CMD_RESET, 0); + DoC_WaitReady(docptr); + + /* Set device to appropriate plane of flash */ + fto = to; + WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); + + /* On interleaved devices the flags for 2nd half 512 are before data */ + if (eccbuf && before) + fto -= 2; + + /* issue the Serial Data In command to initial the Page Program process */ + DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); + DoC_Address(this, 3, fto, 0x00, 0x00); + + /* Disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + + if (eccbuf) { + if (before) { + /* Write the block status BLOCK_USED (0x5555) */ + WriteDOC(0x55, docptr, Mil_CDSN_IO); + WriteDOC(0x55, docptr, Mil_CDSN_IO); + } + + /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); + } + + MemWriteDOC(docptr, (unsigned char *) buf, len); + + if (eccbuf) { + /* Write ECC data to flash, the ECC info is generated by + the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ + DoC_Delay(docptr, 3); + + /* Read the ECC data through the DiskOnChip ECC logic */ + for (i = 0; i < 6; i++) + eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); + + /* disable the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + + /* Write the ECC data to flash */ + MemWriteDOC(docptr, eccbuf, 6); + + if (!before) { + /* Write the block status BLOCK_USED (0x5555) */ + WriteDOC(0x55, docptr, Mil_CDSN_IO+6); + WriteDOC(0x55, docptr, Mil_CDSN_IO+7); + } + +#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 + } + + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_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, 0); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + DoC_Delay(docptr, 2); + if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { + printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); + /* Error in programming + FIXME: implement Bad Block Replacement (in nftl.c ??) */ + *retlen = 0; + ret = -EIO; + } + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + /* 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) +{ + loff_t fofs, base; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + size_t i, size, got, want; + + DoC_CheckASIC(docptr); + + /* 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); + + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + DoC_WaitReady(docptr); + + /* Maximum of 16 bytes in the OOB region, so limit read to that */ + if (len > 16) + len = 16; + got = 0; + want = len; + + for (i = 0; ((i < 3) && (want > 0)); i++) { + /* Figure out which region we are accessing... */ + fofs = ofs; + base = ofs & 0xf; + if (!this->interleave) { + DoC_Command(docptr, NAND_CMD_READOOB, 0); + size = 16 - base; + } else if (base < 6) { + DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); + size = 6 - base; + } else if (base < 8) { + DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); + size = 8 - base; + } else { + DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); + size = 16 - base; + } + if (size > want) + size = want; + + /* Issue read command */ + DoC_Address(this, 3, fofs, 0, 0x00); + WriteDOC(0, docptr, Mplus_FlashControl); + DoC_WaitReady(docptr); + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + MemReadDOC(docptr, &buf[got], size - 2); + buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); + buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); + + ofs += size; + got += size; + want -= size; + } + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + *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) +{ + volatile char dummy; + loff_t fofs, base; + struct DiskOnChip *this = mtd->priv; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + size_t i, size, got, want; + int ret = 0; + + DoC_CheckASIC(docptr); + + /* 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); + + + /* Maximum of 16 bytes in the OOB region, so limit write to that */ + if (len > 16) + len = 16; + got = 0; + want = len; + + for (i = 0; ((i < 3) && (want > 0)); i++) { + /* Reset the chip, see Software Requirement 11.4 item 1. */ + DoC_Command(docptr, NAND_CMD_RESET, 0); + DoC_WaitReady(docptr); + + /* Figure out which region we are accessing... */ + fofs = ofs; + base = ofs & 0x0f; + if (!this->interleave) { + WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); + size = 16 - base; + } else if (base < 6) { + WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 6 - base; + } else if (base < 8) { + WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 8 - base; + } else { + WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 16 - base; + } + if (size > want) + size = want; + + /* Issue the Serial Data In command to initial the Page Program process */ + DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); + DoC_Address(this, 3, fofs, 0, 0x00); + + /* Disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + + /* Write the data via the internal pipeline through CDSN IO + register, see Pipelined Write Operations 11.2 */ + MemWriteDOC(docptr, (unsigned char *) &buf[got], size); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_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, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + DoC_Delay(docptr, 2); + if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { + printk("MTD: Error 0x%x programming oob at 0x%x\n", + dummy, (int)ofs); + /* FIXME: implement Bad Block Replacement */ + *retlen = 0; + ret = -EIO; + } + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + ofs += size; + got += size; + want -= size; + } + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + *retlen = len; + return ret; +} + +int doc_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + volatile char dummy; + struct DiskOnChip *this = mtd->priv; + __u32 ofs = instr->addr; + __u32 len = instr->len; + void __iomem * docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + + DoC_CheckASIC(docptr); + + if (len != mtd->erasesize) + printk(KERN_WARNING "MTD: 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; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); + + DoC_Command(docptr, NAND_CMD_RESET, 0x00); + DoC_WaitReady(docptr); + + DoC_Command(docptr, NAND_CMD_ERASE1, 0); + DoC_Address(this, 2, ofs, 0, 0x00); + DoC_Command(docptr, NAND_CMD_ERASE2, 0); + 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. */ + DoC_Command(docptr, NAND_CMD_STATUS, 0); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { + printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); + /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + } + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + mtd_erase_callback(instr); + + return 0; +} + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +static int __init init_doc2001plus(void) +{ + inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); + return 0; +} + +static void __exit cleanup_doc2001plus(void) +{ + struct mtd_info *mtd; + struct DiskOnChip *this; + + while ((mtd=docmilpluslist)) { + this = mtd->priv; + docmilpluslist = this->nextdoc; + + del_mtd_device(mtd); + + iounmap(this->virtadr); + kfree(this->chips); + kfree(mtd); + } + inter_module_unregister(im_name); +} + +module_exit(cleanup_doc2001plus); +module_init(init_doc2001plus); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Ungerer et al."); +MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); --- linux-2.4.21/drivers/mtd/devices/docecc.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/docecc.c @@ -7,7 +7,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: docecc.c,v 1.5 2003/05/21 15:15:06 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 @@ -519,6 +519,8 @@ return nb_errors; } +EXPORT_SYMBOL_GPL(doc_decode_ecc); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Fabrice Bellard "); MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); --- linux-2.4.21/drivers/mtd/devices/docprobe.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/docprobe.c @@ -4,7 +4,7 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.33 2003/01/24 14:02:47 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.44 2005/01/05 12:40:36 dwmw2 Exp $ */ @@ -31,14 +31,12 @@ /* 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 :) + The old Millennium-only driver has been retained just in case there + are problems with the new code. If the combined driver doesn't work + for you, you can try the old one by undefining DOC_SINGLE_DRIVER + below and also enabling it in your configuration. If this fixes the + problems, please send a report to the MTD mailing list at + . */ #define DOC_SINGLE_DRIVER @@ -47,18 +45,15 @@ #include #include #include -#include -#include -#include #include #include -#include #include #include #include #include #include +#include /* Where to look for the devices? */ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS @@ -95,14 +90,14 @@ ##else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif - 0 }; + 0xffffffff }; /* 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) +static inline int __init doccheck(void __iomem *potential, unsigned long physadr) { - unsigned long window=potential; - unsigned char tmp, ChipID; + void __iomem *window=potential; + unsigned char tmp, tmpb, tmpc, ChipID; #ifndef DOC_PASSIVE_PROBE unsigned char tmp2; #endif @@ -140,26 +135,80 @@ window, DOCControl); #endif /* !DOC_PASSIVE_PROBE */ + /* We need to read the ChipID register four times. For some + newer DiskOnChip 2000 units, the first three reads will + return the DiskOnChip Millennium ident. Don't ask. */ 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) + tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; + tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; + if (tmp != tmpb && tmp == tmpc) return ChipID; break; case DOC_ChipID_DocMil: + /* Check for the new 2000 with Millennium ASIC */ + ReadDOC(window, ChipID); + ReadDOC(window, ChipID); + if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) + ChipID = DOC_ChipID_Doc2kTSOP; + /* Check the TOGGLE bit in the ECC register */ tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; - if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) + tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; + tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; + if (tmp != tmpb && tmp == tmpc) return ChipID; break; + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + case 0: + /* Possible Millennium+, need to do more checks */ +#ifndef DOC_PASSIVE_PROBE + /* Possibly release from power down mode */ + for (tmp = 0; (tmp < 4); tmp++) + ReadDOC(window, Mplus_Power); + + /* Reset the DiskOnChip ASIC */ + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, window, Mplus_DOCControl); + WriteDOC(~tmp, window, Mplus_CtrlConfirm); + + mdelay(1); + /* Enable the DiskOnChip ASIC */ + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, window, Mplus_DOCControl); + WriteDOC(~tmp, window, Mplus_CtrlConfirm); + mdelay(1); +#endif /* !DOC_PASSIVE_PROBE */ + + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + /* Check the TOGGLE bit in the toggle register */ + tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; + tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; + tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; + if (tmp != tmpb && tmp == tmpc) + return ChipID; default: -#ifndef CONFIG_MTD_DOCPROBE_55AA - printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + break; + } + /* FALL TRHU */ + + default: + +#ifdef CONFIG_MTD_DOCPROBE_55AA + printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ChipID, physadr); #endif #ifndef DOC_PASSIVE_PROBE @@ -184,7 +233,7 @@ static void __init DoC_Probe(unsigned long physadr) { - unsigned long docptr; + void __iomem *docptr; struct DiskOnChip *this; struct mtd_info *mtd; int ChipID; @@ -194,18 +243,24 @@ char *im_modname = NULL; void (*initroutine)(struct mtd_info *) = NULL; - docptr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN); + docptr = ioremap(physadr, DOC_IOREMAP_LEN); if (!docptr) return; if ((ChipID = doccheck(docptr, physadr))) { + if (ChipID == DOC_ChipID_Doc2kTSOP) { + /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ + printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); + iounmap(docptr); + return; + } docfound = 1; mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); if (!mtd) { printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n"); - iounmap((void *)docptr); + iounmap(docptr); return; } @@ -221,6 +276,12 @@ sprintf(namebuf, "with ChipID %2.2X", ChipID); switch(ChipID) { + case DOC_ChipID_Doc2kTSOP: + name="2000 TSOP"; + im_funcname = "DoC2k_init"; + im_modname = "doc2000"; + break; + case DOC_ChipID_Doc2k: name="2000"; im_funcname = "DoC2k_init"; @@ -237,6 +298,13 @@ im_modname = "doc2001"; #endif /* DOC_SINGLE_DRIVER */ break; + + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + name="MillenniumPlus"; + im_funcname = "DoCMilPlus_init"; + im_modname = "doc2001plus"; + break; } if (im_funcname) @@ -248,8 +316,9 @@ return; } printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); + kfree(mtd); } - iounmap((void *)docptr); + iounmap(docptr); } @@ -259,7 +328,7 @@ * ****************************************************************************/ -int __init init_doc(void) +static int __init init_doc(void) { int i; @@ -267,7 +336,7 @@ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); DoC_Probe(doc_config_location); } else { - for (i=0; doc_locations[i]; i++) { + for (i=0; (doc_locations[i] != 0xffffffff); i++) { DoC_Probe(doc_locations[i]); } } @@ -275,11 +344,7 @@ found, so the user knows we at least tried. */ if (!docfound) printk(KERN_INFO "No recognised DiskOnChip devices found\n"); - /* So it looks like we've been used and we get unloaded */ - MOD_INC_USE_COUNT; - MOD_DEC_USE_COUNT; - return 0; - + return -EAGAIN; } module_init(init_doc); --- linux-2.4.21/drivers/mtd/devices/lart.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/lart.c @@ -2,7 +2,7 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: lart.c,v 1.7 2004/08/09 13:19:44 dwmw2 Exp $ * * Author: Abraham vd Merwe * @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #ifdef HAVE_PARTITIONS @@ -433,7 +433,7 @@ } instr->state = MTD_ERASE_DONE; - if (instr->callback) instr->callback (instr); + mtd_erase_callback(instr); return (0); } @@ -584,45 +584,40 @@ static struct mtd_info mtd; -static struct mtd_erase_region_info erase_regions[] = -{ +static struct mtd_erase_region_info erase_regions[] = { /* parameter blocks */ { - offset: 0x00000000, - erasesize: FLASH_BLOCKSIZE_PARAM, - numblocks: FLASH_NUMBLOCKS_16m_PARAM + .offset = 0x00000000, + .erasesize = FLASH_BLOCKSIZE_PARAM, + .numblocks = FLASH_NUMBLOCKS_16m_PARAM, }, /* main blocks */ { - offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, - erasesize: FLASH_BLOCKSIZE_MAIN, - numblocks: FLASH_NUMBLOCKS_16m_MAIN + .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, + .erasesize = FLASH_BLOCKSIZE_MAIN, + .numblocks = FLASH_NUMBLOCKS_16m_MAIN, } }; #ifdef HAVE_PARTITIONS -static struct mtd_partition lart_partitions[] = -{ +static struct mtd_partition lart_partitions[] = { /* blob */ { - name: "blob", - offset: BLOB_START, - size: BLOB_LEN, - mask_flags: 0 + .name = "blob", + .offset = BLOB_START, + .size = BLOB_LEN, }, /* kernel */ { - name: "kernel", - offset: KERNEL_START, /* MTDPART_OFS_APPEND */ - size: KERNEL_LEN, - mask_flags: 0 + .name = "kernel", + .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ + .size = KERNEL_LEN, }, /* initial ramdisk / file system */ { - name: "file system", - offset: INITRD_START, /* MTDPART_OFS_APPEND */ - size: INITRD_LEN, /* MTDPART_SIZ_FULL */ - mask_flags: 0 + .name = "file system", + .offset = INITRD_START, /* MTDPART_OFS_APPEND */ + .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ } }; #endif @@ -646,10 +641,10 @@ mtd.erasesize = FLASH_BLOCKSIZE_MAIN; mtd.numeraseregions = NB_OF (erase_regions); mtd.eraseregions = erase_regions; - mtd.module = THIS_MODULE; mtd.erase = flash_erase; mtd.read = flash_read; mtd.write = flash_write; + mtd.owner = THIS_MODULE; #ifdef LART_DEBUG printk (KERN_DEBUG --- linux-2.4.21/drivers/mtd/devices/ms02-nv.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/ms02-nv.c @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: ms02-nv.c,v 1.2 2003/01/24 14:05:17 dwmw2 Exp $ + * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ */ #include @@ -31,16 +31,16 @@ static char version[] __initdata = "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; -MODULE_AUTHOR("Maciej W. Rozycki "); +MODULE_AUTHOR("Maciej W. Rozycki "); MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); MODULE_LICENSE("GPL"); /* * Addresses we probe for an MS02-NV at. Modules may be located - * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB - * boundary within a 0MB up to 448MB range. We don't support a module - * at 0MB, though. + * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB + * boundary within a 0MiB up to 448MiB range. We don't support a module + * at 0MiB, though. */ static ulong ms02nv_addrs[] __initdata = { 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000, @@ -59,7 +59,7 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -73,7 +73,7 @@ static int ms02nv_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -130,7 +130,7 @@ int ret = -ENODEV; - /* The module decodes 8MB of address space. */ + /* The module decodes 8MiB of address space. */ mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); if (!mod_res) return -ENOMEM; @@ -222,7 +222,7 @@ mtd->flags = MTD_CAP_RAM | MTD_XIP; mtd->size = fixsize; mtd->name = (char *)ms02nv_name; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->read = ms02nv_read; mtd->write = ms02nv_write; @@ -233,7 +233,7 @@ goto err_out_csr_res; } - printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n", + printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n", mtd->index, ms02nv_name, addr, size >> 20); mp->next = root_ms02nv_mtd; @@ -265,7 +265,7 @@ static void __exit ms02nv_remove_one(void) { struct mtd_info *mtd = root_ms02nv_mtd; - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; root_ms02nv_mtd = mp->next; @@ -293,12 +293,12 @@ switch (mips_machtype) { case MACH_DS5000_200: - csr = (volatile u32 *)KN02_CSR_ADDR; + csr = (volatile u32 *)KN02_CSR_BASE; if (*csr & KN02_CSR_BNK32M) stride = 2; break; case MACH_DS5000_2X0: - case MACH_DS5000: + case MACH_DS5900: csr = (volatile u32 *)KN03_MCR_BASE; if (*csr & KN03_MCR_BNK32M) stride = 2; --- linux-2.4.21/drivers/mtd/devices/ms02-nv.h~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/ms02-nv.h @@ -1,32 +1,96 @@ /* - * Copyright (c) 2001 Maciej W. Rozycki + * Copyright (c) 2001, 2003 Maciej W. Rozycki + * + * DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for + * DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260 + * systems. * * 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. + * + * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $ */ #include #include +/* + * Addresses are decoded as follows: + * + * 0x000000 - 0x3fffff SRAM + * 0x400000 - 0x7fffff CSR + * + * Within the SRAM area the following ranges are forced by the system + * firmware: + * + * 0x000000 - 0x0003ff diagnostic area, destroyed upon a reboot + * 0x000400 - ENDofRAM storage area, available to operating systems + * + * but we can't really use the available area right from 0x000400 as + * the first word is used by the firmware as a status flag passed + * from an operating system. If anything but the valid data magic + * ID value is found, the firmware considers the SRAM clean, i.e. + * containing no valid data, and disables the battery resulting in + * data being erased as soon as power is switched off. So the choice + * for the start address of the user-available is 0x001000 which is + * nicely page aligned. The area between 0x000404 and 0x000fff may + * be used by the driver for own needs. + * + * The diagnostic area defines two status words to be read by an + * operating system, a magic ID to distinguish a MS02-NV board from + * anything else and a status information providing results of tests + * as well as the size of SRAM available, which can be 1MiB or 2MiB + * (that's what the firmware handles; no idea if 2MiB modules ever + * existed). + * + * The firmware only handles the MS02-NV board if installed in the + * last (15th) slot, so for any other location the status information + * stored in the SRAM cannot be relied upon. But from the hardware + * point of view there is no problem using up to 14 such boards in a + * system -- only the 1st slot needs to be filled with a DRAM module. + * The MS02-NV board is ECC-protected, like other MS02 memory boards. + * + * The state of the battery as provided by the CSR is reflected on + * the two onboard LEDs. When facing the battery side of the board, + * with the LEDs at the top left and the battery at the bottom right + * (i.e. looking from the back side of the system box), their meaning + * is as follows (the system has to be powered on): + * + * left LED battery disable status: lit = enabled + * right LED battery condition status: lit = OK + */ + /* MS02-NV iomem register offsets. */ #define MS02NV_CSR 0x400000 /* control & status register */ +/* MS02-NV CSR status bits. */ +#define MS02NV_CSR_BATT_OK 0x01 /* battery OK */ +#define MS02NV_CSR_BATT_OFF 0x02 /* battery disabled */ + + /* MS02-NV memory offsets. */ #define MS02NV_DIAG 0x0003f8 /* diagnostic status */ #define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */ -#define MS02NV_RAM 0x000400 /* general-purpose RAM start */ +#define MS02NV_VALID 0x000400 /* valid data magic ID */ +#define MS02NV_RAM 0x001000 /* user-exposed RAM start */ -/* MS02-NV diagnostic status constants. */ -#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */ -#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */ +/* MS02-NV diagnostic status bits. */ +#define MS02NV_DIAG_TEST 0x01 /* SRAM test done (?) */ +#define MS02NV_DIAG_RO 0x02 /* SRAM r/o test done */ +#define MS02NV_DIAG_RW 0x04 /* SRAM r/w test done */ +#define MS02NV_DIAG_FAIL 0x08 /* SRAM test failed */ +#define MS02NV_DIAG_SIZE_MASK 0xf0 /* SRAM size mask */ +#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* SRAM size shift (left) */ /* MS02-NV general constants. */ #define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */ +#define MS02NV_VALID_ID 0xbd100248 /* valid data magic ID value */ #define MS02NV_SLOT_SIZE 0x800000 /* size of the address space decoded by the module */ + typedef volatile u32 ms02nv_uint; struct ms02nv_private { --- linux-2.4.21/drivers/mtd/devices/mtdram.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/mtdram.c @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.29 2002/10/21 13:40:06 jocke Exp $ + * $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -55,9 +57,8 @@ memset((char *)mtd->priv + instr->addr, 0xff, instr->len); instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); - if (instr->callback) - (*(instr->callback))(instr); return 0; } @@ -136,7 +137,7 @@ mtd->erasesize = MTDRAM_ERASE_SIZE; mtd->priv = mapped_address; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->erase = ram_erase; mtd->point = ram_point; mtd->unpoint = ram_unpoint; @@ -152,12 +153,12 @@ #if CONFIG_MTDRAM_TOTAL_SIZE > 0 #if CONFIG_MTDRAM_ABS_POS > 0 -int __init init_mtdram(void) +static int __init init_mtdram(void) { void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -185,12 +186,12 @@ #else /* CONFIG_MTDRAM_ABS_POS > 0 */ -int __init init_mtdram(void) +static int __init init_mtdram(void) { void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -219,7 +220,7 @@ #else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */ -int __init init_mtdram(void) +static int __init init_mtdram(void) { return 0; } --- /dev/null +++ linux-2.4.21/drivers/mtd/devices/phram.c @@ -0,0 +1,299 @@ +/** + * $Id: phram.c,v 1.14 2005/03/07 21:43:38 joern Exp $ + * + * Copyright (c) ???? Jochen Schäuble + * Copyright (c) 2003-2004 Jörn Engel + * + * Usage: + * + * one commend line parameter per device, each in the form: + * phram=,, + * may be up to 63 characters. + * and can be octal, decimal or hexadecimal. If followed + * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or + * gigabytes. + * + * Example: + * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi + */ +#include +#include +#include +#include +#include +#include +#include + +#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) + +struct phram_mtd_list { + struct mtd_info mtd; + struct list_head list; +}; + +static LIST_HEAD(phram_list); + + +static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + u_char *start = mtd->priv; + + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + memset(start + instr->addr, 0xff, instr->len); + + /* This'll catch a few races. Free the thing before returning :) + * I don't feel at all ashamed. This kind of thing is possible anyway + * with flash, but unlikely. + */ + + instr->state = MTD_ERASE_DONE; + + mtd_erase_callback(instr); + + return 0; +} + +static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf) +{ + u_char *start = mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; + + *mtdbuf = start + from; + *retlen = len; + return 0; +} + +static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len) +{ +} + +static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + u_char *start = mtd->priv; + + if (from >= mtd->size) + return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; + + memcpy(buf, start + from, len); + + *retlen = len; + return 0; +} + +static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + u_char *start = mtd->priv; + + if (to >= mtd->size) + return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; + + memcpy(start + to, buf, len); + + *retlen = len; + return 0; +} + + + +static void unregister_devices(void) +{ + struct phram_mtd_list *this, *safe; + + list_for_each_entry_safe(this, safe, &phram_list, list) { + del_mtd_device(&this->mtd); + iounmap(this->mtd.priv); + kfree(this); + } +} + +static int register_device(char *name, unsigned long start, unsigned long len) +{ + struct phram_mtd_list *new; + int ret = -ENOMEM; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) + goto out0; + + memset(new, 0, sizeof(*new)); + + ret = -EIO; + new->mtd.priv = ioremap(start, len); + if (!new->mtd.priv) { + ERROR("ioremap failed\n"); + goto out1; + } + + + new->mtd.name = name; + new->mtd.size = len; + new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; + new->mtd.erase = phram_erase; + new->mtd.point = phram_point; + new->mtd.unpoint = phram_unpoint; + new->mtd.read = phram_read; + new->mtd.write = phram_write; + new->mtd.owner = THIS_MODULE; + new->mtd.type = MTD_RAM; + new->mtd.erasesize = PAGE_SIZE; + + ret = -EAGAIN; + if (add_mtd_device(&new->mtd)) { + ERROR("Failed to register new device\n"); + goto out2; + } + + list_add_tail(&new->list, &phram_list); + return 0; + +out2: + iounmap(new->mtd.priv); +out1: + kfree(new); +out0: + return ret; +} + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ + unsigned long result = simple_strtoul(cp, endp, base); + + switch (**endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'k': + result *= 1024; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return result; +} + +static int parse_num32(uint32_t *num32, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num32 = n; + return 0; +} + +static int parse_name(char **pname, const char *token) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > 64) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, token); + + *pname = name; + return 0; +} + + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + +#define parse_err(fmt, args...) do { \ + ERROR(fmt , ## args); \ + return 0; \ +} while (0) + +static int phram_setup(const char *val, struct kernel_param *kp) +{ + char buf[64+12+12], *str = buf; + char *token[3]; + char *name; + uint32_t start; + uint32_t len; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long\n"); + + strcpy(str, val); + kill_final_newline(str); + + for (i=0; i<3; i++) + token[i] = strsep(&str, ","); + + if (str) + parse_err("too many arguments\n"); + + if (!token[2]) + parse_err("not enough arguments\n"); + + ret = parse_name(&name, token[0]); + if (ret == -ENOMEM) + parse_err("out of memory\n"); + if (ret == -ENOSPC) + parse_err("name too long\n"); + if (ret) + return 0; + + ret = parse_num32(&start, token[1]); + if (ret) + parse_err("illegal start address\n"); + + ret = parse_num32(&len, token[2]); + if (ret) + parse_err("illegal device length\n"); + + register_device(name, start, len); + + return 0; +} + +module_param_call(phram, phram_setup, NULL, NULL, 000); +MODULE_PARM_DESC(phram,"Memory region to map. \"map=,,\""); + + +static int __init init_phram(void) +{ + return 0; +} + +static void __exit cleanup_phram(void) +{ + unregister_devices(); +} + +module_init(init_phram); +module_exit(cleanup_phram); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jörn Engel "); +MODULE_DESCRIPTION("MTD driver for physical RAM"); --- linux-2.4.21/drivers/mtd/devices/pmc551.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/pmc551.c @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $ + * $Id: pmc551.c,v 1.30 2005/01/05 18:05:13 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -82,6 +82,7 @@ * * Comb the init routine. It's still a bit cludgy on a few things. */ +#include #include #include #include @@ -108,17 +109,11 @@ #include #include -#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 = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -174,16 +169,14 @@ printk(KERN_DEBUG "pmc551_erase() done\n"); #endif - if (instr->callback) { - (*(instr->callback))(instr); - } + mtd_erase_callback(instr); return 0; } static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi; u32 soff_lo; @@ -224,7 +217,7 @@ 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; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -286,7 +279,7 @@ 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; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -563,7 +556,7 @@ (size<1024)?size:(size<1048576)?size>>10:size>>20, (size<1024)?'B':(size<1048576)?'K':'M', size, ((dcmd&(0x1<<3)) == 0)?"non-":"", - PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK ); + (dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK ); /* * Check to see the state of the memory @@ -655,7 +648,7 @@ /* * PMC551 Card Initialization */ -int __init init_pmc551(void) +static int __init init_pmc551(void) { struct pci_dev *PCI_Device = NULL; struct mypriv *priv; @@ -681,11 +674,6 @@ printk(KERN_INFO PMC551_VERSION); - if(!pci_present()) { - printk(KERN_NOTICE "pmc551: PCI not enabled.\n"); - return -ENODEV; - } - /* * PCU-bus chipset probe. */ @@ -698,7 +686,7 @@ } printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%lX\n", - PCI_BASE_ADDRESS(PCI_Device)); + PCI_Device->resource[0].start); /* * The PMC551 device acts VERY weird if you don't init it @@ -752,7 +740,7 @@ printk(KERN_NOTICE "pmc551: Using specified aperture size %dM\n", asize>>20); priv->asize = asize; } - priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device) + priv->start = ioremap(((PCI_Device->resource[0].start) & PCI_BASE_ADDRESS_MEM_MASK), priv->asize); @@ -787,10 +775,10 @@ mtd->write = pmc551_write; mtd->point = pmc551_point; mtd->unpoint = pmc551_unpoint; - mtd->module = THIS_MODULE; mtd->type = MTD_RAM; mtd->name = "PMC551 RAM board"; mtd->erasesize = 0x10000; + mtd->owner = THIS_MODULE; if (add_mtd_device(mtd)) { printk(KERN_NOTICE "pmc551: Failed to register new device\n"); @@ -832,7 +820,7 @@ struct mypriv *priv; while((mtd=pmc551list)) { - priv = (struct mypriv *)mtd->priv; + priv = mtd->priv; pmc551list = priv->nextpmc551; if(priv->start) { --- linux-2.4.21/drivers/mtd/devices/slram.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/devices/slram.c @@ -1,6 +1,6 @@ /*====================================================================== - $Id: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $ + $Id: slram.c,v 1.34 2005/01/06 21:16:42 jwboyer Exp $ This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually @@ -50,6 +50,7 @@ #include #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ +#define SLRAM_BLK_SZ 0x4000 #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) @@ -75,13 +76,13 @@ 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 *, loff_t, size_t); -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 *); +static int slram_erase(struct mtd_info *, struct erase_info *); +static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); +static void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); +static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); -int slram_erase(struct mtd_info *mtd, struct erase_info *instr) +static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) { slram_priv_t *priv = mtd->priv; @@ -98,34 +99,38 @@ instr->state = MTD_ERASE_DONE; - if (instr->callback) { - (*(instr->callback))(instr); - } - else { - kfree(instr); - } + mtd_erase_callback(instr); return(0); } -int slram_point(struct mtd_info *mtd, loff_t from, size_t len, +static 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; + slram_priv_t *priv = mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; *mtdbuf = priv->start + from; *retlen = len; return(0); } -void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { } -int slram_read(struct mtd_info *mtd, loff_t from, size_t len, +static 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; + slram_priv_t *priv = mtd->priv; + + if (from > mtd->size) + return -EINVAL; + + if (from + len > mtd->size) + len = mtd->size - from; memcpy(buf, priv->start + from, len); @@ -133,10 +138,13 @@ return(0); } -int slram_write(struct mtd_info *mtd, loff_t to, size_t len, +static 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; + slram_priv_t *priv = mtd->priv; + + if (to + len > mtd->size) + return -EINVAL; memcpy(priv->start + to, buf, len); @@ -146,7 +154,7 @@ /*====================================================================*/ -int register_device(char *name, unsigned long start, unsigned long length) +static int register_device(char *name, unsigned long start, unsigned long length) { slram_mtd_list_t **curmtd; @@ -166,7 +174,7 @@ if ((*curmtd)->mtdinfo) { memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); (*curmtd)->mtdinfo->priv = - (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + kmalloc(sizeof(slram_priv_t), GFP_KERNEL); if (!(*curmtd)->mtdinfo->priv) { kfree((*curmtd)->mtdinfo); @@ -193,15 +201,15 @@ (*curmtd)->mtdinfo->name = name; (*curmtd)->mtdinfo->size = length; (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | - MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM; (*curmtd)->mtdinfo->erase = slram_erase; (*curmtd)->mtdinfo->point = slram_point; (*curmtd)->mtdinfo->unpoint = slram_unpoint; (*curmtd)->mtdinfo->read = slram_read; (*curmtd)->mtdinfo->write = slram_write; - (*curmtd)->mtdinfo->module = THIS_MODULE; + (*curmtd)->mtdinfo->owner = THIS_MODULE; (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = 0x0; + (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); @@ -218,7 +226,7 @@ return(0); } -void unregister_devices(void) +static void unregister_devices(void) { slram_mtd_list_t *nextitem; @@ -233,7 +241,7 @@ } } -unsigned long handle_unit(unsigned long value, char *unit) +static unsigned long handle_unit(unsigned long value, char *unit) { if ((*unit == 'M') || (*unit == 'm')) { return(value * 1024 * 1024); @@ -243,7 +251,7 @@ return(value); } -int parse_cmdline(char *devname, char *szstart, char *szlength) +static int parse_cmdline(char *devname, char *szstart, char *szlength) { char *buffer; unsigned long devstart; @@ -266,7 +274,7 @@ } T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", devname, devstart, devlength); - if ((devstart < 0) || (devlength < 0)) { + if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { E("slram: Illegal start / length parameter.\n"); return(-EINVAL); } @@ -290,7 +298,7 @@ #endif -int init_slram(void) +static int init_slram(void) { char *devname; int i; --- linux-2.4.21/drivers/mtd/ftl.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/ftl.c @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $ + * $Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -55,8 +55,8 @@ contact M-Systems (http://www.m-sys.com) directly. ======================================================================*/ +#include #include -#include #include /*#define PSYCHO_DEBUG */ @@ -68,43 +68,13 @@ #include #include #include -#include +#include #include - -#if (LINUX_VERSION_CODE >= 0x20100) #include -#endif -#if (LINUX_VERSION_CODE >= 0x20303) #include -#endif +#include #include -/*====================================================================*/ -/* Stuff which really ought to be in compatmac.h */ - -#if (LINUX_VERSION_CODE < 0x20328) -#define register_disk(dev, drive, minors, ops, size) \ - do { (dev)->part[(drive)*(minors)].nr_sects = size; \ - if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ - resetup_one_dev(dev, drive); } while (0) -#endif - -#if (LINUX_VERSION_CODE < 0x20320) -#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn -#define blk_init_queue(q, req) q = (req) -#define blk_cleanup_queue(q) q = NULL -#define request_arg_t void -#else -#define request_arg_t request_queue_t *q -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) -#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT -#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT -#else -#define BLK_INC_USE_COUNT do {} while(0) -#define BLK_DEC_USE_COUNT do {} while(0) -#endif /*====================================================================*/ @@ -119,19 +89,6 @@ #define FTL_MAJOR 44 #endif -/* Funky stuff for setting up a block device */ -#define MAJOR_NR FTL_MAJOR -#define DEVICE_NAME "ftl" -#define DEVICE_REQUEST do_ftl_request -#define DEVICE_ON(device) -#define DEVICE_OFF(device) - -#define DEVICE_NR(minor) ((minor)>>5) -#define REGION_NR(minor) (((minor)>>3)&3) -#define PART_NR(minor) ((minor)&7) -#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part)) - -#include /*====================================================================*/ @@ -142,8 +99,7 @@ #define MAX_REGION 4 /* Maximum number of partitions in an FTL region */ -#define PART_BITS 3 -#define MAX_PART 8 +#define PART_BITS 4 /* Maximum number of outstanding erase requests per socket */ #define MAX_ERASE 8 @@ -154,7 +110,7 @@ /* Each memory region corresponds to a minor device */ typedef struct partition_t { - struct mtd_info *mtd; + struct mtd_blktrans_dev mbd; u_int32_t state; u_int32_t *VirtualBlockMap; u_int32_t *VirtualPageMap; @@ -179,21 +135,10 @@ region_info_t region; memory_handle_t handle; #endif - atomic_t open; } partition_t; -partition_t *myparts[MAX_MTD_DEVICES]; - -static void ftl_notify_add(struct mtd_info *mtd); -static void ftl_notify_remove(struct mtd_info *mtd); - void ftl_freepart(partition_t *part); -static struct mtd_notifier ftl_notifier = { - add: ftl_notify_add, - remove: ftl_notify_remove, -}; - /* Partition state flags */ #define FTL_FORMATTED 0x01 @@ -204,51 +149,11 @@ #define XFER_PREPARED 0x03 #define XFER_FAILED 0x04 -static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)]; -static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)]; -static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)]; - -static struct gendisk ftl_gendisk = { - major: FTL_MAJOR, - major_name: "ftl", - minor_shift: PART_BITS, - max_p: MAX_PART, -#if (LINUX_VERSION_CODE < 0x20328) - max_nr: MAX_DEV*MAX_PART, -#endif - part: ftl_hd, - sizes: ftl_sizes, -}; - /*====================================================================*/ -static int ftl_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg); -static int ftl_open(struct inode *inode, struct file *file); -static release_t ftl_close(struct inode *inode, struct file *file); -static int ftl_reread_partitions(int minor); static void ftl_erase_callback(struct erase_info *done); -#if LINUX_VERSION_CODE < 0x20326 -static struct file_operations ftl_blk_fops = { - open: ftl_open, - release: ftl_close, - ioctl: ftl_ioctl, - read: block_read, - write: block_write, - fsync: block_fsync -}; -#else -static struct block_device_operations ftl_blk_fops = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) - owner: THIS_MODULE, -#endif - open: ftl_open, - release: ftl_close, - ioctl: ftl_ioctl, -}; -#endif /*====================================================================== @@ -262,19 +167,20 @@ { erase_unit_header_t header; loff_t offset, max_offset; - int ret; + size_t ret; + int err; part->header.FormattedSize = 0; - max_offset = (0x100000mtd->size)?0x100000:part->mtd->size; + max_offset = (0x100000mbd.mtd->size)?0x100000:part->mbd.mtd->size; /* Search first megabyte for a valid FTL header */ for (offset = 0; (offset + sizeof(header)) < max_offset; - offset += part->mtd->erasesize ? : 0x2000) { + offset += part->mbd.mtd->erasesize ? : 0x2000) { - ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, + err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, (unsigned char *)&header); - if (ret) - return ret; + if (err) + return err; if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break; } @@ -283,15 +189,15 @@ printk(KERN_NOTICE "ftl_cs: FTL header not found.\n"); return -ENOENT; } - if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 || + if (header.BlockSize != 9 || (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); return -1; } - if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { + if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n", - 1 << header.EraseUnitSize,part->mtd->erasesize); + 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); return -1; } part->header = header; @@ -326,7 +232,7 @@ for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) << part->header.EraseUnitSize); - ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, + ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval, (unsigned char *)&header); if (ret) @@ -391,7 +297,7 @@ part->EUNInfo[i].Deleted = 0; offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); - ret = part->mtd->read(part->mtd, offset, + ret = part->mbd.mtd->read(part->mbd.mtd, offset, part->BlocksPerUnit * sizeof(u_int32_t), &retval, (unsigned char *)part->bam_cache); @@ -451,12 +357,13 @@ if (!erase) return -ENOMEM; + erase->mtd = part->mbd.mtd; erase->callback = ftl_erase_callback; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; erase->priv = (u_long)part; - ret = part->mtd->erase(part->mtd, erase); + ret = part->mbd.mtd->erase(part->mbd.mtd, erase); if (!ret) xfer->EraseCount++; @@ -523,7 +430,7 @@ header.LogicalEUN = cpu_to_le16(0xffff); header.EraseCount = cpu_to_le32(xfer->EraseCount); - ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header), + ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen, (u_char *)&header); if (ret) { @@ -539,7 +446,7 @@ for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { - ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&ctl); if (ret) @@ -586,7 +493,7 @@ offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); - ret = part->mtd->read(part->mtd, offset, + ret = part->mbd.mtd->read(part->mbd.mtd, offset, part->BlocksPerUnit * sizeof(u_int32_t), &retlen, (u_char *) (part->bam_cache)); @@ -604,7 +511,7 @@ offset = xfer->Offset + 20; /* Bad! */ unit = cpu_to_le16(0x7fff); - ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), &retlen, (u_char *) &unit); if (ret) { @@ -624,7 +531,7 @@ break; case BLOCK_DATA: case BLOCK_REPLACEMENT: - ret = part->mtd->read(part->mtd, src, SECTOR_SIZE, + ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE, &retlen, (u_char *) buf); if (ret) { printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n"); @@ -632,7 +539,7 @@ } - ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE, + ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen, (u_char *) buf); if (ret) { printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n"); @@ -651,7 +558,7 @@ } /* Write the BAM to the transfer unit */ - ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), + ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), part->BlocksPerUnit * sizeof(int32_t), &retlen, (u_char *)part->bam_cache); if (ret) { @@ -661,7 +568,7 @@ /* All clear? Then update the LogicalEUN again */ - ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t), + ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), &retlen, (u_char *)&srcunitswap); if (ret) { @@ -749,8 +656,8 @@ if (queued) { DEBUG(1, "ftl_cs: waiting for transfer " "unit to be prepared...\n"); - if (part->mtd->sync) - part->mtd->sync(part->mtd); + if (part->mbd.mtd->sync) + part->mbd.mtd->sync(part->mbd.mtd); } else { static int ne = 0; if (++ne < 5) @@ -848,7 +755,7 @@ /* Invalidate cache */ part->bam_index = 0xffff; - ret = part->mtd->read(part->mtd, + ret = part->mbd.mtd->read(part->mbd.mtd, part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), part->BlocksPerUnit * sizeof(u_int32_t), &retlen, (u_char *) (part->bam_cache)); @@ -877,78 +784,6 @@ } /* find_free */ -/*====================================================================== - - This gets a memory handle for the region corresponding to the - minor device number. - -======================================================================*/ - -static int ftl_open(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - partition_t *partition; - - if (minor>>4 >= MAX_MTD_DEVICES) - return -ENODEV; - - partition = myparts[minor>>4]; - - if (!partition) - return -ENODEV; - - if (partition->state != FTL_FORMATTED) - return -ENXIO; - - if (ftl_gendisk.part[minor].nr_sects == 0) - return -ENXIO; - - BLK_INC_USE_COUNT; - - if (!get_mtd_device(partition->mtd, -1)) { - BLK_DEC_USE_COUNT; - return -ENXIO; - } - - if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { - put_mtd_device(partition->mtd); - BLK_DEC_USE_COUNT; - return -EROFS; - } - - DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); - - atomic_inc(&partition->open); - - return 0; -} - -/*====================================================================*/ - -static release_t ftl_close(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - partition_t *part = myparts[minor >> 4]; - int i; - - DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); - - /* Wait for any pending erase operations to complete */ - if (part->mtd->sync) - part->mtd->sync(part->mtd); - - for (i = 0; i < part->header.NumTransferUnits; i++) { - if (part->XferInfo[i].state == XFER_ERASED) - prepare_xfer(part, i); - } - - atomic_dec(&part->open); - - put_mtd_device(part->mtd); - BLK_DEC_USE_COUNT; - release_return(0); -} /* ftl_close */ - /*====================================================================== @@ -983,7 +818,7 @@ else { offset = (part->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize)); - ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, + ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, (u_char *) buffer); if (ret) { @@ -1022,7 +857,7 @@ le32_to_cpu(part->header.BAMOffset)); #ifdef PSYCHO_DEBUG - ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&old_addr); if (ret) { printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); @@ -1059,7 +894,7 @@ #endif part->bam_cache[blk] = le_virt_addr; } - ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), &retlen, (u_char *)&le_virt_addr); if (ret) { @@ -1119,13 +954,13 @@ part->EUNInfo[part->bam_index].Deleted++; offset = (part->EUNInfo[part->bam_index].Offset + blk * SECTOR_SIZE); - ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, + ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer); if (ret) { printk(KERN_NOTICE "ftl_cs: block write failed!\n"); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" - " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr, + " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr, offset); return -EIO; } @@ -1151,164 +986,32 @@ return 0; } /* ftl_write */ -/*====================================================================== - - IOCTL calls for getting device parameters. - -======================================================================*/ - -static int ftl_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg) +static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) { - struct hd_geometry *geo = (struct hd_geometry *)arg; - int ret = 0, minor = MINOR(inode->i_rdev); - partition_t *part= myparts[minor >> 4]; + partition_t *part = (void *)dev; u_long sect; - if (!part) - return -ENODEV; /* How? */ - - switch (cmd) { - case HDIO_GETGEO: - ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); - if (ret) return ret; - /* Sort of arbitrary: round size down to 4K boundary */ + /* Sort of arbitrary: round size down to 4KiB boundary */ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; - put_user(1, (char *)&geo->heads); - put_user(8, (char *)&geo->sectors); - put_user((sect>>3), (short *)&geo->cylinders); - put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); - break; - case BLKGETSIZE: - ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); - break; -#ifdef BLKGETSIZE64 - case BLKGETSIZE64: - ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); - break; -#endif - case BLKRRPART: - ret = ftl_reread_partitions(minor); - break; -#if (LINUX_VERSION_CODE < 0x20303) - 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); - break; - RO_IOCTLS(inode->i_rdev, arg); -#else - case BLKROSET: - case BLKROGET: - case BLKFLSBUF: - ret = blk_ioctl(inode->i_rdev, cmd, arg); - break; -#endif - default: - ret = -EINVAL; - } - - return ret; -} /* ftl_ioctl */ - -/*====================================================================== - - Handler for block device requests -======================================================================*/ - -static int ftl_reread_partitions(int minor) -{ - partition_t *part = myparts[minor >> 4]; - int i, whole; - - DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); - if ((atomic_read(&part->open) > 1)) { - return -EBUSY; - } - whole = minor & ~(MAX_PART-1); - - i = MAX_PART - 1; - while (i-- > 0) { - if (ftl_hd[whole+i].nr_sects > 0) { - kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); - - invalidate_device(rdev, 1); - } - ftl_hd[whole+i].start_sect = 0; - ftl_hd[whole+i].nr_sects = 0; - } - - scan_header(part); - - register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, - &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); + geo->heads = 1; + geo->sectors = 8; + geo->cylinders = sect >> 3; -#ifdef PCMCIA_DEBUG - for (i = 0; i < MAX_PART; i++) { - if (ftl_hd[whole+i].nr_sects > 0) - printk(KERN_INFO " %d: start %ld size %ld\n", i, - ftl_hd[whole+i].start_sect, - ftl_hd[whole+i].nr_sects); - } -#endif return 0; } -/*====================================================================== - - Handler for block device requests - -======================================================================*/ - -static void do_ftl_request(request_arg_t) +static int ftl_readsect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) { - int ret, minor; - partition_t *part; - - do { - // sti(); - INIT_REQUEST; - - minor = MINOR(CURRENT->rq_dev); - - part = myparts[minor >> 4]; - if (part) { - ret = 0; - - switch (CURRENT->cmd) { - case READ: - ret = ftl_read(part, CURRENT->buffer, - CURRENT->sector+ftl_hd[minor].start_sect, - CURRENT->current_nr_sectors); - if (ret) printk("ftl_read returned %d\n", ret); - break; - - case WRITE: - ret = ftl_write(part, CURRENT->buffer, - CURRENT->sector+ftl_hd[minor].start_sect, - CURRENT->current_nr_sectors); - if (ret) printk("ftl_write returned %d\n", ret); - break; - - default: - panic("ftl_cs: unknown block command!\n"); - - } - } else { - ret = 1; - printk("NULL part in ftl_request\n"); - } - - if (!ret) { - CURRENT->sector += CURRENT->current_nr_sectors; - } + return ftl_read((void *)dev, buf, block, 1); +} - end_request((ret == 0) ? 1 : 0); - } while (1); -} /* do_ftl_request */ +static int ftl_writesect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) +{ + return ftl_write((void *)dev, buf, block, 1); +} /*====================================================================*/ @@ -1337,19 +1040,9 @@ } /* ftl_freepart */ -static void ftl_notify_add(struct mtd_info *mtd) +static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { partition_t *partition; - int device; - - for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) - ; - - if (device == MAX_MTD_DEVICES) { - printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" - "Not scanning <%s>\n", mtd->name); - return; - } partition = kmalloc(sizeof(partition_t), GFP_KERNEL); @@ -1361,92 +1054,57 @@ memset(partition, 0, sizeof(partition_t)); - partition->mtd = mtd; + partition->mbd.mtd = mtd; if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { partition->state = FTL_FORMATTED; - atomic_set(&partition->open, 0); - myparts[device] = partition; - ftl_reread_partitions(device << 4); #ifdef PCMCIA_DEBUG - printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", + printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n", le32_to_cpu(partition->header.FormattedSize) >> 10); #endif - } else + partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; + partition->mbd.blksize = SECTOR_SIZE; + partition->mbd.tr = tr; + partition->mbd.devnum = -1; + if (!add_mtd_blktrans_dev((void *)partition)) + return; + } + + ftl_freepart(partition); kfree(partition); } -static void ftl_notify_remove(struct mtd_info *mtd) +static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { - int i,j; - - /* Q: What happens if you try to remove a device which has - * a currently-open FTL partition on it? - * - * A: You don't. The ftl_open routine is responsible for - * increasing the use count of the driver module which - * it uses. - */ - - /* That's the theory, anyway :) */ - - for (i=0; i< MAX_MTD_DEVICES; i++) - if (myparts[i] && myparts[i]->mtd == mtd) { - - if (myparts[i]->state == FTL_FORMATTED) - ftl_freepart(myparts[i]); - - myparts[i]->state = 0; - for (j=0; j<16; j++) { - ftl_gendisk.part[j].nr_sects=0; - ftl_gendisk.part[j].start_sect=0; - } - kfree(myparts[i]); - myparts[i] = NULL; - } + del_mtd_blktrans_dev(dev); + ftl_freepart((partition_t *)dev); + kfree(dev); } +struct mtd_blktrans_ops ftl_tr = { + .name = "ftl", + .major = FTL_MAJOR, + .part_bits = PART_BITS, + .readsect = ftl_readsect, + .writesect = ftl_writesect, + .getgeo = ftl_getgeo, + .add_mtd = ftl_add_mtd, + .remove_dev = ftl_remove_dev, + .owner = THIS_MODULE, +}; + int init_ftl(void) { - int i; - - memset(myparts, 0, sizeof(myparts)); - - DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n"); - - if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { - printk(KERN_NOTICE "ftl_cs: unable to grab major " - "device number!\n"); - return -EAGAIN; - } - - for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) - ftl_blocksizes[i] = 1024; - for (i = 0; i < MAX_DEV*MAX_PART; i++) { - ftl_hd[i].nr_sects = 0; - ftl_hd[i].start_sect = 0; - } - blksize_size[FTL_MAJOR] = ftl_blocksizes; - ftl_gendisk.major = FTL_MAJOR; - blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); - add_gendisk(&ftl_gendisk); - - register_mtd_user(&ftl_notifier); + DEBUG(0, "$Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $\n"); - return 0; + return register_mtd_blktrans(&ftl_tr); } static void __exit cleanup_ftl(void) { - unregister_mtd_user(&ftl_notifier); - - unregister_blkdev(FTL_MAJOR, "ftl"); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); - blksize_size[FTL_MAJOR] = NULL; - - del_gendisk(&ftl_gendisk); + deregister_mtd_blktrans(&ftl_tr); } module_init(init_ftl); --- /dev/null +++ linux-2.4.21/drivers/mtd/inftlcore.c @@ -0,0 +1,912 @@ +/* + * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * + * Based heavily on the nftlcore.c code which is: + * (c) 1999 Machine Vision Holdings, Inc. + * Author: David Woodhouse + * + * $Id: inftlcore.c,v 1.18 2004/11/16 18:28:59 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 + +extern void INFTL_dumptables(struct INFTLrecord *inftl); +extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); + +static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct INFTLrecord *inftl; + unsigned long temp; + + if (mtd->type != MTD_NANDFLASH) + return; + /* OK, this is moderately ugly. But probably safe. Alternatives? */ + if (memcmp(mtd->name, "DiskOnChip", 10)) + return; + + if (!mtd->block_isbad) { + printk(KERN_ERR +"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" +"Please use the new diskonchip driver under the NAND subsystem.\n"); + return; + } + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); + + inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); + + if (!inftl) { + printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); + return; + } + memset(inftl, 0, sizeof(*inftl)); + + inftl->mbd.mtd = mtd; + inftl->mbd.devnum = -1; + inftl->mbd.blksize = 512; + inftl->mbd.tr = tr; + memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); + inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; + + if (INFTL_mount(inftl) < 0) { + printk(KERN_WARNING "INFTL: could not mount device\n"); + kfree(inftl); + return; + } + + /* OK, it's a new one. Set up all the data structures. */ + + /* Calculate geometry */ + inftl->cylinders = 1024; + inftl->heads = 16; + + temp = inftl->cylinders * inftl->heads; + inftl->sectors = inftl->mbd.size / temp; + if (inftl->mbd.size % temp) { + inftl->sectors++; + temp = inftl->cylinders * inftl->sectors; + inftl->heads = inftl->mbd.size / temp; + + if (inftl->mbd.size % temp) { + inftl->heads++; + temp = inftl->heads * inftl->sectors; + inftl->cylinders = inftl->mbd.size / temp; + } + } + + if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) { + /* + Oh no we don't have + mbd.size == heads * cylinders * sectors + */ + printk(KERN_WARNING "INFTL: cannot calculate a geometry to " + "match size of 0x%lx.\n", inftl->mbd.size); + printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d " + "(== 0x%lx sects)\n", + inftl->cylinders, inftl->heads , inftl->sectors, + (long)inftl->cylinders * (long)inftl->heads * + (long)inftl->sectors ); + } + + if (add_mtd_blktrans_dev(&inftl->mbd)) { + if (inftl->PUtable) + kfree(inftl->PUtable); + if (inftl->VUtable) + kfree(inftl->VUtable); + kfree(inftl); + return; + } +#ifdef PSYCHO_DEBUG + printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); +#endif + return; +} + +static void inftl_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct INFTLrecord *inftl = (void *)dev; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum); + + del_mtd_blktrans_dev(dev); + + if (inftl->PUtable) + kfree(inftl->PUtable); + if (inftl->VUtable) + kfree(inftl->VUtable); + kfree(inftl); +} + +/* + * Actual INFTL access routines. + */ + +/* + * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. + * This function is used when the give Virtual Unit Chain. + */ +static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate) +{ + u16 pot = inftl->LastFreeEUN; + int silly = inftl->nb_blocks; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=%p," + "desperate=%d)\n", inftl, desperate); + + /* + * Normally, we force a fold to happen before we run out of free + * blocks completely. + */ + if (!desperate && inftl->numfreeEUNs < 2) { + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free " + "EUNs (%d)\n", inftl->numfreeEUNs); + return 0xffff; + } + + /* Scan for a free block */ + do { + if (inftl->PUtable[pot] == BLOCK_FREE) { + inftl->LastFreeEUN = pot; + return pot; + } + + if (++pot > inftl->lastEUN) + pot = 0; + + if (!silly--) { + printk(KERN_WARNING "INFTL: no free blocks found! " + "EUN range = %d - %d\n", 0, inftl->LastFreeEUN); + return BLOCK_NIL; + } + } while (pot != inftl->LastFreeEUN); + + return BLOCK_NIL; +} + +static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) +{ + u16 BlockMap[MAX_SECTORS_PER_UNIT]; + unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; + unsigned int thisEUN, prevEUN, status; + int block, silly; + unsigned int targetEUN; + struct inftl_oob oob; + size_t retlen; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d," + "pending=%d)\n", inftl, thisVUC, pendingblock); + + memset(BlockMap, 0xff, sizeof(BlockMap)); + memset(BlockDeleted, 0, sizeof(BlockDeleted)); + + thisEUN = targetEUN = inftl->VUtable[thisVUC]; + + if (thisEUN == BLOCK_NIL) { + printk(KERN_WARNING "INFTL: 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; + while (thisEUN < inftl->nb_blocks) { + for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { + if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) + continue; + + if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 16 , &retlen, + (char *)&oob) < 0) + status = SECTOR_IGNORE; + else + status = oob.b.Status | oob.b.Status1; + + switch(status) { + case SECTOR_FREE: + case SECTOR_IGNORE: + break; + case SECTOR_USED: + BlockMap[block] = thisEUN; + continue; + case SECTOR_DELETED: + BlockDeleted[block] = 1; + continue; + default: + printk(KERN_WARNING "INFTL: unknown status " + "for block %d in EUN %d: %x\n", + block, thisEUN, status); + break; + } + } + + if (!silly--) { + printk(KERN_WARNING "INFTL: infinite loop in Virtual " + "Unit Chain 0x%x\n", thisVUC); + return BLOCK_NIL; + } + + thisEUN = inftl->PUtable[thisEUN]; + } + + /* + * 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, "INFTL: folding chain %d into unit %d\n", + thisVUC, targetEUN); + + for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { + unsigned char movebuf[SECTORSIZE]; + int ret; + + /* + * If it's in the target EUN already, or if it's pending write, + * do nothing. + */ + if (BlockMap[block] == targetEUN || (pendingblock == + (thisVUC * (inftl->EraseSize / SECTORSIZE) + 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_READ(inftl->mbd.mtd, (inftl->EraseSize * + BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, + &retlen, movebuf); + if (ret < 0) { + ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * + BlockMap[block]) + (block * SECTORSIZE), + SECTORSIZE, &retlen, movebuf); + if (ret != -EIO) + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " + "away on retry?\n"); + } + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + + (block * SECTORSIZE), SECTORSIZE, &retlen, + movebuf, (char *)&oob, &inftl->oobinfo); + } + + /* + * Newest unit in chain now contains data from _all_ older units. + * So go through and erase each unit in chain, oldest first. (This + * is important, by doing oldest first if we crash/reboot then it + * it is relatively simple to clean up the mess). + */ + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n", + thisVUC); + + for (;;) { + /* Find oldest unit in chain. */ + thisEUN = inftl->VUtable[thisVUC]; + prevEUN = BLOCK_NIL; + while (inftl->PUtable[thisEUN] != BLOCK_NIL) { + prevEUN = thisEUN; + thisEUN = inftl->PUtable[thisEUN]; + } + + /* Check if we are all done */ + if (thisEUN == targetEUN) + break; + + if (INFTL_formatblock(inftl, thisEUN) < 0) { + /* + * Could not erase : mark block as reserved. + */ + inftl->PUtable[thisEUN] = BLOCK_RESERVED; + } else { + /* Correctly erased : mark it as free */ + inftl->PUtable[thisEUN] = BLOCK_FREE; + inftl->PUtable[prevEUN] = BLOCK_NIL; + inftl->numfreeEUNs++; + } + } + + return targetEUN; +} + +static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, 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; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=%p," + "pending=%d)\n", inftl, pendingblock); + + for (chain = 0; chain < inftl->nb_blocks; chain++) { + EUN = inftl->VUtable[chain]; + thislen = 0; + + while (EUN <= inftl->lastEUN) { + thislen++; + EUN = inftl->PUtable[EUN]; + if (thislen > 0xff00) { + printk(KERN_WARNING "INFTL: endless loop in " + "Virtual Chain %d: Unit %x\n", + chain, EUN); + /* + * Actually, don't return failure. + * Just ignore this chain and get on with it. + */ + thislen = 0; + break; + } + } + + if (thislen > ChainLength) { + ChainLength = thislen; + LongestChain = chain; + } + } + + if (ChainLength < 2) { + printk(KERN_WARNING "INFTL: no Virtual Unit Chains available " + "for folding. Failing request\n"); + return BLOCK_NIL; + } + + return INFTL_foldchain(inftl, LongestChain, pendingblock); +} + +static int nrbits(unsigned int val, int bitcount) +{ + int i, total = 0; + + for (i = 0; (i < bitcount); i++) + total += (((0x1 << i) & val) ? 1 : 0); + return total; +} + +/* + * INFTL_findwriteunit: Return the unit number into which we can write + * for this block. Make it available if it isn't already. + */ +static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) +{ + unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); + unsigned int thisEUN, writeEUN, prev_block, status; + unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); + struct inftl_oob oob; + struct inftl_bci bci; + unsigned char anac, nacs, parity; + size_t retlen; + int silly, silly2 = 3; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=%p," + "block=%d)\n", inftl, block); + + do { + /* + * Scan the media to find a unit in the VUC which has + * a free space for the block in question. + */ + writeEUN = BLOCK_NIL; + thisEUN = inftl->VUtable[thisVUC]; + silly = MAX_LOOPS; + + while (thisEUN <= inftl->lastEUN) { + MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci); + + status = bci.Status | bci.Status1; + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " + "EUN %d is %x\n", block , writeEUN, status); + + switch(status) { + case SECTOR_FREE: + writeEUN = thisEUN; + break; + case SECTOR_DELETED: + case SECTOR_USED: + /* Can't go any further */ + goto hitused; + case SECTOR_IGNORE: + break; + default: + /* + * Invalid block. Don't use it any more. + * Must implement. + */ + break; + } + + if (!silly--) { + printk(KERN_WARNING "INFTL: infinite loop in " + "Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; + } + + /* Skip to next block in chain */ + thisEUN = inftl->PUtable[thisEUN]; + } + +hitused: + if (writeEUN != BLOCK_NIL) + return writeEUN; + + + /* + * OK. We didn't find one in the existing chain, or there + * is no existing chain. Allocate a new one. + */ + writeEUN = INFTL_findfreeblock(inftl, 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. + */ + thisEUN = INFTL_makefreeblock(inftl, 0xffff); + + /* + * Hopefully we free something, lets try again. + * This time we are desperate... + */ + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 " + "to find free EUN to accommodate write to " + "VUC %d\n", thisVUC); + writeEUN = INFTL_findfreeblock(inftl, 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 "INFTL: cannot make free " + "space.\n"); +#ifdef DEBUG + INFTL_dumptables(inftl); + INFTL_dumpVUchains(inftl); +#endif + return BLOCK_NIL; + } + } + + /* + * Insert new block into virtual chain. Firstly update the + * block headers in flash... + */ + anac = 0; + nacs = 0; + thisEUN = inftl->VUtable[thisVUC]; + if (thisEUN != BLOCK_NIL) { + MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize + + 8, 8, &retlen, (char *)&oob.u); + anac = oob.u.a.ANAC + 1; + nacs = oob.u.a.NACs + 1; + } + + prev_block = inftl->VUtable[thisVUC]; + if (prev_block < inftl->nb_blocks) + prev_block -= inftl->firstEUN; + + parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; + parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; + parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; + parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; + + oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); + oob.u.a.prevUnitNo = cpu_to_le16(prev_block); + oob.u.a.ANAC = anac; + oob.u.a.NACs = nacs; + oob.u.a.parityPerField = parity; + oob.u.a.discarded = 0xaa; + + MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + /* Also back up header... */ + oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); + oob.u.b.prevUnitNo = cpu_to_le16(prev_block); + oob.u.b.ANAC = anac; + oob.u.b.NACs = nacs; + oob.u.b.parityPerField = parity; + oob.u.b.discarded = 0xaa; + + MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + + SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); + + inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; + inftl->VUtable[thisVUC] = writeEUN; + + inftl->numfreeEUNs--; + return writeEUN; + + } while (silly2--); + + printk(KERN_WARNING "INFTL: error folding to make room for Virtual " + "Unit Chain 0x%x\n", thisVUC); + return 0xffff; +} + +/* + * Given a Virtual Unit Chain, see if it can be deleted, and if so do it. + */ +static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) +{ + unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; + unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; + unsigned int thisEUN, status; + int block, silly; + struct inftl_bci bci; + size_t retlen; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=%p," + "thisVUC=%d)\n", inftl, thisVUC); + + memset(BlockUsed, 0, sizeof(BlockUsed)); + memset(BlockDeleted, 0, sizeof(BlockDeleted)); + + thisEUN = inftl->VUtable[thisVUC]; + if (thisEUN == BLOCK_NIL) { + printk(KERN_WARNING "INFTL: trying to delete non-existent " + "Virtual Unit Chain %d!\n", thisVUC); + return; + } + + /* + * Scan through the Erase Units to determine whether any data is in + * each of the 512-byte blocks within the Chain. + */ + silly = MAX_LOOPS; + while (thisEUN < inftl->nb_blocks) { + for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { + if (BlockUsed[block] || BlockDeleted[block]) + continue; + + if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + + (block * SECTORSIZE), 8 , &retlen, + (char *)&bci) < 0) + status = SECTOR_IGNORE; + else + status = bci.Status | bci.Status1; + + switch(status) { + case SECTOR_FREE: + case SECTOR_IGNORE: + break; + case SECTOR_USED: + BlockUsed[block] = 1; + continue; + case SECTOR_DELETED: + BlockDeleted[block] = 1; + continue; + default: + printk(KERN_WARNING "INFTL: unknown status " + "for block %d in EUN %d: 0x%x\n", + block, thisEUN, status); + } + } + + if (!silly--) { + printk(KERN_WARNING "INFTL: infinite loop in Virtual " + "Unit Chain 0x%x\n", thisVUC); + return; + } + + thisEUN = inftl->PUtable[thisEUN]; + } + + for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) + if (BlockUsed[block]) + return; + + /* + * For each block in the chain free it and make it available + * for future use. Erase from the oldest unit first. + */ + DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC); + + for (;;) { + u16 *prevEUN = &inftl->VUtable[thisVUC]; + thisEUN = *prevEUN; + + /* If the chain is all gone already, we're done */ + if (thisEUN == BLOCK_NIL) { + DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); + return; + } + + /* Find oldest unit in chain. */ + while (inftl->PUtable[thisEUN] != BLOCK_NIL) { + BUG_ON(thisEUN >= inftl->nb_blocks); + + prevEUN = &inftl->PUtable[thisEUN]; + thisEUN = *prevEUN; + } + + DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", + thisEUN, thisVUC); + + if (INFTL_formatblock(inftl, thisEUN) < 0) { + /* + * Could not erase : mark block as reserved. + */ + inftl->PUtable[thisEUN] = BLOCK_RESERVED; + } else { + /* Correctly erased : mark it as free */ + inftl->PUtable[thisEUN] = BLOCK_FREE; + inftl->numfreeEUNs++; + } + + /* Now sort out whatever was pointing to it... */ + *prevEUN = BLOCK_NIL; + + /* Ideally we'd actually be responsive to new + requests while we're doing this -- if there's + free space why should others be made to wait? */ + cond_resched(); + } + + inftl->VUtable[thisVUC] = BLOCK_NIL; +} + +static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) +{ + unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; + unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + unsigned int status; + int silly = MAX_LOOPS; + size_t retlen; + struct inftl_bci bci; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=%p," + "block=%d)\n", inftl, block); + + while (thisEUN < inftl->nb_blocks) { + if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else + status = bci.Status | bci.Status1; + + switch (status) { + case SECTOR_FREE: + case SECTOR_IGNORE: + break; + case SECTOR_DELETED: + thisEUN = BLOCK_NIL; + goto foundit; + case SECTOR_USED: + goto foundit; + default: + printk(KERN_WARNING "INFTL: unknown status for " + "block %d in EUN %d: 0x%x\n", + block, thisEUN, status); + break; + } + + if (!silly--) { + printk(KERN_WARNING "INFTL: infinite loop in Virtual " + "Unit Chain 0x%x\n", + block / (inftl->EraseSize / SECTORSIZE)); + return 1; + } + thisEUN = inftl->PUtable[thisEUN]; + } + +foundit: + if (thisEUN != BLOCK_NIL) { + loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; + + if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) + return -EIO; + bci.Status = bci.Status1 = SECTOR_DELETED; + if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) + return -EIO; + INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); + } + return 0; +} + +static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, + char *buffer) +{ + struct INFTLrecord *inftl = (void *)mbd; + unsigned int writeEUN; + unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + size_t retlen; + struct inftl_oob oob; + char *p, *pend; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld," + "buffer=%p)\n", inftl, block, buffer); + + /* Is block all zero? */ + pend = buffer + SECTORSIZE; + for (p = buffer; p < pend && !*p; p++) + ; + + if (p < pend) { + writeEUN = INFTL_findwriteunit(inftl, block); + + if (writeEUN == BLOCK_NIL) { + printk(KERN_WARNING "inftl_writeblock(): cannot find " + "block to write to\n"); + /* + * If we _still_ haven't got a block to use, + * we're screwed. + */ + return 1; + } + + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + + blockofs, SECTORSIZE, &retlen, (char *)buffer, + (char *)&oob, &inftl->oobinfo); + /* + * need to write SECTOR_USED flags since they are not written + * in mtd_writeecc + */ + } else { + INFTL_deleteblock(inftl, block); + } + + return 0; +} + +static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, + char *buffer) +{ + struct INFTLrecord *inftl = (void *)mbd; + unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; + unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); + unsigned int status; + int silly = MAX_LOOPS; + struct inftl_bci bci; + size_t retlen; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld," + "buffer=%p)\n", inftl, block, buffer); + + while (thisEUN < inftl->nb_blocks) { + if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + + blockofs, 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else + status = bci.Status | bci.Status1; + + switch (status) { + case SECTOR_DELETED: + thisEUN = BLOCK_NIL; + goto foundit; + case SECTOR_USED: + goto foundit; + case SECTOR_FREE: + case SECTOR_IGNORE: + break; + default: + printk(KERN_WARNING "INFTL: unknown status for " + "block %ld in EUN %d: 0x%04x\n", + block, thisEUN, status); + break; + } + + if (!silly--) { + printk(KERN_WARNING "INFTL: infinite loop in " + "Virtual Unit Chain 0x%lx\n", + block / (inftl->EraseSize / SECTORSIZE)); + return 1; + } + + thisEUN = inftl->PUtable[thisEUN]; + } + +foundit: + if (thisEUN == BLOCK_NIL) { + /* The requested block is not on the media, return all 0x00 */ + memset(buffer, 0, SECTORSIZE); + } else { + size_t retlen; + loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; + if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, + buffer)) + return -EIO; + } + return 0; +} + +static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct INFTLrecord *inftl = (void *)dev; + + geo->heads = inftl->heads; + geo->sectors = inftl->sectors; + geo->cylinders = inftl->cylinders; + + return 0; +} + +static struct mtd_blktrans_ops inftl_tr = { + .name = "inftl", + .major = INFTL_MAJOR, + .part_bits = INFTL_PARTN_BITS, + .getgeo = inftl_getgeo, + .readsect = inftl_readblock, + .writesect = inftl_writeblock, + .add_mtd = inftl_add_mtd, + .remove_dev = inftl_remove_dev, + .owner = THIS_MODULE, +}; + +extern char inftlmountrev[]; + +static int __init init_inftl(void) +{ + printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.18 $, " + "inftlmount.c %s\n", inftlmountrev); + + return register_mtd_blktrans(&inftl_tr); +} + +static void __exit cleanup_inftl(void) +{ + deregister_mtd_blktrans(&inftl_tr); +} + +module_init(init_inftl); +module_exit(cleanup_inftl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Ungerer , David Woodhouse , Fabrice Bellard et al."); +MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus"); --- /dev/null +++ linux-2.4.21/drivers/mtd/inftlmount.c @@ -0,0 +1,804 @@ +/* + * inftlmount.c -- INFTL mount code with extensive checks. + * + * Author: Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) + * + * Based heavily on the nftlmount.c code which is: + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * + * $Id: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char inftlmountrev[]="$Revision: 1.16 $"; + +/* + * find_boot_record: Find the INFTL Media Header and its Spare copy which + * contains the various device information of the INFTL partition and + * Bad Unit Table. Update the PUtable[] table according to the Bad + * Unit Table. PUtable[] is used for management of Erase Unit in + * other routines in inftlcore.c and inftlmount.c. + */ +static int find_boot_record(struct INFTLrecord *inftl) +{ + struct inftl_unittail h1; + //struct inftl_oob oob; + unsigned int i, block; + u8 buf[SECTORSIZE]; + struct INFTLMediaHeader *mh = &inftl->MediaHdr; + struct INFTLPartition *ip; + size_t retlen; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl); + + /* + * Assume logical EraseSize == physical erasesize for starting the + * scan. We'll sort it out later if we find a MediaHeader which says + * otherwise. + */ + inftl->EraseSize = inftl->mbd.mtd->erasesize; + inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + + inftl->MediaUnit = BLOCK_NIL; + + /* Search for a valid boot record */ + for (block = 0; block < inftl->nb_blocks; block++) { + int ret; + + /* + * Check for BNAND header first. Then whinge if it's found + * but later checks fail. + */ + ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, + SECTORSIZE, &retlen, buf); + /* We ignore ret in case the ECC of the MediaHeader is invalid + (which is apparently acceptable) */ + if (retlen != SECTORSIZE) { + static int warncount = 5; + + if (warncount) { + printk(KERN_WARNING "INFTL: block read at 0x%x " + "of mtd%d failed: %d\n", + block * inftl->EraseSize, + inftl->mbd.mtd->index, ret); + if (!--warncount) + printk(KERN_WARNING "INFTL: further " + "failures for this block will " + "not be printed\n"); + } + continue; + } + + if (retlen < 6 || memcmp(buf, "BNAND", 6)) { + /* BNAND\0 not found. Continue */ + continue; + } + + /* To be safer with BIOS, also use erase mark as discriminant */ + if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { + printk(KERN_WARNING "INFTL: ANAND header found at " + "0x%x in mtd%d, but OOB data read failed " + "(err %d)\n", block * inftl->EraseSize, + inftl->mbd.mtd->index, ret); + continue; + } + + + /* + * This is the first we've seen. + * Copy the media header structure into place. + */ + memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); + + /* Read the spare media header at offset 4096 */ + MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, + SECTORSIZE, &retlen, buf); + if (retlen != SECTORSIZE) { + printk(KERN_WARNING "INFTL: Unable to read spare " + "Media Header\n"); + return -1; + } + /* Check if this one is the same as the first one we found. */ + if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { + printk(KERN_WARNING "INFTL: Primary and spare Media " + "Headers disagree.\n"); + return -1; + } + + mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); + mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); + mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); + mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); + mh->FormatFlags = le32_to_cpu(mh->FormatFlags); + mh->PercentUsed = le32_to_cpu(mh->PercentUsed); + +#ifdef CONFIG_MTD_DEBUG_VERBOSE + if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { + printk("INFTL: Media Header ->\n" + " bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = 0x%x\n" + " PercentUsed = %d\n", + mh->bootRecordID, mh->NoOfBootImageBlocks, + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->BlockMultiplierBits, mh->FormatFlags, + mh->OsakVersion, mh->PercentUsed); + } +#endif + + if (mh->NoOfBDTLPartitions == 0) { + printk(KERN_WARNING "INFTL: Media Header sanity check " + "failed: NoOfBDTLPartitions (%d) == 0, " + "must be at least 1\n", mh->NoOfBDTLPartitions); + return -1; + } + + if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { + printk(KERN_WARNING "INFTL: Media Header sanity check " + "failed: Total Partitions (%d) > 4, " + "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->NoOfBinaryPartitions); + return -1; + } + + if (mh->BlockMultiplierBits > 1) { + printk(KERN_WARNING "INFTL: sorry, we don't support " + "UnitSizeFactor 0x%02x\n", + mh->BlockMultiplierBits); + return -1; + } else if (mh->BlockMultiplierBits == 1) { + printk(KERN_WARNING "INFTL: support for INFTL with " + "UnitSizeFactor 0x%02x is experimental\n", + mh->BlockMultiplierBits); + inftl->EraseSize = inftl->mbd.mtd->erasesize << + mh->BlockMultiplierBits; + inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + block >>= mh->BlockMultiplierBits; + } + + /* Scan the partitions */ + for (i = 0; (i < 4); i++) { + ip = &mh->Partitions[i]; + ip->virtualUnits = le32_to_cpu(ip->virtualUnits); + ip->firstUnit = le32_to_cpu(ip->firstUnit); + ip->lastUnit = le32_to_cpu(ip->lastUnit); + ip->flags = le32_to_cpu(ip->flags); + ip->spareUnits = le32_to_cpu(ip->spareUnits); + ip->Reserved0 = le32_to_cpu(ip->Reserved0); + +#ifdef CONFIG_MTD_DEBUG_VERBOSE + if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { + printk(" PARTITION[%d] ->\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + i, ip->virtualUnits, ip->firstUnit, + ip->lastUnit, ip->flags, + ip->spareUnits); + } +#endif + + if (ip->Reserved0 != ip->firstUnit) { + struct erase_info *instr = &inftl->instr; + + instr->mtd = inftl->mbd.mtd; + + /* + * Most likely this is using the + * undocumented qiuck mount feature. + * We don't support that, we will need + * to erase the hidden block for full + * compatibility. + */ + instr->addr = ip->Reserved0 * inftl->EraseSize; + instr->len = inftl->EraseSize; + MTD_ERASE(inftl->mbd.mtd, instr); + } + if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { + printk(KERN_WARNING "INFTL: Media Header " + "Partition %d sanity check failed\n" + " firstUnit %d : lastUnit %d > " + "virtualUnits %d\n", i, ip->lastUnit, + ip->firstUnit, ip->Reserved0); + return -1; + } + if (ip->Reserved1 != 0) { + printk(KERN_WARNING "INFTL: Media Header " + "Partition %d sanity check failed: " + "Reserved1 %d != 0\n", + i, ip->Reserved1); + return -1; + } + + if (ip->flags & INFTL_BDTL) + break; + } + + if (i >= 4) { + printk(KERN_WARNING "INFTL: Media Header Partition " + "sanity check failed:\n No partition " + "marked as Disk Partition\n"); + return -1; + } + + inftl->nb_boot_blocks = ip->firstUnit; + inftl->numvunits = ip->virtualUnits; + if (inftl->numvunits > (inftl->nb_blocks - + inftl->nb_boot_blocks - 2)) { + printk(KERN_WARNING "INFTL: Media Header sanity check " + "failed:\n numvunits (%d) > nb_blocks " + "(%d) - nb_boot_blocks(%d) - 2\n", + inftl->numvunits, inftl->nb_blocks, + inftl->nb_boot_blocks); + return -1; + } + + inftl->mbd.size = inftl->numvunits * + (inftl->EraseSize / SECTORSIZE); + + /* + * Block count is set to last used EUN (we won't need to keep + * any meta-data past that point). + */ + inftl->firstEUN = ip->firstUnit; + inftl->lastEUN = ip->lastUnit; + inftl->nb_blocks = ip->lastUnit + 1; + + /* Memory alloc */ + inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); + if (!inftl->PUtable) { + printk(KERN_WARNING "INFTL: allocation of PUtable " + "failed (%zd bytes)\n", + inftl->nb_blocks * sizeof(u16)); + return -ENOMEM; + } + + inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); + if (!inftl->VUtable) { + kfree(inftl->PUtable); + printk(KERN_WARNING "INFTL: allocation of VUtable " + "failed (%zd bytes)\n", + inftl->nb_blocks * sizeof(u16)); + return -ENOMEM; + } + + /* Mark the blocks before INFTL MediaHeader as reserved */ + for (i = 0; i < inftl->nb_boot_blocks; i++) + inftl->PUtable[i] = BLOCK_RESERVED; + /* Mark all remaining blocks as potentially containing data */ + for (; i < inftl->nb_blocks; i++) + inftl->PUtable[i] = BLOCK_NOTEXPLORED; + + /* Mark this boot record (NFTL MediaHeader) block as reserved */ + inftl->PUtable[block] = BLOCK_RESERVED; + + /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ + for (i = 0; i < inftl->nb_blocks; i++) { + int physblock; + /* If any of the physical eraseblocks are bad, don't + use the unit. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) { + if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock)) + inftl->PUtable[i] = BLOCK_RESERVED; + } + } + + inftl->MediaUnit = block; + return 0; + } + + /* Not found. */ + return -1; +} + +static int memcmpb(void *a, int c, int n) +{ + int i; + for (i = 0; i < n; i++) { + if (c != ((unsigned char *)a)[i]) + return 1; + } + return 0; +} + +/* + * check_free_sector: check if a free sector is actually FREE, + * i.e. All 0xff in data and oob area. + */ +static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, + int len, int check_oob) +{ + u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; + size_t retlen; + int i; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p," + "address=0x%x,len=%d,check_oob=%d)\n", inftl, + address, len, check_oob); + + for (i = 0; i < len; i += SECTORSIZE) { + if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0) + return -1; + if (memcmpb(buf, 0xff, SECTORSIZE) != 0) + return -1; + + if (check_oob) { + if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0) + return -1; + } + address += SECTORSIZE; + } + + return 0; +} + +/* + * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase + * Unit and Update INFTL metadata. Each erase operation is + * checked with check_free_sectors. + * + * Return: 0 when succeed, -1 on error. + * + * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? + */ +int INFTL_formatblock(struct INFTLrecord *inftl, int block) +{ + size_t retlen; + struct inftl_unittail uci; + struct erase_info *instr = &inftl->instr; + int physblock; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," + "block=%d)\n", inftl, block); + + memset(instr, 0, sizeof(struct erase_info)); + + /* FIXME: Shouldn't we be setting the 'discarded' flag to zero + _first_? */ + + /* Use async erase interface, test return code */ + instr->mtd = inftl->mbd.mtd; + instr->addr = block * inftl->EraseSize; + instr->len = inftl->mbd.mtd->erasesize; + /* Erase one physical eraseblock at a time, even though the NAND api + allows us to group them. This way we if we have a failure, we can + mark only the failed block in the bbt. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { + MTD_ERASE(inftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + printk(KERN_WARNING "INFTL: error while formatting block %d\n", + block); + goto fail; + } + + /* + * Check the "freeness" of Erase Unit before updating metadata. + * FixMe: is this check really necessary? Since we have check the + * return code after the erase operation. + */ + if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) + goto fail; + } + + uci.EraseMark = cpu_to_le16(ERASE_MARK); + uci.EraseMark1 = cpu_to_le16(ERASE_MARK); + uci.Reserved[0] = 0; + uci.Reserved[1] = 0; + uci.Reserved[2] = 0; + uci.Reserved[3] = 0; + instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; + if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr + + 8, 8, &retlen, (char *)&uci) < 0) + goto fail; + return 0; +fail: + /* could not format, update the bad block table (caller is responsible + for setting the PUtable to BLOCK_RESERVED on failure) */ + inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr); + return -1; +} + +/* + * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase + * Units in a Virtual Unit Chain, i.e. all the units are disconnected. + * + * Since the chain is invalid then we will have to erase it from its + * head (normally for INFTL we go from the oldest). But if it has a + * loop then there is no oldest... + */ +static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) +{ + unsigned int block = first_block, block1; + + printk(KERN_WARNING "INFTL: formatting chain at block %d\n", + first_block); + + for (;;) { + block1 = inftl->PUtable[block]; + + printk(KERN_WARNING "INFTL: formatting block %d\n", block); + if (INFTL_formatblock(inftl, block) < 0) { + /* + * Cannot format !!!! Mark it as Bad Unit, + */ + inftl->PUtable[block] = BLOCK_RESERVED; + } else { + inftl->PUtable[block] = BLOCK_FREE; + } + + /* Goto next block on the chain */ + block = block1; + + if (block == BLOCK_NIL || block >= inftl->lastEUN) + break; + } +} + +void INFTL_dumptables(struct INFTLrecord *s) +{ + int i; + + printk("-------------------------------------------" + "----------------------------------\n"); + + printk("VUtable[%d] ->", s->nb_blocks); + for (i = 0; i < s->nb_blocks; i++) { + if ((i % 8) == 0) + printk("\n%04x: ", i); + printk("%04x ", s->VUtable[i]); + } + + printk("\n-------------------------------------------" + "----------------------------------\n"); + + printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks); + for (i = 0; i <= s->lastEUN; i++) { + if ((i % 8) == 0) + printk("\n%04x: ", i); + printk("%04x ", s->PUtable[i]); + } + + printk("\n-------------------------------------------" + "----------------------------------\n"); + + printk("INFTL ->\n" + " EraseSize = %d\n" + " h/s/c = %d/%d/%d\n" + " numvunits = %d\n" + " firstEUN = %d\n" + " lastEUN = %d\n" + " numfreeEUNs = %d\n" + " LastFreeEUN = %d\n" + " nb_blocks = %d\n" + " nb_boot_blocks = %d", + s->EraseSize, s->heads, s->sectors, s->cylinders, + s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs, + s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks); + + printk("\n-------------------------------------------" + "----------------------------------\n"); +} + +void INFTL_dumpVUchains(struct INFTLrecord *s) +{ + int logical, block, i; + + printk("-------------------------------------------" + "----------------------------------\n"); + + printk("INFTL Virtual Unit Chains:\n"); + for (logical = 0; logical < s->nb_blocks; logical++) { + block = s->VUtable[logical]; + if (block > s->nb_blocks) + continue; + printk(" LOGICAL %d --> %d ", logical, block); + for (i = 0; i < s->nb_blocks; i++) { + if (s->PUtable[block] == BLOCK_NIL) + break; + block = s->PUtable[block]; + printk("%d ", block); + } + printk("\n"); + } + + printk("-------------------------------------------" + "----------------------------------\n"); +} + +int INFTL_mount(struct INFTLrecord *s) +{ + unsigned int block, first_block, prev_block, last_block; + unsigned int first_logical_block, logical_block, erase_mark; + int chain_length, do_format_chain; + struct inftl_unithead1 h0; + struct inftl_unittail h1; + size_t retlen; + int i; + u8 *ANACtable, ANAC; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", s); + + /* Search for INFTL MediaHeader and Spare INFTL Media Header */ + if (find_boot_record(s) < 0) { + printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); + return -1; + } + + /* Init the logical to physical table */ + for (i = 0; i < s->nb_blocks; i++) + s->VUtable[i] = BLOCK_NIL; + + logical_block = block = BLOCK_NIL; + + /* Temporary buffer to store ANAC numbers. */ + ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); + memset(ANACtable, 0, s->nb_blocks); + + /* + * First pass is to explore each physical unit, and construct the + * virtual chains that exist (newest physical unit goes into VUtable). + * Any block that is in any way invalid will be left in the + * NOTEXPLORED state. Then at the end we will try to format it and + * mark it as free. + */ + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n"); + for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) { + if (s->PUtable[first_block] != BLOCK_NOTEXPLORED) + continue; + + do_format_chain = 0; + first_logical_block = BLOCK_NIL; + last_block = BLOCK_NIL; + block = first_block; + + for (chain_length = 0; ; chain_length++) { + + if ((chain_length == 0) && + (s->PUtable[block] != BLOCK_NOTEXPLORED)) { + /* Nothing to do here, onto next block */ + break; + } + + if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, + 8, &retlen, (char *)&h0) < 0 || + MTD_READOOB(s->mbd.mtd, block * s->EraseSize + + 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { + /* Should never happen? */ + do_format_chain++; + break; + } + + logical_block = le16_to_cpu(h0.virtualUnitNo); + prev_block = le16_to_cpu(h0.prevUnitNo); + erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1)); + ANACtable[block] = h0.ANAC; + + /* Previous block is relative to start of Partition */ + if (prev_block < s->nb_blocks) + prev_block += s->firstEUN; + + /* Already explored partial chain? */ + if (s->PUtable[block] != BLOCK_NOTEXPLORED) { + /* Check if chain for this logical */ + if (logical_block == first_logical_block) { + if (last_block != BLOCK_NIL) + s->PUtable[last_block] = block; + } + break; + } + + /* Check for invalid block */ + if (erase_mark != ERASE_MARK) { + printk(KERN_WARNING "INFTL: corrupt block %d " + "in chain %d, chain length %d, erase " + "mark 0x%x?\n", block, first_block, + chain_length, erase_mark); + /* + * Assume end of chain, probably incomplete + * fold/erase... + */ + if (chain_length == 0) + do_format_chain++; + break; + } + + /* Check for it being free already then... */ + if ((logical_block == BLOCK_FREE) || + (logical_block == BLOCK_NIL)) { + s->PUtable[block] = BLOCK_FREE; + break; + } + + /* Sanity checks on block numbers */ + if ((logical_block >= s->nb_blocks) || + ((prev_block >= s->nb_blocks) && + (prev_block != BLOCK_NIL))) { + if (chain_length > 0) { + printk(KERN_WARNING "INFTL: corrupt " + "block %d in chain %d?\n", + block, first_block); + do_format_chain++; + } + break; + } + + if (first_logical_block == BLOCK_NIL) { + first_logical_block = logical_block; + } else { + if (first_logical_block != logical_block) { + /* Normal for folded chain... */ + break; + } + } + + /* + * Current block is valid, so if we followed a virtual + * chain to get here then we can set the previous + * block pointer in our PUtable now. Then move onto + * the previous block in the chain. + */ + s->PUtable[block] = BLOCK_NIL; + if (last_block != BLOCK_NIL) + s->PUtable[last_block] = block; + last_block = block; + block = prev_block; + + /* Check for end of chain */ + if (block == BLOCK_NIL) + break; + + /* Validate next block before following it... */ + if (block > s->lastEUN) { + printk(KERN_WARNING "INFTL: invalid previous " + "block %d in chain %d?\n", block, + first_block); + do_format_chain++; + break; + } + } + + if (do_format_chain) { + format_chain(s, first_block); + continue; + } + + /* + * Looks like a valid chain then. It may not really be the + * newest block in the chain, but it is the newest we have + * found so far. We might update it in later iterations of + * this loop if we find something newer. + */ + s->VUtable[first_logical_block] = first_block; + logical_block = BLOCK_NIL; + } + +#ifdef CONFIG_MTD_DEBUG_VERBOSE + if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + INFTL_dumptables(s); +#endif + + /* + * Second pass, check for infinite loops in chains. These are + * possible because we don't update the previous pointers when + * we fold chains. No big deal, just fix them up in PUtable. + */ + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n"); + for (logical_block = 0; logical_block < s->numvunits; logical_block++) { + block = s->VUtable[logical_block]; + last_block = BLOCK_NIL; + + /* Check for free/reserved/nil */ + if (block >= BLOCK_RESERVED) + continue; + + ANAC = ANACtable[block]; + for (i = 0; i < s->numvunits; i++) { + if (s->PUtable[block] == BLOCK_NIL) + break; + if (s->PUtable[block] > s->lastEUN) { + printk(KERN_WARNING "INFTL: invalid prev %d, " + "in virtual chain %d\n", + s->PUtable[block], logical_block); + s->PUtable[block] = BLOCK_NIL; + + } + if (ANACtable[block] != ANAC) { + /* + * Chain must point back to itself. This is ok, + * but we will need adjust the tables with this + * newest block and oldest block. + */ + s->VUtable[logical_block] = block; + s->PUtable[last_block] = BLOCK_NIL; + break; + } + + ANAC--; + last_block = block; + block = s->PUtable[block]; + } + + if (i >= s->nb_blocks) { + /* + * Uhoo, infinite chain with valid ANACS! + * Format whole chain... + */ + format_chain(s, first_block); + } + } + +#ifdef CONFIG_MTD_DEBUG_VERBOSE + if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + INFTL_dumptables(s); + if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + INFTL_dumpVUchains(s); +#endif + + /* + * Third pass, format unreferenced blocks and init free block count. + */ + s->numfreeEUNs = 0; + s->LastFreeEUN = BLOCK_NIL; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n"); + for (block = s->firstEUN; block <= s->lastEUN; block++) { + if (s->PUtable[block] == BLOCK_NOTEXPLORED) { + printk("INFTL: unreferenced block %d, formatting it\n", + block); + if (INFTL_formatblock(s, block) < 0) + s->PUtable[block] = BLOCK_RESERVED; + else + s->PUtable[block] = BLOCK_FREE; + } + if (s->PUtable[block] == BLOCK_FREE) { + s->numfreeEUNs++; + if (s->LastFreeEUN == BLOCK_NIL) + s->LastFreeEUN = block; + } + } + + kfree(ANACtable); + return 0; +} --- linux-2.4.21/drivers/mtd/maps/Config.in~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/Config.in @@ -1,16 +1,18 @@ # drivers/mtd/maps/Config.in -# $Id: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $ +# $Id: Config.in,v 1.72 2005/02/27 21:50:21 ppopov 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_GEN_PROBE -if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then +bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS + +bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE +if [ "$CONFIG_MTD_PHYSMAP" = "y" ]; 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 + int ' Bank width in octets' CONFIG_MTD_PHYSMAP_BANKWIDTH 2 fi if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then @@ -21,41 +23,58 @@ 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 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 Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000 fi - 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 ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE - dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE + dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE fi -if [ "$CONFIG_PPC" = "y" ]; then - dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL +if [ "$CONFIG_PPC32" = "y" ]; then + if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then + dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE + fi + if [ "$CONFIG_8xx" = "y" ]; then + if [ "$CONFIG_TQM8xxL" = "y" ]; then + dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI + fi + if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI + fi + if [ "$CONFIG_MBX" = "y" ]; then dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI + fi + if [ "$CONFIG_DBOX2" = "y" ]; then dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI + fi dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI - dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI + fi + if [ "$CONFIG_4xx" = "y" ]; then + if [ "$CONFIG_40x" = "y" ]; then + if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then + dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI + fi + dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH + dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2 + dep_tristate ' Flash device mapped on IBM Walnut' CONFIG_MTD_WALNUT $CONFIG_MTD_JEDECPROBE $CONFIG_WALNUT + fi + if [ "$CONFIG_440" = "y" ]; then + dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_JEDECPROBE $CONFIG_EBONY + fi + fi fi -if [ "$CONFIG_MIPS" = "y" ]; then - dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000 - dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500 - dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100 - if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \ - -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then - bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT - bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER - fi +if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then + dep_tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' CONFIG_MTD_ALCHEMY $CONFIG_SOC_AU1X00 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 @@ -63,7 +82,7 @@ int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2 fi dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT - dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT + dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT fi if [ "$CONFIG_SUPERH" = "y" ]; then @@ -75,22 +94,25 @@ fi if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' CFI Flash device mapped on Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK $CONFIG_MTD_PARTITIONS - dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS - dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE + dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 - dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_FORTUNET - dep_tristate ' CFI Flash device mapped on Epxa' CONFIG_MTD_EPXA $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK + dep_tristate ' CFI Flash device mapped on Ramses board' CONFIG_MTD_RAMSES $CONFIG_MTD_CFI $CONFIG_ARCH_RAMSES $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 - dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_ARCH_EDB7212 $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA + dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO fi if [ "$CONFIG_ALPHA" = "y" ]; then - dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE + dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS fi if [ "$CONFIG_UCLINUX" = "y" ]; then @@ -98,7 +120,7 @@ fi # This needs CFI or JEDEC, depending on the cards found. -dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI -dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA +dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS +dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS endmenu --- linux-2.4.21/drivers/mtd/maps/Makefile~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/Makefile @@ -1,67 +1,11 @@ # -# linux/drivers/maps/Makefile +# linux/drivers/maps/Makefile.24 +# Makefile for obsolete kernels # -# $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $ +# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $ O_TARGET := mapslink.o +export-objs := map_funcs.o -# Chip mappings -obj-$(CONFIG_MTD_CDB89712) += cdb89712.o -obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o -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_DILNETPC) += dilnetpc.o -obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o -obj-$(CONFIG_MTD_EPXA) += epxa-flash.o -obj-$(CONFIG_MTD_IQ80310) += iq80310.o -obj-$(CONFIG_MTD_LUBBOCK) += lubbock.o -obj-$(CONFIG_MTD_PXA_CERF) += pxa_cerf.o -obj-$(CONFIG_MTD_TRIZEPS2) += trizeps2.o -obj-$(CONFIG_MTD_L440GX) += l440gx.o -obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o -obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o -obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o -obj-$(CONFIG_MTD_MBX860) += mbx860.o -obj-$(CONFIG_MTD_NORA) += nora.o -obj-$(CONFIG_MTD_CEIVA) += ceiva.o -obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o -ifneq ($(CONFIG_MTD_PHYSMAP),n) - ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) - obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o - else - obj-$(CONFIG_MTD_PHYSMAP) += physmap.o - endif -endif -obj-$(CONFIG_MTD_PNC2000) += pnc2000.o -obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o -obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o -obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o -obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o -ifeq ($(CONFIG_ASSABET_NEPONSET),y) - obj-$(CONFIG_MTD_SA1100) += neponset-flash.o -endif -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_SCx200_DOCFLASH)+= scx200_docflash.o -obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o -obj-$(CONFIG_MTD_OCELOT) += ocelot.o -obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o -obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o -obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o -obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o -obj-$(CONFIG_MTD_LASAT) += lasat.o -obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o -obj-$(CONFIG_MTD_EDB7312) += edb7312.o -obj-$(CONFIG_MTD_IMPA7) += impa7.o -obj-$(CONFIG_MTD_FORTUNET) += fortunet.o -obj-$(CONFIG_MTD_REDWOOD) += redwood.o -obj-$(CONFIG_MTD_UCLINUX) += uclinux.o -obj-$(CONFIG_MTD_NETtel) += nettel.o -obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o - +include Makefile.common include $(TOPDIR)/Rules.make --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/Makefile.common @@ -0,0 +1,73 @@ +# +# linux/drivers/maps/Makefile +# +# $Id: Makefile.common,v 1.27 2005/03/07 23:15:48 joern Exp $ + +ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) +obj-$(CONFIG_MTD) += map_funcs.o +endif + +# Chip mappings +obj-$(CONFIG_MTD_CDB89712) += cdb89712.o +obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o +obj-$(CONFIG_MTD_BAST) += bast-flash.o +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_DILNETPC) += dilnetpc.o +obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o +obj-$(CONFIG_MTD_IQ80310) += iq80310.o +obj-$(CONFIG_MTD_L440GX) += l440gx.o +obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o +obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o +obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o +obj-$(CONFIG_MTD_RAMSES) += ramses.o +obj-$(CONFIG_MTD_MBX860) += mbx860.o +obj-$(CONFIG_MTD_CEIVA) += ceiva.o +obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o +obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +obj-$(CONFIG_MTD_MULTI_PHYSMAP) += mphysmap.o +obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o +obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o +obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o +obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_IPAQ) += ipaq-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_TS5500) += ts5500_flash.o +obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o +obj-$(CONFIG_MTD_VMAX) += vmax301.o +obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o +obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o +obj-$(CONFIG_MTD_OCELOT) += ocelot.o +obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o +obj-$(CONFIG_MTD_PCI) += pci.o +obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o +obj-$(CONFIG_MTD_LASAT) += lasat.o +obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o +obj-$(CONFIG_MTD_EDB7312) += edb7312.o +obj-$(CONFIG_MTD_IMPA7) += impa7.o +obj-$(CONFIG_MTD_FORTUNET) += fortunet.o +obj-$(CONFIG_MTD_REDWOOD) += redwood.o +obj-$(CONFIG_MTD_CHESTNUT) += chestnut.o +obj-$(CONFIG_MTD_UCLINUX) += uclinux.o +obj-$(CONFIG_MTD_NETtel) += nettel.o +obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o +obj-$(CONFIG_MTD_EBONY) += ebony.o +obj-$(CONFIG_MTD_OCOTEA) += ocotea.o +obj-$(CONFIG_MTD_BEECH) += beech-mtd.o +obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o +obj-$(CONFIG_MTD_WALNUT) += walnut.o +obj-$(CONFIG_MTD_H720X) += h720x-flash.o +obj-$(CONFIG_MTD_SBC8240) += sbc8240.o +obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o +obj-$(CONFIG_MTD_MPC1211) += mpc1211.o +obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o +obj-$(CONFIG_MTD_IXP2000) += ixp2000.o +obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o +obj-$(CONFIG_MTD_DMV182) += dmv182.o +obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o +obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o --- linux-2.4.21/drivers/mtd/maps/amd76xrom.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/amd76xrom.c @@ -2,133 +2,138 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.1 2002/10/18 22:45:48 eric Exp $ + * $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $ */ #include #include #include +#include #include #include #include +#include +#include #include #include #include +#include + + +#define xstr(s) str(s) +#define str(s) #s +#define MOD_NAME xstr(KBUILD_BASENAME) + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ +struct amd76xrom_window { + void __iomem *virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; struct amd76xrom_map_info { + struct list_head list; struct map_info map; struct mtd_info *mtd; - unsigned long window_addr; - u32 window_start, window_size; - struct pci_dev *pdev; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; -static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} +static struct amd76xrom_window amd76xrom_window = { + .maps = LIST_HEAD_INIT(amd76xrom_window.maps), +}; -static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr) +static void amd76xrom_cleanup(struct amd76xrom_window *window) { - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} + struct amd76xrom_map_info *map, *scratch; + u8 byte; -static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} + if (window->pdev) { + /* Disable writes through the rom window */ + pci_read_config_byte(window->pdev, 0x40, &byte); + pci_write_config_byte(window->pdev, 0x40, byte & ~1); + } -static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) { + release_resource(&map->rsrc); + } + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); -static void amd76xrom_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 (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; + } } -static struct amd76xrom_map_info amd76xrom_map = { - map: { - name: "AMD76X rom", - size: 0, - buswidth: 1, - read8: amd76xrom_read8, - read16: amd76xrom_read16, - read32: amd76xrom_read32, - copy_from: amd76xrom_copy_from, - write8: amd76xrom_write8, - write16: amd76xrom_write16, - write32: amd76xrom_write32, - copy_to: amd76xrom_copy_to, - /* The standard rom socket is for single power supply chips - * that don't have an extra vpp. - */ - }, - mtd: 0, - window_addr: 0, -}; static int __devinit amd76xrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { - struct rom_window { - u32 start; - u32 size; - u8 segen_bits; - }; - static struct rom_window rom_window[] = { - { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, - { 0xffc00000, 4*1024*1024, (1<<7), }, - { 0xffff0000, 64*1024, 0 }, - { 0 , 0, 0 }, - }; - static const u32 rom_probe_sizes[] = { - 5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, - 256*1024, 128*1024, 64*1024, 0}; - static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", 0 }; + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; - struct amd76xrom_map_info *info = &amd76xrom_map; - struct rom_window *window; - int i; - u32 rom_size; + struct amd76xrom_window *window = &amd76xrom_window; + struct amd76xrom_map_info *map = NULL; + unsigned long map_top; - window = &rom_window[0]; -#if 0 - while(window->size) { - if (request_mem_region(window->start, window->size, "amd76xrom")) { - break; + /* Remember the pci dev I find the window in */ + window->pdev = pdev; + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x43, &byte); + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { + window->phys = 0xffb00000; /* 5MiB */ } - window++; + else if ((byte & (1<<7)) == (1<<7)) { + window->phys = 0xffc00000; /* 4MiB */ } - if (!window->size) { - printk(KERN_ERR "amd76xrom: cannot reserve rom window\n"); - goto err_out_none; + else { + window->phys = 0xffff0000; /* 64KiB */ + } + window->size = 0xffffffffUL - window->phys + 1UL; + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to a fragment of the window being + * "reseved" by the BIOS. In the case that the + * request_mem_region() fails then once the rom size is + * discovered we will try to reserve the unreserved fragment. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_ERR MOD_NAME + " %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + window->rsrc.start, window->rsrc.end); } -#endif + +#if 0 /* Enable the selected rom window */ pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); + pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); +#endif /* Enable writes through the rom window */ pci_read_config_byte(pdev, 0x40, &byte); @@ -136,78 +141,149 @@ /* FIXME handle registers 0x80 - 0x8C the bios region locks */ - printk(KERN_NOTICE "amd76xrom window : %x at %x\n", - window->size, window->start); /* For write accesses caches are useless */ - info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size); + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); - goto err_out_free_mmio_region; + /* Get the first address to look for an rom chip at */ + map_top = window->phys; +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) { + map_top = 0xffc00000; } - info->mtd = 0; - for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { - char **chip_type; - if (rom_size > window->size) { +#endif + /* Loop through and look for rom chips */ + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); + } + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* There is no generic VPP support */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; } - info->map.map_priv_1 = - info->window_addr + window->size - rom_size; - info->map.size = rom_size; - chip_type = rom_probe_types; - for(; !info->mtd && *chip_type; chip_type++) { - info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); } - if (info->mtd) { - break; + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; } } - if (!info->mtd) { - goto err_out_iounmap; + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; } - printk(KERN_NOTICE "amd76xrom chip at offset: %x\n", - window->size - rom_size); - info->mtd->module = THIS_MODULE; - add_mtd_device(info->mtd); - info->window_start = window->start; - info->window_size = window->size; - return 0; + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } -err_out_iounmap: - iounmap((void *)(info->window_addr)); -err_out_free_mmio_region: - release_mem_region(window->start, window->size); -err_out_none: + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + if (map) { + kfree(map); + } + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + amd76xrom_cleanup(window); return -ENODEV; + } + return 0; } static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) { - struct amd76xrom_map_info *info = &amd76xrom_map; - u8 byte; - - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = 0; - info->map.map_priv_1 = 0; - - iounmap((void *)(info->window_addr)); - info->window_addr = 0; - - /* Disable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte & ~1); + struct amd76xrom_window *window = &amd76xrom_window; - release_mem_region(info->window_start, info->window_size); + amd76xrom_cleanup(window); } -static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = { +static struct pci_device_id amd76xrom_pci_tbl[] = { { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */ { 0, } }; @@ -215,26 +291,25 @@ #if 0 static struct pci_driver amd76xrom_driver = { - name: "amd76xrom", - id_table: amd76xrom_pci_tbl, - probe: amd76xrom_init_one, - remove: amd76xrom_remove_one, + .name = MOD_NAME, + .id_table = amd76xrom_pci_tbl, + .probe = amd76xrom_init_one, + .remove = amd76xrom_remove_one, }; #endif -int __init init_amd76xrom(void) +static int __init init_amd76xrom(void) { struct pci_dev *pdev; struct pci_device_id *id; - pdev = 0; + pdev = NULL; for(id = amd76xrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, 0); + pdev = pci_find_device(id->vendor, id->device, NULL); if (pdev) { break; } } if (pdev) { - amd76xrom_map.pdev = pdev; return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); } return -ENXIO; @@ -245,7 +320,7 @@ static void __exit cleanup_amd76xrom(void) { - amd76xrom_remove_one(amd76xrom_map.pdev); + amd76xrom_remove_one(amd76xrom_window.pdev); } module_init(init_amd76xrom); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/arctic-mtd.c @@ -0,0 +1,135 @@ +/* + * $Id: arctic-mtd.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ + * + * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for + * IBM 405LP Arctic boards. + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + * modified for Arctic by, + * David Gibson + * IBM OzLabs, Canberra, Australia + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) + * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) + * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) + * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) + */ + +#define FFS1_SIZE 0x01000000 /* 16MiB */ +#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ +#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ +#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ + + +#define NAME "Arctic Linux Flash" +#define PADDR SUBZERO_BOOTFLASH_PADDR +#define BUSWIDTH 2 +#define SIZE SUBZERO_BOOTFLASH_SIZE +#define PARTITIONS 4 + +/* Flash memories on these boards are memory resources, accessed big-endian. */ + +{ + /* do nothing for now */ +} + +static struct map_info arctic_mtd_map = { + .name = NAME, + .size = SIZE, + .bankwidth = BUSWIDTH, + .phys = PADDR, +}; + +static struct mtd_info *arctic_mtd; + +static struct mtd_partition arctic_partitions[PARTITIONS] = { + { .name = "Filesystem", + .size = FFS1_SIZE, + .offset = 0,}, + { .name = "Kernel", + .size = KERNEL_SIZE, + .offset = FFS1_SIZE,}, + { .name = "Filesystem", + .size = FFS2_SIZE, + .offset = FFS1_SIZE + KERNEL_SIZE,}, + { .name = "Firmware", + .size = FIRMWARE_SIZE, + .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, +}; + +static int __init +init_arctic_mtd(void) +{ + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); + + arctic_mtd_map.virt = ioremap(PADDR, SIZE); + + if (!arctic_mtd_map.virt) { + printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); + return -EIO; + } + simple_map_init(&arctic_mtd_map); + + printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); + arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); + + if (!arctic_mtd) + return -ENXIO; + + arctic_mtd->owner = THIS_MODULE; + + return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); +} + +static void __exit +cleanup_arctic_mtd(void) +{ + if (arctic_mtd) { + del_mtd_partitions(arctic_mtd); + map_destroy(arctic_mtd); + iounmap((void *) arctic_mtd_map.virt); + } +} + +module_init(init_arctic_mtd); +module_exit(cleanup_arctic_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Gibson "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); --- linux-2.4.21/drivers/mtd/maps/autcpu12-nvram.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/autcpu12-nvram.c @@ -2,7 +2,7 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ + * $Id: autcpu12-nvram.c,v 1.8 2004/11/04 13:24:14 gleixner 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 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,80 +33,27 @@ #include #include -__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); - from++; - to++; - len--; - } -} static struct mtd_info *sram_mtd; struct map_info autcpu12_sram_map = { - name: "SRAM", - size: 32768, - buswidth: 8, - read8: autcpu12_read8, - read16: autcpu12_read16, - read32: autcpu12_read32, - copy_from: autcpu12_copy_from, - write8: autcpu12_write8, - write16: autcpu12_write16, - write32: autcpu12_write32, - copy_to: autcpu12_copy_to + .name = "SRAM", + .size = 32768, + .bankwidth = 4, + .phys = 0x12000000, }; static int __init init_autcpu12_sram (void) { int err, save0, save1; - autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); - if (!autcpu12_sram_map.map_priv_1) { + autcpu12_sram_map.virt = ioremap(0x12000000, SZ_128K); + if (!autcpu12_sram_map.virt) { printk("Failed to ioremap autcpu12 NV-RAM space\n"); err = -EIO; goto out; } + simple_map_init(&autcpu_sram_map); /* * Check for 32K/128K @@ -115,20 +63,20 @@ * Read and check result on ofs 0x0 * Restore contents */ - save0 = autcpu12_read32(&autcpu12_sram_map,0); - save1 = autcpu12_read32(&autcpu12_sram_map,0x10000); - autcpu12_write32(&autcpu12_sram_map,~save0,0x10000); + save0 = map_read32(&autcpu12_sram_map,0); + save1 = map_read32(&autcpu12_sram_map,0x10000); + map_write32(&autcpu12_sram_map,~save0,0x10000); /* if we find this pattern on 0x0, we have 32K size * restore contents and exit */ - if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) { - autcpu12_write32(&autcpu12_sram_map,save0,0x0); + if ( map_read32(&autcpu12_sram_map,0) != save0) { + map_write32(&autcpu12_sram_map,save0,0x0); goto map; } /* We have a 128K found, restore 0x10000 and set size * to 128K */ - autcpu12_write32(&autcpu12_sram_map,save1,0x10000); + map_write32(&autcpu12_sram_map,save1,0x10000); autcpu12_sram_map.size = SZ_128K; map: @@ -139,7 +87,7 @@ goto out_ioremap; } - sram_mtd->module = THIS_MODULE; + sram_mtd->owner = THIS_MODULE; sram_mtd->erasesize = 16; if (add_mtd_device(sram_mtd)) { @@ -148,7 +96,7 @@ goto out_probe; } - printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); return 0; @@ -157,7 +105,7 @@ sram_mtd = 0; out_ioremap: - iounmap((void *)autcpu12_sram_map.map_priv_1); + iounmap((void *)autcpu12_sram_map.virt); out: return err; } @@ -167,7 +115,7 @@ if (sram_mtd) { del_mtd_device(sram_mtd); map_destroy(sram_mtd); - iounmap((void *)autcpu12_sram_map.map_priv_1); + iounmap((void *)autcpu12_sram_map.virt); } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/bast-flash.c @@ -0,0 +1,230 @@ +/* linux/drivers/mtd/maps/bast_flash.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks + * + * Simtec Bast (EB2410ITX) NOR MTD Mapping driver + * + * Changelog: + * 20-Sep-2004 BJD Initial version + * 17-Jan-2005 BJD Add whole device if no partitions found + * + * $Id: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MTD_BAST_MAXSIZE +#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) +#else +#define AREA_MAXSIZE (32 * SZ_1M) +#endif + +#define PFX "bast-flash: " + +struct bast_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *area; +}; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static struct bast_flash_info *to_bast_info(struct device *dev) +{ + return (struct bast_flash_info *)dev_get_drvdata(dev); +} + +static void bast_flash_setrw(int to) +{ + unsigned int val; + unsigned long flags; + + local_irq_save(flags); + val = __raw_readb(BAST_VA_CTRL3); + + if (to) + val |= BAST_CPLD_CTRL3_ROMWEN; + else + val &= ~BAST_CPLD_CTRL3_ROMWEN; + + pr_debug("new cpld ctrl3=%02x\n", val); + + __raw_writeb(val, BAST_VA_CTRL3); + local_irq_restore(flags); +} + +static int bast_flash_remove(struct device *dev) +{ + struct bast_flash_info *info = to_bast_info(dev); + + dev_set_drvdata(dev, NULL); + + if (info == NULL) + return 0; + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + + if (info->partitions) + kfree(info->partitions); + + if (info->area) { + release_resource(info->area); + kfree(info->area); + } + + kfree(info); + + return 0; +} + +static int bast_flash_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bast_flash_info *info; + struct resource *res; + int err = 0; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR PFX "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info, sizeof(*info)); + dev_set_drvdata(dev, info); + + res = pdev->resource; /* assume that the flash has one resource */ + + info->map.phys = res->start; + info->map.size = res->end - res->start + 1; + info->map.name = dev->bus_id; + info->map.bankwidth = 2; + + if (info->map.size > AREA_MAXSIZE) + info->map.size = AREA_MAXSIZE; + + pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__, + info->map.phys, info->map.size); + + info->area = request_mem_region(res->start, info->map.size, + pdev->name); + if (info->area == NULL) { + printk(KERN_ERR PFX "cannot reserve flash memory region\n"); + err = -ENOENT; + goto exit_error; + } + + info->map.virt = ioremap(res->start, info->map.size); + pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt); + + if (info->map.virt == 0) { + printk(KERN_ERR PFX "failed to ioremap() region\n"); + err = -EIO; + goto exit_error; + } + + simple_map_init(&info->map); + + /* enable the write to the flash area */ + + bast_flash_setrw(1); + + /* probe for the device(s) */ + + info->mtd = do_map_probe("jedec_probe", &info->map); + if (info->mtd == NULL) + info->mtd = do_map_probe("cfi_probe", &info->map); + + if (info->mtd == NULL) { + printk(KERN_ERR PFX "map_probe() failed\n"); + err = -ENXIO; + goto exit_error; + } + + /* mark ourselves as the owner */ + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if (err) + printk(KERN_ERR PFX "cannot add/parse partitions\n"); + } else { + err = add_mtd_device(info->mtd); + } + + if (err == 0) + return 0; + + /* fall through to exit error */ + + exit_error: + bast_flash_remove(dev); + return err; +} + +static struct device_driver bast_flash_driver = { + .name = "bast-nor", + .bus = &platform_bus_type, + .probe = bast_flash_probe, + .remove = bast_flash_remove, +}; + +static int __init bast_flash_init(void) +{ + printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n"); + return driver_register(&bast_flash_driver); +} + +static void __exit bast_flash_exit(void) +{ + driver_unregister(&bast_flash_driver); +} + +module_init(bast_flash_init); +module_exit(bast_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("BAST MTD Map driver"); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/beech-mtd.c @@ -0,0 +1,112 @@ +/* + * $Id: beech-mtd.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $ + * + * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for + * IBM 405LP Beech boards. + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define NAME "Beech Linux Flash" +#define PADDR BEECH_BIGFLASH_PADDR +#define SIZE BEECH_BIGFLASH_SIZE +#define BUSWIDTH 1 + +/* Flash memories on these boards are memory resources, accessed big-endian. */ + + +static struct map_info beech_mtd_map = { + .name = NAME, + .size = SIZE, + .bankwidth = BUSWIDTH, + .phys = PADDR +}; + +static struct mtd_info *beech_mtd; + +static struct mtd_partition beech_partitions[2] = { + { + .name = "Linux Kernel", + .size = BEECH_KERNEL_SIZE, + .offset = BEECH_KERNEL_OFFSET + }, { + .name = "Free Area", + .size = BEECH_FREE_AREA_SIZE, + .offset = BEECH_FREE_AREA_OFFSET + } +}; + +static int __init +init_beech_mtd(void) +{ + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); + + beech_mtd_map.virt = ioremap(PADDR, SIZE); + + if (!beech_mtd_map.virt) { + printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); + return -EIO; + } + + simple_map_init(&beech_mtd_map); + + printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); + beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); + + if (!beech_mtd) + return -ENXIO; + + beech_mtd->owner = THIS_MODULE; + + return add_mtd_partitions(beech_mtd, beech_partitions, 2); +} + +static void __exit +cleanup_beech_mtd(void) +{ + if (beech_mtd) { + del_mtd_partitions(beech_mtd); + map_destroy(beech_mtd); + iounmap((void *) beech_mtd_map.virt); + } +} + +module_init(init_beech_mtd); +module_exit(cleanup_beech_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bishop Brock "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); --- linux-2.4.21/drivers/mtd/maps/cdb89712.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/cdb89712.c @@ -1,13 +1,14 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $ + * $Id: cdb89712.c,v 1.10 2004/11/04 13:24:14 gleixner Exp $ */ #include #include #include #include +#include #include #include #include @@ -16,77 +17,21 @@ -__u8 cdb89712_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 cdb89712_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 cdb89712_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to); - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); - from++; - to++; - len--; - } -} - static struct mtd_info *flash_mtd; struct map_info cdb89712_flash_map = { - name: "flash", - size: FLASH_SIZE, - buswidth: FLASH_WIDTH, - read8: cdb89712_read8, - read16: cdb89712_read16, - read32: cdb89712_read32, - copy_from: cdb89712_copy_from, - write8: cdb89712_write8, - write16: cdb89712_write16, - write32: cdb89712_write32, - copy_to: cdb89712_copy_to + .name = "flash", + .size = FLASH_SIZE, + .bankwidth = FLASH_WIDTH, + .phys = FLASH_START, }; struct resource cdb89712_flash_resource = { - name: "Flash", - start: FLASH_START, - end: FLASH_START + FLASH_SIZE - 1, - flags: IORESOURCE_IO | IORESOURCE_BUSY, + .name = "Flash", + .start = FLASH_START, + .end = FLASH_START + FLASH_SIZE - 1, + .flags = IORESOURCE_IO | IORESOURCE_BUSY, }; static int __init init_cdb89712_flash (void) @@ -99,13 +44,13 @@ goto out; } - cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); - if (!cdb89712_flash_map.map_priv_1) { + cdb89712_flash_map.virt = ioremap(FLASH_START, FLASH_SIZE); + if (!cdb89712_flash_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); err = -EIO; goto out_resource; } - + simple_map_init(&cdb89712_flash_map); flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); if (!flash_mtd) { flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); @@ -118,7 +63,7 @@ goto out_ioremap; } - flash_mtd->module = THIS_MODULE; + flash_mtd->owner = THIS_MODULE; if (add_mtd_device(flash_mtd)) { printk("FLASH device addition failed\n"); @@ -132,7 +77,7 @@ map_destroy(flash_mtd); flash_mtd = 0; out_ioremap: - iounmap((void *)cdb89712_flash_map.map_priv_1); + iounmap((void *)cdb89712_flash_map.virt); out_resource: release_resource (&cdb89712_flash_resource); out: @@ -146,24 +91,17 @@ static struct mtd_info *sram_mtd; struct map_info cdb89712_sram_map = { - name: "SRAM", - size: SRAM_SIZE, - buswidth: SRAM_WIDTH, - read8: cdb89712_read8, - read16: cdb89712_read16, - read32: cdb89712_read32, - copy_from: cdb89712_copy_from, - write8: cdb89712_write8, - write16: cdb89712_write16, - write32: cdb89712_write32, - copy_to: cdb89712_copy_to + .name = "SRAM", + .size = SRAM_SIZE, + .bankwidth = SRAM_WIDTH, + .phys = SRAM_START, }; struct resource cdb89712_sram_resource = { - name: "SRAM", - start: SRAM_START, - end: SRAM_START + SRAM_SIZE - 1, - flags: IORESOURCE_IO | IORESOURCE_BUSY, + .name = "SRAM", + .start = SRAM_START, + .end = SRAM_START + SRAM_SIZE - 1, + .flags = IORESOURCE_IO | IORESOURCE_BUSY, }; static int __init init_cdb89712_sram (void) @@ -176,13 +114,13 @@ goto out; } - cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); - if (!cdb89712_sram_map.map_priv_1) { + cdb89712_sram_map.virt = ioremap(SRAM_START, SRAM_SIZE); + if (!cdb89712_sram_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); err = -EIO; goto out_resource; } - + simple_map_init(&cdb89712_sram_map); sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); if (!sram_mtd) { printk("SRAM probe failed\n"); @@ -190,7 +128,7 @@ goto out_ioremap; } - sram_mtd->module = THIS_MODULE; + sram_mtd->owner = THIS_MODULE; sram_mtd->erasesize = 16; if (add_mtd_device(sram_mtd)) { @@ -205,7 +143,7 @@ map_destroy(sram_mtd); sram_mtd = 0; out_ioremap: - iounmap((void *)cdb89712_sram_map.map_priv_1); + iounmap((void *)cdb89712_sram_map.virt); out_resource: release_resource (&cdb89712_sram_resource); out: @@ -221,20 +159,17 @@ static struct mtd_info *bootrom_mtd; struct map_info cdb89712_bootrom_map = { - name: "BootROM", - size: BOOTROM_SIZE, - buswidth: BOOTROM_WIDTH, - read8: cdb89712_read8, - read16: cdb89712_read16, - read32: cdb89712_read32, - copy_from: cdb89712_copy_from, + .name = "BootROM", + .size = BOOTROM_SIZE, + .bankwidth = BOOTROM_WIDTH, + .phys = BOOTROM_START, }; struct resource cdb89712_bootrom_resource = { - name: "BootROM", - start: BOOTROM_START, - end: BOOTROM_START + BOOTROM_SIZE - 1, - flags: IORESOURCE_IO | IORESOURCE_BUSY, + .name = "BootROM", + .start = BOOTROM_START, + .end = BOOTROM_START + BOOTROM_SIZE - 1, + .flags = IORESOURCE_IO | IORESOURCE_BUSY, }; static int __init init_cdb89712_bootrom (void) @@ -247,13 +182,13 @@ goto out; } - cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); - if (!cdb89712_bootrom_map.map_priv_1) { + cdb89712_bootrom_map.virt = ioremap(BOOTROM_START, BOOTROM_SIZE); + if (!cdb89712_bootrom_map.virt) { printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); err = -EIO; goto out_resource; } - + simple_map_init(&cdb89712_bootrom_map); bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); if (!bootrom_mtd) { printk("BootROM probe failed\n"); @@ -261,7 +196,7 @@ goto out_ioremap; } - bootrom_mtd->module = THIS_MODULE; + bootrom_mtd->owner = THIS_MODULE; bootrom_mtd->erasesize = 0x10000; if (add_mtd_device(bootrom_mtd)) { @@ -276,7 +211,7 @@ map_destroy(bootrom_mtd); bootrom_mtd = 0; out_ioremap: - iounmap((void *)cdb89712_bootrom_map.map_priv_1); + iounmap((void *)cdb89712_bootrom_map.virt); out_resource: release_resource (&cdb89712_bootrom_resource); out: @@ -306,21 +241,21 @@ if (sram_mtd) { del_mtd_device(sram_mtd); map_destroy(sram_mtd); - iounmap((void *)cdb89712_sram_map.map_priv_1); + iounmap((void *)cdb89712_sram_map.virt); release_resource (&cdb89712_sram_resource); } if (flash_mtd) { del_mtd_device(flash_mtd); map_destroy(flash_mtd); - iounmap((void *)cdb89712_flash_map.map_priv_1); + iounmap((void *)cdb89712_flash_map.virt); release_resource (&cdb89712_flash_resource); } if (bootrom_mtd) { del_mtd_device(bootrom_mtd); map_destroy(bootrom_mtd); - iounmap((void *)cdb89712_bootrom_map.map_priv_1); + iounmap((void *)cdb89712_bootrom_map.virt); release_resource (&cdb89712_bootrom_resource); } } --- linux-2.4.21/drivers/mtd/maps/ceiva.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/ceiva.c @@ -11,7 +11,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $ */ #include @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,62 +32,10 @@ #include /* - * This isnt complete yet, so... + * This isn't complete yet, so... */ #define CONFIG_MTD_CEIVA_STATICMAP -static __u8 clps_read8(struct map_info *map, unsigned long ofs) -{ - return readb(map->map_priv_1 + ofs); -} - -static __u16 clps_read16(struct map_info *map, unsigned long ofs) -{ - return readw(map->map_priv_1 + ofs); -} - -static __u32 clps_read32(struct map_info *map, unsigned long ofs) -{ - return readl(map->map_priv_1 + ofs); -} - -static void clps_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 clps_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -static void clps_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 clps_map __initdata = { - name: "clps flash", - read8: clps_read8, - read16: clps_read16, - read32: clps_read32, - copy_from: clps_copy_from, - write8: clps_write8, - write16: clps_write16, - write32: clps_write32, - copy_to: clps_copy_to, -}; - #ifdef CONFIG_MTD_CEIVA_STATICMAP /* * See include/linux/mtd/partitions.h for definition of the mtd_partition @@ -115,23 +64,23 @@ static struct mtd_partition ceiva_partitions[] = { { - name: "Ceiva BOOT partition", - size: BOOT_PARTITION_SIZE_KiB*1024, - offset: 0, + .name = "Ceiva BOOT partition", + .size = BOOT_PARTITION_SIZE_KiB*1024, + .offset = 0, },{ - name: "Ceiva parameters partition", - size: PARAMS_PARTITION_SIZE_KiB*1024, - offset: (16 + 8) * 1024, + .name = "Ceiva parameters partition", + .size = PARAMS_PARTITION_SIZE_KiB*1024, + .offset = (16 + 8) * 1024, },{ - name: "Ceiva kernel partition", - size: (KERNEL_PARTITION_SIZE_KiB)*1024, - offset: 0x20000, + .name = "Ceiva kernel partition", + .size = (KERNEL_PARTITION_SIZE_KiB)*1024, + .offset = 0x20000, },{ - name: "Ceiva root filesystem partition", - offset: MTDPART_OFS_APPEND, - size: (ROOT_PARTITION_SIZE_KiB)*1024, + .name = "Ceiva root filesystem partition", + .offset = MTDPART_OFS_APPEND, + .size = (ROOT_PARTITION_SIZE_KiB)*1024, } }; #endif @@ -176,7 +125,7 @@ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); if (!maps) return -ENOMEM; - + memset(maps, 0, sizeof(struct map_info) * nr); /* * Claim and then map the memory regions. */ @@ -191,7 +140,9 @@ } clps[i].map = maps + i; - memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); + + clps[i].map->name = "clps flash"; + clps[i].map->phys = clps[i].base; clps[i].vbase = ioremap(clps[i].base, clps[i].size); if (!clps[i].vbase) { @@ -199,16 +150,18 @@ break; } - clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; - clps[i].map->buswidth = clps[i].width; + clps[i].map->virt = (void __iomem *)clps[i].vbase; + clps[i].map->bankwidth = clps[i].width; clps[i].map->size = clps[i].size; + simple_map_init(&clps[i].map); + clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); if (clps[i].mtd == NULL) { ret = -ENXIO; break; } - clps[i].mtd->module = THIS_MODULE; + clps[i].mtd->owner = THIS_MODULE; subdev[i] = clps[i].mtd; printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " @@ -318,10 +271,8 @@ return nr; } -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); -extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); - static struct mtd_partition *parsed_parts; +static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; static void __init clps_locate_partitions(struct mtd_info *mtd) { @@ -331,20 +282,11 @@ /* * Partition selection stuff. */ -#ifdef CONFIG_MTD_CMDLINE_PARTS - nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps"); + nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); if (nr_parts > 0) { part_type = "command line"; break; } -#endif -#ifdef CONFIG_MTD_REDBOOT_PARTS - nr_parts = parse_redboot_partitions(mtd, &parsed_parts); - if (nr_parts > 0) { - part_type = "RedBoot"; - break; - } -#endif #ifdef CONFIG_MTD_CEIVA_STATICMAP nr_parts = clps_static_partitions(&parsed_parts); if (nr_parts > 0) { --- linux-2.4.21/drivers/mtd/maps/cfi_flagadm.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/cfi_flagadm.c @@ -1,7 +1,7 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíđsson * - * $Id: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: cfi_flagadm.c,v 1.14 2004/11/04 13:24:14 gleixner 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 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -55,83 +56,33 @@ #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 + .name = "FlagaDM flash device", + .size = FLASH_SIZE, + .bankwidth = 2, }; struct mtd_partition flagadm_parts[] = { { - name : "Bootloader", - offset : FLASH_PARTITION0_ADDR, - size : FLASH_PARTITION0_SIZE + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE }, { - name : "Kernel image", - offset : FLASH_PARTITION1_ADDR, - size : FLASH_PARTITION1_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 = "Initial ramdisk image", + .offset = FLASH_PARTITION2_ADDR, + .size = FLASH_PARTITION2_SIZE }, { - name : "Persistant storage", - offset : FLASH_PARTITION3_ADDR, - size : FLASH_PARTITION3_SIZE + .name = "Persistant storage", + .offset = FLASH_PARTITION3_ADDR, + .size = FLASH_PARTITION3_SIZE } }; @@ -144,22 +95,26 @@ 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, + flagadm_map.phys = FLASH_PHYS_ADDR; + flagadm_map.virt = ioremap(FLASH_PHYS_ADDR, FLASH_SIZE); - if (!flagadm_map.map_priv_1) { + if (!flagadm_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + + simple_map_init(&flagadm_map); + mymtd = do_map_probe("cfi_probe", &flagadm_map); if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = 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); + iounmap((void *)flagadm_map.virt); return -ENXIO; } @@ -169,9 +124,9 @@ 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; + if (flagadm_map.virt) { + iounmap((void *)flagadm_map.virt); + flagadm_map.virt = 0; } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/chestnut.c @@ -0,0 +1,91 @@ +/* + * drivers/mtd/maps/chestnut.c + * + * $Id: chestnut.c,v 1.1 2005/01/05 16:59:50 dwmw2 Exp $ + * + * Flash map driver for IBM Chestnut (750FXGX Eval) + * + * Chose not to enable 8 bit flash as it contains the firmware and board + * info. Thus only the 32bit flash is supported. + * + * Author: + * + * 2004 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct map_info chestnut32_map = { + .name = "User FS", + .size = CHESTNUT_32BIT_SIZE, + .bankwidth = 4, + .phys = CHESTNUT_32BIT_BASE, +}; + +static struct mtd_partition chestnut32_partitions[] = { + { + .name = "User FS", + .offset = 0, + .size = CHESTNUT_32BIT_SIZE, + } +}; + +static struct mtd_info *flash32; + +int __init init_chestnut(void) +{ + /* 32-bit FLASH */ + + chestnut32_map.virt = ioremap(chestnut32_map.phys, chestnut32_map.size); + + if (!chestnut32_map.virt) { + printk(KERN_NOTICE "Failed to ioremap 32-bit flash\n"); + return -EIO; + } + + simple_map_init(&chestnut32_map); + + flash32 = do_map_probe("cfi_probe", &chestnut32_map); + if (flash32) { + flash32->owner = THIS_MODULE; + add_mtd_partitions(flash32, chestnut32_partitions, + ARRAY_SIZE(chestnut32_partitions)); + } else { + printk(KERN_NOTICE "map probe failed for 32-bit flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit +cleanup_chestnut(void) +{ + if (flash32) { + del_mtd_partitions(flash32); + map_destroy(flash32); + } + + if (chestnut32_map.virt) { + iounmap((void *)chestnut32_map.virt); + chestnut32_map.virt = 0; + } +} + +module_init(init_chestnut); +module_exit(cleanup_chestnut); + +MODULE_DESCRIPTION("MTD map and partitions for IBM Chestnut (750fxgx Eval)"); +MODULE_AUTHOR(""); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/maps/cstm_mips_ixx.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/cstm_mips_ixx.c @@ -1,5 +1,5 @@ /* - * $Id: cstm_mips_ixx.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: cstm_mips_ixx.c,v 1.13 2005/01/12 22:34:35 gleixner Exp $ * * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. * Config with both CFI and JEDEC device support. @@ -33,55 +33,13 @@ #include #include #include +#include #include #include #include #include #include - -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) #include -#endif - -__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(map->map_priv_1 + ofs); -} - -__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(map->map_priv_1 + ofs); -} - -__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) -{ - return *(__u32 *)(map->map_priv_1 + ofs); -} - -void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) #define CC_GCR 0xB4013818 @@ -97,10 +55,17 @@ #define CC_GPAICR 0xB4013804 #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) { + static DEFINE_SPINLOCK(vpp_lock); + static int vpp_count = 0; + unsigned long flags; + + spin_lock_irqsave(&vpp_lock, flags); + if (vpp) { -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + if (!vpp_count++) { __u16 data; __u8 data1; static u8 first = 1; @@ -116,10 +81,9 @@ enabling vpp after powerup */ udelay(40); } -#endif /* CONFIG_MIPS_ITE8172 */ } - else { -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + } else { + if (!--vpp_count) { __u16 data; // Set GPIO port B pin3 to high @@ -127,26 +91,11 @@ data = (data & 0xff3f) | 0x0040; *(__u16 *)CC_GPBCR = data; *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; -#endif /* CONFIG_MIPS_ITE8172 */ } + } + spin_unlock_irqrestore(&vpp_lock, flags); } - -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 -}; +#endif /* board and partition description */ @@ -155,7 +104,7 @@ char *name; unsigned long window_addr; unsigned long window_size; - int buswidth; + int bankwidth; int num_partitions; }; @@ -167,7 +116,7 @@ "big flash", // name 0x08000000, // window_addr 0x02000000, // window_size - 4, // buswidth + 4, // bankwidth 1, // num_partitions } @@ -175,9 +124,9 @@ 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, + .name = "main partition ", + .size = 0x02000000, // 128 x 2 x 128k byte sectors + .offset = 0, }, }, }; @@ -189,7 +138,7 @@ "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 + CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // bankwidth 1, // num_partitions }, @@ -197,9 +146,9 @@ 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, + .name = "main partition", + .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, + .offset = 0, }, }, }; @@ -216,17 +165,24 @@ /* Initialize mapping */ for (i=0;imodule = THIS_MODULE; + mymtd->owner = 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); @@ -266,9 +222,9 @@ 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 (cstm_mips_ixx_map[i].virt) { + iounmap((void *)cstm_mips_ixx_map[i].virt); + cstm_mips_ixx_map[i].virt = 0; } } } --- linux-2.4.21/drivers/mtd/maps/dbox2-flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/dbox2-flash.c @@ -1,37 +1,61 @@ /* - * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: dbox2-flash.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * - * Nokia / Sagem D-Box 2 flash driver + * D-Box 2 flash driver */ #include #include #include +#include #include #include #include #include #include +#include /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the * device. */ -static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw - size: 128 * 1024, - offset: 0, - mask_flags: MTD_WRITEABLE}, - {name: "PPC bootloader", // flfs - size: 128 * 1024, - offset: MTDPART_OFS_APPEND, - mask_flags: 0}, - {name: "Kernel", // idxfs - size: 768 * 1024, - offset: MTDPART_OFS_APPEND, - mask_flags: 0}, - {name: "System", // jffs - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, - mask_flags: 0}}; +static struct mtd_partition partition_info[]= { + { + .name = "BR bootloader", + .size = 128 * 1024, + .offset = 0, + .mask_flags = MTD_WRITEABLE + }, + { + .name = "FLFS (U-Boot)", + .size = 128 * 1024, + .offset = MTDPART_OFS_APPEND, + .mask_flags = 0 + }, + { + .name = "Root (SquashFS)", + .size = 7040 * 1024, + .offset = MTDPART_OFS_APPEND, + .mask_flags = 0 + }, + { + .name = "var (JFFS2)", + .size = 896 * 1024, + .offset = MTDPART_OFS_APPEND, + .mask_flags = 0 + }, + { + .name = "Flash without bootloader", + .size = MTDPART_SIZ_FULL, + .offset = 128 * 1024, + .mask_flags = 0 + }, + { + .name = "Complete Flash", + .size = MTDPART_SIZ_FULL, + .offset = 0, + .mask_flags = MTD_WRITEABLE + } +}; #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) @@ -40,84 +64,36 @@ 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 + .name = "D-Box 2 flash memory", + .size = WINDOW_SIZE, + .bankwidth = 4, + .phys = WINDOW_ADDR, }; int __init 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); + dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!dbox2_flash_map.map_priv_1) { + if (!dbox2_flash_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + simple_map_init(&dbox2_flash_map); // Probe for dual Intel 28F320 or dual AMD mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); if (!mymtd) { // Probe for single Intel 28F640 - dbox2_flash_map.buswidth = 2; + dbox2_flash_map.bankwidth = 2; mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; /* Create MTD devices for each partition. */ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); @@ -125,7 +101,7 @@ return 0; } - iounmap((void *)dbox2_flash_map.map_priv_1); + iounmap((void *)dbox2_flash_map.virt); return -ENXIO; } @@ -135,9 +111,9 @@ 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; + if (dbox2_flash_map.virt) { + iounmap((void *)dbox2_flash_map.virt); + dbox2_flash_map.virt = 0; } } @@ -146,5 +122,5 @@ MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kári Davíđsson "); -MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); +MODULE_AUTHOR("Kári Davíđsson , Bastian Blank , Alexander Wild "); +MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); --- linux-2.4.21/drivers/mtd/maps/dc21285.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/dc21285.c @@ -5,12 +5,14 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ + * $Id: dc21285.c,v 1.22 2004/11/01 13:39:21 rmk Exp $ */ #include #include #include #include +#include +#include #include #include @@ -18,143 +20,199 @@ #include #include +#include -static struct mtd_info *mymtd; +static struct mtd_info *dc21285_mtd; -__u8 dc21285_read8(struct map_info *map, unsigned long ofs) +#ifdef CONFIG_ARCH_NETWINDER +/* + * This is really ugly, but it seams to be the only + * realiable way to do it, as the cpld state machine + * is unpredictible. So we have a 25us penalty per + * write access. + */ +static void nw_en_write(void) { - return *(__u8*)(map->map_priv_1 + ofs); + extern spinlock_t gpio_lock; + unsigned long flags; + + /* + * we want to write a bit pattern XXX1 to Xilinx to enable + * the write gate, which will be open for about the next 2ms. + */ + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(1, 1); + spin_unlock_irqrestore(&gpio_lock, flags); + + /* + * let the ISA bus to catch on... + */ + udelay(25); } +#else +#define nw_en_write() do { } while (0) +#endif -__u16 dc21285_read16(struct map_info *map, unsigned long ofs) +static map_word dc21285_read8(struct map_info *map, unsigned long ofs) { - return *(__u16*)(map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(uint8_t*)(map->virt + ofs); + return val; } -__u32 dc21285_read32(struct map_info *map, unsigned long ofs) +static map_word dc21285_read16(struct map_info *map, unsigned long ofs) { - return *(__u32*)(map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(uint16_t*)(map->virt + ofs); + return val; } -void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static map_word dc21285_read32(struct map_info *map, unsigned long ofs) { - memcpy(to, (void*)(map->map_priv_1 + from), len); + map_word val; + val.x[0] = *(uint32_t*)(map->virt + ofs); + return val; } -void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) +static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { + memcpy(to, (void*)(map->virt + from), len); +} + +static void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr) +{ + if (machine_is_netwinder()) + nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(__u8*)(map->map_priv_1 + adr) = d; + *(uint8_t*)(map->virt + adr) = d.x[0]; } -void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) +static void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr) { + if (machine_is_netwinder()) + nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(__u16*)(map->map_priv_1 + adr) = d; + *(uint16_t*)(map->virt + adr) = d.x[0]; } -void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) +static void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr) { - *(__u32*)(map->map_priv_1 + adr) = d; + if (machine_is_netwinder()) + nw_en_write(); + *(uint32_t*)(map->virt + adr) = d.x[0]; } -void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void dc21285_copy_to_32(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)++; + map_word d; + d.x[0] = *((uint32_t*)from)++; dc21285_write32(map, d, to); to += 4; len -= 4; } - break; - case 2: +} + +static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ while (len > 0) { - __u16 d = *((__u16*)from)++; + map_word d; + d.x[0] = *((uint16_t*)from)++; dc21285_write16(map, d, to); to += 2; len -= 2; } - break; - case 1: - while (len > 0) { - __u8 d = *((__u8*)from)++; +} + +static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + map_word d; + d.x[0] = *((uint8_t*)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 +static struct map_info dc21285_map = { + .name = "DC21285 flash", + .phys = NO_XIP, + .size = 16*1024*1024, + .copy_from = dc21285_copy_from, }; /* Partition stuff */ +#ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition *dc21285_parts; +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif -extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); - -int __init init_dc21285(void) +static int __init init_dc21285(void) { - /* Determine buswidth */ + +#ifdef CONFIG_MTD_PARTITIONS + int nrparts; +#endif + + /* Determine bankwidth */ switch (*CSR_SA110_CNTL & (3<<14)) { case SA110_CNTL_ROMWIDTH_8: - dc21285_map.buswidth = 1; + dc21285_map.bankwidth = 1; + dc21285_map.read = dc21285_read8; + dc21285_map.write = dc21285_write8; + dc21285_map.copy_to = dc21285_copy_to_8; break; case SA110_CNTL_ROMWIDTH_16: - dc21285_map.buswidth = 2; + dc21285_map.bankwidth = 2; + dc21285_map.read = dc21285_read16; + dc21285_map.write = dc21285_write16; + dc21285_map.copy_to = dc21285_copy_to_16; break; case SA110_CNTL_ROMWIDTH_32: - dc21285_map.buswidth = 4; + dc21285_map.bankwidth = 4; + dc21285_map.read = dc21285_read32; + dc21285_map.write = dc21285_write32; + dc21285_map.copy_to = dc21285_copy_to_32; break; default: - printk (KERN_ERR "DC21285 flash: undefined buswidth\n"); + printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); return -ENXIO; } - printk (KERN_NOTICE "DC21285 flash support (%d-bit buswidth)\n", - dc21285_map.buswidth*8); + printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n", + dc21285_map.bankwidth*8); /* Let's map the flash area */ - dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); - if (!dc21285_map.map_priv_1) { + dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024); + if (!dc21285_map.virt) { printk("Failed to ioremap\n"); return -EIO; } - mymtd = do_map_probe("cfi_probe", &dc21285_map); - if (mymtd) { - int nrparts = 0; + if (machine_is_ebsa285()) { + dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); + } else { + dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); + } - mymtd->module = THIS_MODULE; + if (!dc21285_mtd) { + iounmap(dc21285_map.virt); + return -ENXIO; + } - /* partition fixup */ + dc21285_mtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_REDBOOT_PARTS - nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); +#ifdef CONFIG_MTD_PARTITIONS + nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0); + if (nrparts > 0) + add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts); + else #endif - if (nrparts > 0) { - add_mtd_partitions(mymtd, dc21285_parts, nrparts); - } else if (nrparts == 0) { - printk(KERN_NOTICE "RedBoot partition table failed\n"); - add_mtd_device(mymtd); - } + add_mtd_device(dc21285_mtd); + if(machine_is_ebsa285()) { /* * Flash timing is determined with bits 19-16 of the * CSR_SA110_CNTL. The value is the number of wait cycles, or @@ -167,27 +225,23 @@ *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; + return 0; } 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) +#ifdef CONFIG_MTD_PARTITIONS + if (dc21285_parts) { + del_mtd_partitions(dc21285_mtd); kfree(dc21285_parts); + } else +#endif + del_mtd_device(dc21285_mtd); + + map_destroy(dc21285_mtd); + iounmap(dc21285_map.virt); } module_init(init_dc21285); --- linux-2.4.21/drivers/mtd/maps/dilnetpc.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/dilnetpc.c @@ -14,7 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $ + * $Id: dilnetpc.c,v 1.18 2005/01/12 22:34:35 gleixner Exp $ * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #include /* -** The DIL/NetPC keeps it's BIOS in two distinct flash blocks. +** The DIL/NetPC keeps its BIOS in two distinct flash blocks. ** Destroying any of these blocks transforms the DNPC into ** a paperweight (albeit not a very useful one, considering ** it only weighs a few grams). @@ -189,45 +190,6 @@ } -static __u8 dnpc_read8(struct map_info *map, unsigned long ofs) -{ - return readb(map->map_priv_1 + ofs); -} - -static __u16 dnpc_read16(struct map_info *map, unsigned long ofs) -{ - return readw(map->map_priv_1 + ofs); -} - -static __u32 dnpc_read32(struct map_info *map, unsigned long ofs) -{ - return readl(map->map_priv_1 + ofs); -} - -static void dnpc_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 dnpc_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -static void dnpc_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); -} /* ************************************************************ @@ -235,7 +197,7 @@ ************************************************************ */ -static spinlock_t dnpc_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dnpc_spin); static int vpp_counter = 0; /* ** This is what has to be done for the DNP board .. @@ -288,19 +250,11 @@ #define WINDOW_ADDR FLASH_BASE static struct map_info dnpc_map = { - name: "ADNP Flash Bank", - size: ADNP_WINDOW_SIZE, - buswidth: 1, - read8: dnpc_read8, - read16: dnpc_read16, - read32: dnpc_read32, - copy_from: dnpc_copy_from, - write8: dnpc_write8, - write16: dnpc_write16, - write32: dnpc_write32, - copy_to: dnpc_copy_to, - set_vpp: adnp_set_vpp, - map_priv_2: WINDOW_ADDR + .name = "ADNP Flash Bank", + .size = ADNP_WINDOW_SIZE, + .bankwidth = 1, + .set_vpp = adnp_set_vpp, + .phys = WINDOW_ADDR }; /* @@ -316,29 +270,29 @@ static struct mtd_partition partition_info[]= { { - name: "ADNP boot", - offset: 0, - size: 0xf0000, + .name = "ADNP boot", + .offset = 0, + .size = 0xf0000, }, { - name: "ADNP system BIOS", - offset: MTDPART_OFS_NXTBLK, - size: 0x10000, + .name = "ADNP system BIOS", + .offset = MTDPART_OFS_NXTBLK, + .size = 0x10000, #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED - mask_flags: MTD_WRITEABLE, + .mask_flags = MTD_WRITEABLE, #endif }, { - name: "ADNP file system", - offset: MTDPART_OFS_NXTBLK, - size: 0x2f0000, + .name = "ADNP file system", + .offset = MTDPART_OFS_NXTBLK, + .size = 0x2f0000, }, { - name: "ADNP system BIOS entry", - offset: MTDPART_OFS_NXTBLK, - size: MTDPART_SIZ_FULL, + .name = "ADNP system BIOS entry", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED - mask_flags: MTD_WRITEABLE, + .mask_flags = MTD_WRITEABLE, #endif }, }; @@ -369,21 +323,21 @@ static struct mtd_partition higlvl_partition_info[]= { { - name: "ADNP boot block", - offset: 0, - size: CONFIG_MTD_DILNETPC_BOOTSIZE, + .name = "ADNP boot block", + .offset = 0, + .size = CONFIG_MTD_DILNETPC_BOOTSIZE, }, { - name: "ADNP file system space", - offset: MTDPART_OFS_NXTBLK, - size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, + .name = "ADNP file system space", + .offset = MTDPART_OFS_NXTBLK, + .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, }, { - name: "ADNP system BIOS + BIOS Entry", - offset: MTDPART_OFS_NXTBLK, - size: MTDPART_SIZ_FULL, + .name = "ADNP system BIOS + BIOS Entry", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED - mask_flags: MTD_WRITEABLE, + .mask_flags = MTD_WRITEABLE, #endif }, }; @@ -447,18 +401,19 @@ } printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", - is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2); + is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); - dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size); + dnpc_map.virt = ioremap_nocache(dnpc_map.phys, dnpc_map.size); - dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size); + dnpc_map_flash(dnpc_map.phys, dnpc_map.size); - if (!dnpc_map.map_priv_1) { + if (!dnpc_map.virt) { printk("Failed to ioremap_nocache\n"); return -EIO; } + simple_map_init(&dnpc_map); - printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1); + printk("FLASH virtual address: 0x%p\n", dnpc_map.virt); mymtd = do_map_probe("jedec_probe", &dnpc_map); @@ -475,11 +430,11 @@ mymtd->erasesize = 0x10000; if (!mymtd) { - iounmap((void *)dnpc_map.map_priv_1); + iounmap(dnpc_map.virt); return -ENXIO; } - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; /* ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions() @@ -525,10 +480,10 @@ del_mtd_partitions(mymtd); map_destroy(mymtd); } - if (dnpc_map.map_priv_1) { - iounmap((void *)dnpc_map.map_priv_1); + if (dnpc_map.virt) { + iounmap(dnpc_map.virt); dnpc_unmap_flash(); - dnpc_map.map_priv_1 = 0; + dnpc_map.virt = NULL; } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/dmv182.c @@ -0,0 +1,149 @@ + +/* + * drivers/mtd/maps/svme182.c + * + * Flash map driver for the Dy4 SVME182 board + * + * $Id: dmv182.c,v 1.5 2004/11/04 13:24:14 gleixner Exp $ + * + * Copyright 2003-2004, TimeSys Corporation + * + * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This driver currently handles only the 16MiB user flash bank 1 on the + * board. It does not provide access to bank 0 (contains the Dy4 FFW), bank 2 + * (VxWorks boot), or the optional 48MiB expansion flash. + * + * scott.wood@timesys.com: On the newer boards with 128MiB flash, it + * now supports the first 96MiB (the boot flash bank containing FFW + * is excluded). The VxWorks loader is in partition 1. + */ + +#define FLASH_BASE_ADDR 0xf0000000 +#define FLASH_BANK_SIZE (128*1024*1024) + +MODULE_AUTHOR("Scott Wood, TimeSys Corporation "); +MODULE_DESCRIPTION("User-programmable flash device on the Dy4 SVME182 board"); +MODULE_LICENSE("GPL"); + +static struct map_info svme182_map = { + .name = "Dy4 SVME182", + .bankwidth = 32, + .size = 128 * 1024 * 1024 +}; + +#define BOOTIMAGE_PART_SIZE ((6*1024*1024)-RESERVED_PART_SIZE) + +// Allow 6MiB for the kernel +#define NEW_BOOTIMAGE_PART_SIZE (6 * 1024 * 1024) +// Allow 1MiB for the bootloader +#define NEW_BOOTLOADER_PART_SIZE (1024 * 1024) +// Use the remaining 9MiB at the end of flash for the RFS +#define NEW_RFS_PART_SIZE (0x01000000 - NEW_BOOTLOADER_PART_SIZE - \ + NEW_BOOTIMAGE_PART_SIZE) + +static struct mtd_partition svme182_partitions[] = { + // The Lower PABS is only 128KiB, but the partition code doesn't + // like partitions that don't end on the largest erase block + // size of the device, even if all of the erase blocks in the + // partition are small ones. The hardware should prevent + // writes to the actual PABS areas. + { + name: "Lower PABS and CPU 0 bootloader or kernel", + size: 6*1024*1024, + offset: 0, + }, + { + name: "Root Filesystem", + size: 10*1024*1024, + offset: MTDPART_OFS_NXTBLK + }, + { + name: "CPU1 Bootloader", + size: 1024*1024, + offset: MTDPART_OFS_NXTBLK, + }, + { + name: "Extra", + size: 110*1024*1024, + offset: MTDPART_OFS_NXTBLK + }, + { + name: "Foundation Firmware and Upper PABS", + size: 1024*1024, + offset: MTDPART_OFS_NXTBLK, + mask_flags: MTD_WRITEABLE // read-only + } +}; + +static struct mtd_info *this_mtd; + +static int __init init_svme182(void) +{ + struct mtd_partition *partitions; + int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition); + + partitions = svme182_partitions; + + svme182_map.virt = ioremap(FLASH_BASE_ADDR, svme182_map.size); + + if (svme182_map.virt == 0) { + printk("Failed to ioremap FLASH memory area.\n"); + return -EIO; + } + + simple_map_init(&svme182_map); + + this_mtd = do_map_probe("cfi_probe", &svme182_map); + if (!this_mtd) + { + iounmap((void *)svme182_map.virt); + return -ENXIO; + } + + printk(KERN_NOTICE "SVME182 flash device: %dMiB at 0x%08x\n", + this_mtd->size >> 20, FLASH_BASE_ADDR); + + this_mtd->owner = THIS_MODULE; + add_mtd_partitions(this_mtd, partitions, num_parts); + + return 0; +} + +static void __exit cleanup_svme182(void) +{ + if (this_mtd) + { + del_mtd_partitions(this_mtd); + map_destroy(this_mtd); + } + + if (svme182_map.virt) + { + iounmap((void *)svme182_map.virt); + svme182_map.virt = 0; + } + + return; +} + +module_init(init_svme182); +module_exit(cleanup_svme182); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ebony.c @@ -0,0 +1,163 @@ +/* + * $Id: ebony.c,v 1.15 2004/12/09 18:39:54 holindho Exp $ + * + * Mapping for Ebony user flash + * + * Matt Porter + * + * Copyright 2002-2004 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash; + +static struct map_info ebony_small_map = { + .name = "Ebony small flash", + .size = EBONY_SMALL_FLASH_SIZE, + .bankwidth = 1, +}; + +static struct map_info ebony_large_map = { + .name = "Ebony large flash", + .size = EBONY_LARGE_FLASH_SIZE, + .bankwidth = 1, +}; + +static struct mtd_partition ebony_small_partitions[] = { + { + .name = "OpenBIOS", + .offset = 0x0, + .size = 0x80000, + } +}; + +static struct mtd_partition ebony_large_partitions[] = { + { + .name = "fs", + .offset = 0, + .size = 0x380000, + }, + { + .name = "firmware", + .offset = 0x380000, + .size = 0x80000, + } +}; + +int __init init_ebony(void) +{ + u8 fpga0_reg; + u8 __iomem *fpga0_adr; + unsigned long long small_flash_base, large_flash_base; + + fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); + if (!fpga0_adr) + return -ENOMEM; + + fpga0_reg = readb(fpga0_adr); + iounmap(fpga0_adr); + + if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_HIGH2; + else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_HIGH1; + else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_LOW2; + else + small_flash_base = EBONY_SMALL_FLASH_LOW1; + + if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_ONBRD_FLASH_EN(fpga0_reg)) + large_flash_base = EBONY_LARGE_FLASH_LOW; + else + large_flash_base = EBONY_LARGE_FLASH_HIGH; + + ebony_small_map.phys = small_flash_base; + ebony_small_map.virt = ioremap64(small_flash_base, + ebony_small_map.size); + + if (!ebony_small_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ebony_small_map); + + flash = do_map_probe("jedec_probe", &ebony_small_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ebony_small_partitions, + ARRAY_SIZE(ebony_small_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + ebony_large_map.phys = large_flash_base; + ebony_large_map.virt = ioremap64(large_flash_base, + ebony_large_map.size); + + if (!ebony_large_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ebony_large_map); + + flash = do_map_probe("jedec_probe", &ebony_large_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ebony_large_partitions, + ARRAY_SIZE(ebony_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ebony(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ebony_small_map.virt) { + iounmap(ebony_small_map.virt); + ebony_small_map.virt = NULL; + } + + if (ebony_large_map.virt) { + iounmap(ebony_large_map.virt); + ebony_large_map.virt = NULL; + } +} + +module_init(init_ebony); +module_exit(cleanup_ebony); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); --- linux-2.4.21/drivers/mtd/maps/edb7312.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/edb7312.c @@ -1,5 +1,5 @@ /* - * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * $Id: edb7312.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * Handle mapping of the NOR flash on Cogent EDB7312 boards * @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -27,69 +28,19 @@ #define BUSWIDTH 2 #define FLASH_BLOCKSIZE_MAIN 0x20000 #define FLASH_NUMBLOCKS_MAIN 128 -/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */ -#define PROBETYPES { "cfi_probe", 0 } +/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */ +#define PROBETYPES { "cfi_probe", NULL } #define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ #define MTDID "edb7312-nor" /* for mtdparts= partitioning */ static struct mtd_info *mymtd; -__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_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 edb7312nor_map = { - name: "NOR flash on EDB7312", - size: WINDOW_SIZE, - buswidth: BUSWIDTH, - read8: edb7312nor_read8, - read16: edb7312nor_read16, - read32: edb7312nor_read32, - copy_from: edb7312nor_copy_from, - write8: edb7312nor_write8, - write16: edb7312nor_write16, - write32: edb7312nor_write32, - copy_to: edb7312nor_copy_to + .name = "NOR flash on EDB7312", + .size = WINDOW_SIZE, + .bankwidth = BUSWIDTH, + .phys = WINDOW_ADDR, }; #ifdef CONFIG_MTD_PARTITIONS @@ -100,29 +51,23 @@ static struct mtd_partition static_partitions[3] = { { - name: "ARMboot", - size: 0x40000, - offset: 0 + .name = "ARMboot", + .size = 0x40000, + .offset = 0 }, { - name: "Kernel", - size: 0x200000, - offset: 0x40000 + .name = "Kernel", + .size = 0x200000, + .offset = 0x40000 }, { - name: "RootFS", - size: 0xDC0000, - offset: 0x240000 + .name = "RootFS", + .size = 0xDC0000, + .offset = 0x240000 }, }; -#define NB_OF(x) (sizeof (x) / sizeof (x[0])) - -#ifdef CONFIG_MTD_CMDLINE_PARTS -int parse_cmdline_partitions(struct mtd_info *master, - struct mtd_partition **pparts, - const char *mtd_id); -#endif +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif @@ -137,32 +82,32 @@ printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE, WINDOW_ADDR); - edb7312nor_map.map_priv_1 = (unsigned long) - ioremap(WINDOW_ADDR, WINDOW_SIZE); + edb7312nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!edb7312nor_map.map_priv_1) { + if (!edb7312nor_map.virt) { printk(MSG_PREFIX "failed to ioremap\n"); return -EIO; } + simple_map_init(&edb7312nor_map); + mymtd = 0; type = rom_probe_types; for(; !mymtd && *type; type++) { mymtd = do_map_probe(*type, &edb7312nor_map); } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; #ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); + mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); if (mtd_parts_nb > 0) - part_type = "command line"; -#endif + part_type = "detected"; + if (mtd_parts_nb == 0) { mtd_parts = static_partitions; - mtd_parts_nb = NB_OF(static_partitions); + mtd_parts_nb = ARRAY_SIZE(static_partitions); part_type = "static"; } #endif @@ -178,7 +123,7 @@ return 0; } - iounmap((void *)edb7312nor_map.map_priv_1); + iounmap((void *)edb7312nor_map.virt); return -ENXIO; } @@ -188,9 +133,9 @@ del_mtd_device(mymtd); map_destroy(mymtd); } - if (edb7312nor_map.map_priv_1) { - iounmap((void *)edb7312nor_map.map_priv_1); - edb7312nor_map.map_priv_1 = 0; + if (edb7312nor_map.virt) { + iounmap((void *)edb7312nor_map.virt); + edb7312nor_map.virt = 0; } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/epxa10db-flash.c @@ -0,0 +1,176 @@ +/* + * Flash memory access on EPXA based devices + * + * (C) 2000 Nicolas Pitre + * Copyright (C) 2001 Altera Corporation + * Copyright (C) 2001 Red Hat, Inc. + * + * $Id: epxa10db-flash.c,v 1.13 2004/11/04 13:24:14 gleixner 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef CONFIG_EPXA10DB +#define BOARD_NAME "EPXA10DB" +#else +#define BOARD_NAME "EPXA1DB" +#endif + +static int nr_parts = 0; +static struct mtd_partition *parts; + +static struct mtd_info *mymtd; + +static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); + + +static struct map_info epxa_map = { + .name = "EPXA flash", + .size = FLASH_SIZE, + .bankwidth = 2, + .phys = FLASH_START, +}; + +static const char *probes[] = { "RedBoot", "afs", NULL }; + +static int __init epxa_mtd_init(void) +{ + int i; + + printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); + + epxa_map.virt = ioremap(FLASH_START, FLASH_SIZE); + if (!epxa_map.virt) { + printk("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } + simple_map_init(&epxa_map); + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { + iounmap((void *)epxa_map.virt); + return -ENXIO; + } + + mymtd->owner = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ + for (i=0; inumeraseregions;i++){ + int j; + for(j=0;jeraseregions[i].numblocks;j++){ + mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); + } + } + } + +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif + /* No recognised partitioning schemes found - use defaults */ + nr_parts = epxa_default_partitions(mymtd, &parts); + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + + /* If all else fails... */ + add_mtd_device(mymtd); + return 0; +} + +static void __exit epxa_mtd_cleanup(void) +{ + if (mymtd) { + if (nr_parts) + del_mtd_partitions(mymtd); + else + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (epxa_map.virt) { + iounmap((void *)epxa_map.virt); + epxa_map.virt = 0; + } +} + + +/* + * This will do for now, once we decide which bootldr we're finally + * going to use then we'll remove this function and do it properly + * + * Partions are currently (as offsets from base of flash): + * 0x00000000 - 0x003FFFFF - bootloader (!) + * 0x00400000 - 0x00FFFFFF - Flashdisk + */ + +static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + struct mtd_partition *parts; + int ret, i; + int npartitions = 0; + char *names; + const char *name = "jffs"; + + printk("Using default partitions for %s\n",BOARD_NAME); + npartitions=1; + parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); + memzero(parts,npartitions*sizeof(*parts)+strlen(name)); + if (!parts) { + ret = -ENOMEM; + goto out; + } + i=0; + names = (char *)&parts[npartitions]; + parts[i].name = names; + names += strlen(name) + 1; + strcpy(parts[i].name, name); + +#ifdef CONFIG_EPXA10DB + parts[i].size = FLASH_SIZE-0x00400000; + parts[i].offset = 0x00400000; +#else + parts[i].size = FLASH_SIZE-0x00180000; + parts[i].offset = 0x00180000; +#endif + + out: + *pparts = parts; + return npartitions; +} + + +module_init(epxa_mtd_init); +module_exit(epxa_mtd_cleanup); + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera epxa mtd flash map"); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/maps/fortunet.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/fortunet.c @@ -1,11 +1,12 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + * $Id: fortunet.c,v 1.9 2004/11/04 13:24:14 gleixner Exp $ */ #include #include #include +#include #include #include #include @@ -23,8 +24,8 @@ struct map_region { - int window_addr_phyical; - int altbuswidth; + int window_addr_physical; + int altbankwidth; struct map_info map_info; struct mtd_info *mymtd; struct mtd_partition parts[MAX_NUM_PARTITIONS]; @@ -37,57 +38,10 @@ static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; -__u8 fortunet_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(map->map_priv_1 + ofs); -} - -__u16 fortunet_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(map->map_priv_1 + ofs); -} - -__u32 fortunet_read32(struct map_info *map, unsigned long ofs) -{ - return *(__u32 *)(map->map_priv_1 + ofs); -} - -void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(map->map_priv_1 + from), len); -} - -void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(map->map_priv_1 + adr) = d; -} - -void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(map->map_priv_1 + adr) = d; -} - -void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(map->map_priv_1 + adr) = d; -} - -void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy((void *)(map->map_priv_1 + to), from, len); -} struct map_info default_map = { - size: DEF_WINDOW_SIZE, - buswidth: 4, - read8: fortunet_read8, - read16: fortunet_read16, - read32: fortunet_read32, - copy_from: fortunet_copy_from, - write8: fortunet_write8, - write16: fortunet_write16, - write32: fortunet_write32, - copy_to: fortunet_copy_to + .size = DEF_WINDOW_SIZE, + .bankwidth = 4, }; static char * __init get_string_option(char *dest,int dest_size,char *sor) @@ -147,8 +101,8 @@ get_options (get_string_option(string,sizeof(string),line),6,params); if(params[0]<1) { - printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " - " name,region-number[,base,size,buswidth,altbuswidth]\n"); + printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " + " name,region-number[,base,size,bankwidth,altbankwidth]\n"); return 1; } if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) @@ -161,14 +115,14 @@ memcpy(&map_regions[params[1]].map_info, &default_map,sizeof(map_regions[params[1]].map_info)); map_regions_set[params[1]] = 1; - map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY; - map_regions[params[1]].altbuswidth = 2; + map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[params[1]].altbankwidth = 2; map_regions[params[1]].mymtd = NULL; map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; strcpy(map_regions[params[1]].map_info.name,string); if(params[0]>1) { - map_regions[params[1]].window_addr_phyical = params[2]; + map_regions[params[1]].window_addr_physical = params[2]; } if(params[0]>2) { @@ -176,23 +130,23 @@ } if(params[0]>3) { - map_regions[params[1]].map_info.buswidth = params[4]; + map_regions[params[1]].map_info.bankwidth = params[4]; } if(params[0]>4) { - map_regions[params[1]].altbuswidth = params[5]; + map_regions[params[1]].altbankwidth = params[5]; } return 1; } -static int __init MTD_New_Partion(char *line) +static int __init MTD_New_Partition(char *line) { char string[MAX_NAME_SIZE]; int params[4]; get_options (get_string_option(string,sizeof(string),line),4,params); if(params[0]<3) { - printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion " + printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " " name,region-number,size,offset\n"); return 1; } @@ -204,7 +158,7 @@ } if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) { - printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); + printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); return 1; } map_regions[params[1]].parts[map_regions_parts[params[1]]].name = @@ -220,7 +174,10 @@ } __setup("MTD_Region=", MTD_New_Region); -__setup("MTD_Partion=", MTD_New_Partion); +__setup("MTD_Partition=", MTD_New_Partition); + +/* Backwards-spelling-compatibility */ +__setup("MTD_Partion=", MTD_New_Partition); int __init init_fortunet(void) { @@ -229,14 +186,14 @@ { if(map_regions_parts[ix]&&(!map_regions_set[ix])) { - printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n", + printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", ix); memset(&map_regions[ix],0,sizeof(map_regions[ix])); memcpy(&map_regions[ix].map_info,&default_map, sizeof(map_regions[ix].map_info)); map_regions_set[ix] = 1; - map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY; - map_regions[ix].altbuswidth = 2; + map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[ix].altbankwidth = 2; map_regions[ix].mymtd = NULL; map_regions[ix].map_info.name = map_regions[ix].map_name; strcpy(map_regions[ix].map_info.name,"FORTUNET"); @@ -244,38 +201,43 @@ if(map_regions_set[ix]) { iy++; - printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly " + printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " " address %x size %x\n", map_regions[ix].map_info.name, - map_regions[ix].window_addr_phyical, + map_regions[ix].window_addr_physical, map_regions[ix].map_info.size); - map_regions[ix].map_info.map_priv_1 = - (int)ioremap_nocache( - map_regions[ix].window_addr_phyical, + + map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, + + map_regions[ix].map_info.virt = + ioremap_nocache( + map_regions[ix].window_addr_physical, map_regions[ix].map_info.size); - if(!map_regions[ix].map_info.map_priv_1) + if(!map_regions[ix].map_info.virt) { printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", map_regions[ix].map_info.name); return -ENXIO; } - printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n", + simple_map_init(&map_regions[ix].map_info); + + printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", map_regions[ix].map_info.name, - map_regions[ix].map_info.map_priv_1); + map_regions[ix].map_info.virt); map_regions[ix].mymtd = do_map_probe("cfi_probe", &map_regions[ix].map_info); if((!map_regions[ix].mymtd)&&( - map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth)) + map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth)) { - printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth " + printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth " "for %s flash.\n", map_regions[ix].map_info.name); - map_regions[ix].map_info.buswidth = - map_regions[ix].altbuswidth; + map_regions[ix].map_info.bankwidth = + map_regions[ix].altbankwidth; map_regions[ix].mymtd = do_map_probe("cfi_probe", &map_regions[ix].map_info); } - map_regions[ix].mymtd->module = THIS_MODULE; + map_regions[ix].mymtd->owner = THIS_MODULE; add_mtd_partitions(map_regions[ix].mymtd, map_regions[ix].parts,map_regions_parts[ix]); } @@ -297,7 +259,7 @@ del_mtd_partitions( map_regions[ix].mymtd ); map_destroy( map_regions[ix].mymtd ); } - iounmap((void *)map_regions[ix].map_info.map_priv_1); + iounmap((void *)map_regions[ix].map_info.virt); } } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/h720x-flash.c @@ -0,0 +1,144 @@ +/* + * Flash memory access on Hynix GMS30C7201/HMS30C7202 based + * evaluation boards + * + * $Id: h720x-flash.c,v 1.11 2004/11/04 13:24:14 gleixner Exp $ + * + * (C) 2002 Jungjun Kim + * 2003 Thomas Gleixner + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct mtd_info *mymtd; + +static struct map_info h720x_map = { + .name = "H720X", + .bankwidth = 4, + .size = FLASH_SIZE, + .phys = FLASH_PHYS, +}; + +static struct mtd_partition h720x_partitions[] = { + { + .name = "ArMon", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Env", + .size = 0x00040000, + .offset = 0x00080000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Kernel", + .size = 0x00180000, + .offset = 0x000c0000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Ramdisk", + .size = 0x00400000, + .offset = 0x00240000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND + } +}; + +#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) + +static int nr_mtd_parts; +static struct mtd_partition *mtd_parts; +static const char *probes[] = { "cmdlinepart", NULL }; + +/* + * Initialize FLASH support + */ +int __init h720x_mtd_init(void) +{ + + char *part_type = NULL; + + h720x_map.virt = ioremap(FLASH_PHYS, FLASH_SIZE); + + if (!h720x_map.virt) { + printk(KERN_ERR "H720x-MTD: ioremap failed\n"); + return -EIO; + } + + simple_map_init(&h720x_map); + + // Probe for flash bankwidth 4 + printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); + mymtd = do_map_probe("cfi_probe", &h720x_map); + if (!mymtd) { + printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); + // Probe for bankwidth 2 + h720x_map.bankwidth = 2; + mymtd = do_map_probe("cfi_probe", &h720x_map); + } + + if (mymtd) { + mymtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); + if (nr_mtd_parts > 0) + part_type = "command line"; +#endif + if (nr_mtd_parts <= 0) { + mtd_parts = h720x_partitions; + nr_mtd_parts = NUM_PARTITIONS; + part_type = "builtin"; + } + printk(KERN_INFO "Using %s partition table\n", part_type); + add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); + return 0; + } + + iounmap((void *)h720x_map.virt); + return -ENXIO; +} + +/* + * Cleanup + */ +static void __exit h720x_mtd_cleanup(void) +{ + + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + + /* Free partition info, if commandline partition was used */ + if (mtd_parts && (mtd_parts != h720x_partitions)) + kfree (mtd_parts); + + if (h720x_map.virt) { + iounmap((void *)h720x_map.virt); + h720x_map.virt = 0; + } +} + + +module_init(h720x_mtd_init); +module_exit(h720x_mtd_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards"); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ichxrom.c @@ -0,0 +1,383 @@ +/* + * ichxrom.c + * + * Normal mappings of chips in physical memory + * $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define xstr(s) str(s) +#define str(s) #s +#define MOD_NAME xstr(KBUILD_BASENAME) + +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +#define BIOS_CNTL 0x4e +#define FWH_DEC_EN1 0xE3 +#define FWH_DEC_EN2 0xF0 +#define FWH_SEL1 0xE8 +#define FWH_SEL2 0xEE + +struct ichxrom_window { + void __iomem* virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; + +struct ichxrom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; +}; + +static struct ichxrom_window ichxrom_window = { + .maps = LIST_HEAD_INIT(ichxrom_window.maps), +}; + +static void ichxrom_cleanup(struct ichxrom_window *window) +{ + struct ichxrom_map_info *map, *scratch; + u16 word; + + /* Disable writes through the rom window */ + pci_read_config_word(window->pdev, BIOS_CNTL, &word); + pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1); + + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; + } +} + + +static int __devinit ichxrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct ichxrom_window *window = &ichxrom_window; + struct ichxrom_map_info *map = NULL; + unsigned long map_top; + u8 byte; + u16 word; + + /* For now I just handle the ichx and I assume there + * are not a lot of resources up at the top of the address + * space. It is possible to handle other devices in the + * top 16MB but it is very painful. Also since + * you can only really attach a FWH to an ICHX there + * a number of simplifications you can make. + * + * Also you can page firmware hubs if an 8MB window isn't enough + * but don't currently handle that case either. + */ + window->pdev = pdev; + + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + if (byte == 0xff) { + window->phys = 0xffc00000; + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + if ((byte & 0x0f) == 0x0f) { + window->phys = 0xff400000; + } + else if ((byte & 0x0e) == 0x0e) { + window->phys = 0xff500000; + } + else if ((byte & 0x0c) == 0x0c) { + window->phys = 0xff600000; + } + else if ((byte & 0x08) == 0x08) { + window->phys = 0xff700000; + } + } + else if ((byte & 0xfe) == 0xfe) { + window->phys = 0xffc80000; + } + else if ((byte & 0xfc) == 0xfc) { + window->phys = 0xffd00000; + } + else if ((byte & 0xf8) == 0xf8) { + window->phys = 0xffd80000; + } + else if ((byte & 0xf0) == 0xf0) { + window->phys = 0xffe00000; + } + else if ((byte & 0xe0) == 0xe0) { + window->phys = 0xffe80000; + } + else if ((byte & 0xc0) == 0xc0) { + window->phys = 0xfff00000; + } + else if ((byte & 0x80) == 0x80) { + window->phys = 0xfff80000; + } + + if (window->phys == 0) { + printk(KERN_ERR MOD_NAME ": Rom window is closed\n"); + goto out; + } + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; + + /* Enable writes through the rom window */ + pci_read_config_word(pdev, BIOS_CNTL, &word); + if (!(word & 1) && (word & (1<<1))) { + /* The BIOS will generate an error if I enable + * this device, so don't even try. + */ + printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); + goto out; + } + pci_write_config_word(pdev, BIOS_CNTL, word | 1); + + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + window->rsrc.start, window->rsrc.end); + } + + /* Map the firmware hub into my address space. */ + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); + goto out; + } + + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4M of the address space. + */ + if (map_top < 0xffc00000) { + map_top = 0xffc00000; + } +#endif + /* Loop through and look for rom chips */ + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); + } + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto out; + } + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = (void __iomem *) + (((unsigned long)(window->virt)) + offset); + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. + */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; + } + if (window->rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + map->rsrc.parent = NULL; + } + } + + /* Make the whole region visible in the map */ + map->map.virt = window->virt; + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; + } + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + if (add_mtd_device(map->mtd)) { + map_destroy(map->mtd); + map->mtd = NULL; + goto out; + } + + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = NULL; + } + + out: + /* Free any left over map structures */ + if (map) { + kfree(map); + } + /* See if I have any map structures */ + if (list_empty(&window->maps)) { + ichxrom_cleanup(window); + return -ENODEV; + } + return 0; +} + + +static void __devexit ichxrom_remove_one (struct pci_dev *pdev) +{ + struct ichxrom_window *window = &ichxrom_window; + ichxrom_cleanup(window); +} + +static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl); + +#if 0 +static struct pci_driver ichxrom_driver = { + .name = MOD_NAME, + .id_table = ichxrom_pci_tbl, + .probe = ichxrom_init_one, + .remove = ichxrom_remove_one, +}; +#endif + +static int __init init_ichxrom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + + pdev = NULL; + for (id = ichxrom_pci_tbl; id->vendor; id++) { + pdev = pci_find_device(id->vendor, id->device, NULL); + if (pdev) { + break; + } + } + if (pdev) { + return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]); + } + return -ENXIO; +#if 0 + return pci_module_init(&ichxrom_driver); +#endif +} + +static void __exit cleanup_ichxrom(void) +{ + ichxrom_remove_one(ichxrom_window.pdev); +} + +module_init(init_ichxrom); +module_exit(cleanup_ichxrom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biederman "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge"); --- linux-2.4.21/drivers/mtd/maps/impa7.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/impa7.c @@ -1,5 +1,5 @@ /* - * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * $Id: impa7.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ * * Handle mapping of the NOR flash on implementa A7 boards * @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -29,83 +30,25 @@ #define NUM_FLASHBANKS 2 #define BUSWIDTH 4 -/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */ -#define PROBETYPES { "jedec_probe", 0 } +/* can be { "cfi_probe", "jedec_probe", "map_rom", NULL } */ +#define PROBETYPES { "jedec_probe", NULL } #define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ #define MTDID "impa7-%d" /* for mtdparts= partitioning */ -static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; - -__u8 impa7_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 impa7_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 impa7_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} +static struct mtd_info *impa7_mtd[NUM_FLASHBANKS]; -void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} static struct map_info impa7_map[NUM_FLASHBANKS] = { { - name: "impA7 NOR Flash Bank #0", - size: WINDOW_SIZE0, - buswidth: BUSWIDTH, - read8: impa7_read8, - read16: impa7_read16, - read32: impa7_read32, - copy_from: impa7_copy_from, - write8: impa7_write8, - write16: impa7_write16, - write32: impa7_write32, - copy_to: impa7_copy_to + .name = "impA7 NOR Flash Bank #0", + .size = WINDOW_SIZE0, + .bankwidth = BUSWIDTH, }, { - name: "impA7 NOR Flash Bank #1", - size: WINDOW_SIZE1, - buswidth: BUSWIDTH, - read8: impa7_read8, - read16: impa7_read16, - read32: impa7_read32, - copy_from: impa7_copy_from, - write8: impa7_write8, - write16: impa7_write16, - write32: impa7_write32, - copy_to: impa7_copy_to + .name = "impA7 NOR Flash Bank #1", + .size = WINDOW_SIZE1, + .bankwidth = BUSWIDTH, }, }; @@ -117,24 +60,18 @@ static struct mtd_partition static_partitions[] = { { - name: "FileSystem", - size: 0x800000, - offset: 0x00000000 + .name = "FileSystem", + .size = 0x800000, + .offset = 0x00000000 }, }; -#define NB_OF(x) (sizeof (x) / sizeof (x[0])) - -#ifdef CONFIG_MTD_CMDLINE_PARTS -int parse_cmdline_partitions(struct mtd_info *master, - struct mtd_partition **pparts, - const char *mtd_id); -#endif +static int mtd_parts_nb[NUM_FLASHBANKS]; +static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; #endif -static int mtd_parts_nb = 0; -static struct mtd_partition *mtd_parts = 0; +static const char *probes[] = { "cmdlinepart", NULL }; int __init init_impa7(void) { @@ -146,20 +83,20 @@ { WINDOW_ADDR0, WINDOW_SIZE0 }, { WINDOW_ADDR1, WINDOW_SIZE1 }, }; - char mtdid[10]; int devicesfound = 0; for(i=0; imodule = THIS_MODULE; - add_mtd_device(impa7_mtd[i]); + if (impa7_mtd[i]) { + impa7_mtd[i]->owner = THIS_MODULE; devicesfound++; #ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - sprintf(mtdid, MTDID, i); - mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], - &mtd_parts, - mtdid); - if (mtd_parts_nb > 0) + mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], + probes, + &mtd_parts[i], + 0); + if (mtd_parts_nb[i] > 0) { part_type = "command line"; -#endif - if (mtd_parts_nb <= 0) - { - mtd_parts = static_partitions; - mtd_parts_nb = NB_OF(static_partitions); + } else { + mtd_parts[i] = static_partitions; + mtd_parts_nb[i] = ARRAY_SIZE(static_partitions); part_type = "static"; } - if (mtd_parts_nb <= 0) - { - printk(KERN_NOTICE MSG_PREFIX - "no partition info available\n"); - } - else - { + printk(KERN_NOTICE MSG_PREFIX "using %s partition definition\n", part_type); add_mtd_partitions(impa7_mtd[i], - mtd_parts, mtd_parts_nb); - } + mtd_parts[i], mtd_parts_nb[i]); +#else + add_mtd_device(impa7_mtd[i]); + #endif } else - iounmap((void *)impa7_map[i].map_priv_1); + iounmap((void *)impa7_map[i].virt); } return devicesfound == 0 ? -ENXIO : 0; } @@ -211,17 +139,16 @@ static void __exit cleanup_impa7(void) { int i; - for (i=0; i +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +// board specific stuff - sorry, it should be in arch/arm/mach-*. +#ifdef CONFIG_ARCH_INTEGRATOR + +#define FLASH_BASE INTEGRATOR_FLASH_BASE +#define FLASH_SIZE INTEGRATOR_FLASH_SIZE + +#define FLASH_PART_SIZE 0x400000 + +#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) +#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) +#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) + +/* + * Initialise the flash access systems: + * - Disable VPP + * - Assert WP + * - Set write enable bit in EBI reg + */ +static void armflash_flash_init(void) +{ + unsigned int tmp; + + __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); + + tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; + __raw_writel(tmp, EBI_CSR1); + + if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { + __raw_writel(0xa05f, EBI_LOCK); + __raw_writel(tmp, EBI_CSR1); + __raw_writel(0, EBI_LOCK); + } +} + +/* + * Shutdown the flash access systems: + * - Disable VPP + * - Assert WP + * - Clear write enable bit in EBI reg + */ +static void armflash_flash_exit(void) +{ + unsigned int tmp; + + __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); + + /* + * Clear the write enable bit in system controller EBI register. + */ + tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; + __raw_writel(tmp, EBI_CSR1); + + if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { + __raw_writel(0xa05f, EBI_LOCK); + __raw_writel(tmp, EBI_CSR1); + __raw_writel(0, EBI_LOCK); + } +} + +static void armflash_flash_wp(int on) +{ + unsigned int reg; + + if (on) + reg = SC_CTRLC; + else + reg = SC_CTRLS; + + __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); +} + +static void armflash_set_vpp(struct map_info *map, int on) +{ + unsigned int reg; + + if (on) + reg = SC_CTRLS; + else + reg = SC_CTRLC; + + __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); +} +#endif + +#ifdef CONFIG_ARCH_P720T + +#define FLASH_BASE (0x04000000) +#define FLASH_SIZE (64*1024*1024) + +#define FLASH_PART_SIZE (4*1024*1024) +#define FLASH_BLOCK_SIZE (128*1024) + +static void armflash_flash_init(void) +{ +} + +static void armflash_flash_exit(void) +{ +} + +static void armflash_flash_wp(int on) +{ +} + +static void armflash_set_vpp(struct map_info *map, int on) +{ +} +#endif + + +static struct map_info armflash_map = +{ + .name = "AFS", + .set_vpp = armflash_set_vpp, + .phys = FLASH_BASE, +}; + +static struct mtd_info *mtd; +static struct mtd_partition *parts; +static const char *probes[] = { "RedBoot", "afs", NULL }; + +static int __init armflash_cfi_init(void *base, u_int size) +{ + int ret; + + armflash_flash_init(); + armflash_flash_wp(1); + + /* + * look for CFI based flash parts fitted to this board + */ + armflash_map.size = size; + armflash_map.bankwidth = 4; + armflash_map.virt = (void __iomem *) base; + + simple_map_init(&armflash_map); + + /* + * Also, the CFI layer automatically works out what size + * of chips we have, and does the necessary identification + * for us automatically. + */ + mtd = do_map_probe("cfi_probe", &armflash_map); + if (!mtd) + return -ENXIO; + + mtd->owner = THIS_MODULE; + + ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0); + if (ret > 0) { + ret = add_mtd_partitions(mtd, parts, ret); + if (ret) + printk(KERN_ERR "mtd partition registration " + "failed: %d\n", ret); + } + + /* + * If we got an error, free all resources. + */ + if (ret < 0) { + del_mtd_partitions(mtd); + map_destroy(mtd); + } + + return ret; +} + +static void armflash_cfi_exit(void) +{ + if (mtd) { + del_mtd_partitions(mtd); + map_destroy(mtd); + } + if (parts) + kfree(parts); +} + +static int __init armflash_init(void) +{ + int err = -EBUSY; + void *base; + + if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) + goto out; + + base = ioremap(FLASH_BASE, FLASH_SIZE); + err = -ENOMEM; + if (base == NULL) + goto release; + + err = armflash_cfi_init(base, FLASH_SIZE); + if (err) { + iounmap(base); +release: + release_mem_region(FLASH_BASE, FLASH_SIZE); + } +out: + return err; +} + +static void __exit armflash_exit(void) +{ + armflash_cfi_exit(); + iounmap((void *)armflash_map.virt); + release_mem_region(FLASH_BASE, FLASH_SIZE); + armflash_flash_exit(); +} + +module_init(armflash_init); +module_exit(armflash_exit); + +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("ARM Integrator CFI map driver"); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/maps/integrator-flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/integrator-flash.c @@ -1,8 +1,9 @@ /*====================================================================== - drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning + drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver Copyright (C) 2000 ARM Limited + Copyright (C) 2003 Deep Blue Solutions 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 @@ -21,7 +22,7 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.7 2001/11/01 20:55:47 rmk Exp $ + $Id: integrator-flash.c,v 1.18 2004/11/01 13:26:15 rmk Exp $ ======================================================================*/ @@ -31,268 +32,181 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include -extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); - -// board specific stuff - sorry, it should be in arch/arm/mach-*. -#ifdef CONFIG_ARCH_INTEGRATOR - -#define FLASH_BASE INTEGRATOR_FLASH_BASE -#define FLASH_SIZE INTEGRATOR_FLASH_SIZE - -#define FLASH_PART_SIZE 0x400000 - -#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) -#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) -#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) -#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) - -/* - * Initialise the flash access systems: - * - Disable VPP - * - Assert WP - * - Set write enable bit in EBI reg - */ -static void armflash_flash_init(void) -{ - unsigned int tmp; - - __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); - - tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; - __raw_writel(tmp, EBI_CSR1); - - if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { - __raw_writel(0xa05f, EBI_LOCK); - __raw_writel(tmp, EBI_CSR1); - __raw_writel(0, EBI_LOCK); - } -} - -/* - * Shutdown the flash access systems: - * - Disable VPP - * - Assert WP - * - Clear write enable bit in EBI reg - */ -static void armflash_flash_exit(void) -{ - unsigned int tmp; - - __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); - - /* - * Clear the write enable bit in system controller EBI register. - */ - tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; - __raw_writel(tmp, EBI_CSR1); - - if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { - __raw_writel(0xa05f, EBI_LOCK); - __raw_writel(tmp, EBI_CSR1); - __raw_writel(0, EBI_LOCK); - } -} - -static void armflash_flash_wp(int on) -{ - unsigned int reg; - - if (on) - reg = SC_CTRLC; - else - reg = SC_CTRLS; - - __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); -} - -static void armflash_set_vpp(struct map_info *map, int on) -{ - unsigned int reg; - - if (on) - reg = SC_CTRLS; - else - reg = SC_CTRLC; - - __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); -} -#endif - #ifdef CONFIG_ARCH_P720T - #define FLASH_BASE (0x04000000) #define FLASH_SIZE (64*1024*1024) - -#define FLASH_PART_SIZE (4*1024*1024) -#define FLASH_BLOCK_SIZE (128*1024) - -static void armflash_flash_init(void) -{ -} - -static void armflash_flash_exit(void) -{ -} - -static void armflash_flash_wp(int on) -{ -} - -static void armflash_set_vpp(struct map_info *map, int on) -{ -} #endif -static __u8 armflash_read8(struct map_info *map, unsigned long ofs) -{ - return readb(ofs + map->map_priv_2); -} - -static __u16 armflash_read16(struct map_info *map, unsigned long ofs) -{ - return readw(ofs + map->map_priv_2); -} - -static __u32 armflash_read32(struct map_info *map, unsigned long ofs) -{ - return readl(ofs + map->map_priv_2); -} +struct armflash_info { + struct flash_platform_data *plat; + struct resource *res; + struct mtd_partition *parts; + struct mtd_info *mtd; + struct map_info map; +}; -static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void armflash_set_vpp(struct map_info *map, int on) { - memcpy(to, (void *) (from + map->map_priv_2), len); -} + struct armflash_info *info = container_of(map, struct armflash_info, map); -static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, adr + map->map_priv_2); + if (info->plat && info->plat->set_vpp) + info->plat->set_vpp(on); } -static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, adr + map->map_priv_2); -} +static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr) +static int armflash_probe(struct device *_dev) { - writel(d, adr + map->map_priv_2); -} + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct resource *res = dev->resource; + unsigned int size = res->end - res->start + 1; + struct armflash_info *info; + int err; + void __iomem *base; -static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy((void *) (to + map->map_priv_2), from, len); -} + info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto out; + } -static struct map_info armflash_map = -{ - name: "AFS", - read8: armflash_read8, - read16: armflash_read16, - read32: armflash_read32, - copy_from: armflash_copy_from, - write8: armflash_write8, - write16: armflash_write16, - write32: armflash_write32, - copy_to: armflash_copy_to, - set_vpp: armflash_set_vpp, -}; + memset(info, 0, sizeof(struct armflash_info)); -static struct mtd_info *mtd; -static struct mtd_partition *parts; + info->plat = plat; + if (plat && plat->init) { + err = plat->init(); + if (err) + goto no_resource; + } -static int __init armflash_cfi_init(void *base, u_int size) -{ - int ret; + info->res = request_mem_region(res->start, size, "armflash"); + if (!info->res) { + err = -EBUSY; + goto no_resource; + } - armflash_flash_init(); - armflash_flash_wp(1); + base = ioremap(res->start, size); + if (!base) { + err = -ENOMEM; + goto no_mem; + } /* * look for CFI based flash parts fitted to this board */ - armflash_map.size = size; - armflash_map.buswidth = 4; - armflash_map.map_priv_2 = (unsigned long) base; + info->map.size = size; + info->map.bankwidth = plat->width; + info->map.phys = res->start; + info->map.virt = base; + info->map.name = dev->dev.bus_id; + info->map.set_vpp = armflash_set_vpp; + + simple_map_init(&info->map); /* * Also, the CFI layer automatically works out what size * of chips we have, and does the necessary identification * for us automatically. */ - mtd = do_map_probe("cfi_probe", &armflash_map); - if (!mtd) - return -ENXIO; + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + err = -ENXIO; + goto no_device; + } - mtd->module = THIS_MODULE; + info->mtd->owner = THIS_MODULE; - ret = parse_afs_partitions(mtd, &parts); - if (ret > 0) { - ret = add_mtd_partitions(mtd, parts, ret); - if (ret) - printk(KERN_ERR "mtd partition registration " - "failed: %d\n", ret); + err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->parts, err); + if (err) + printk(KERN_ERR + "mtd partition registration failed: %d\n", err); } + if (err == 0) + dev_set_drvdata(&dev->dev, info); + /* * If we got an error, free all resources. */ - if (ret < 0) { - del_mtd_partitions(mtd); - map_destroy(mtd); + if (err < 0) { + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); } + if (info->parts) + kfree(info->parts); - return ret; -} - -static void armflash_cfi_exit(void) -{ - if (mtd) { - del_mtd_partitions(mtd); - map_destroy(mtd); + no_device: + iounmap(base); + no_mem: + release_mem_region(res->start, size); + no_resource: + if (plat && plat->exit) + plat->exit(); + kfree(info); } - if (parts) - kfree(parts); + out: + return err; } -static int __init armflash_init(void) +static int armflash_remove(struct device *_dev) { - int err = -EBUSY; - void *base; + struct platform_device *dev = to_platform_device(_dev); + struct armflash_info *info = dev_get_drvdata(&dev->dev); - if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) - goto out; + dev_set_drvdata(&dev->dev, NULL); - base = ioremap(FLASH_BASE, FLASH_SIZE); - err = -ENOMEM; - if (base == NULL) - goto release; + if (info) { + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->parts) + kfree(info->parts); - err = armflash_cfi_init(base, FLASH_SIZE); - if (err) { - iounmap(base); -release: - release_mem_region(FLASH_BASE, FLASH_SIZE); + iounmap(info->map.virt); + release_resource(info->res); + kfree(info->res); + + if (info->plat && info->plat->exit) + info->plat->exit(); + + kfree(info); } -out: - return err; + + return 0; +} + +static struct device_driver armflash_driver = { + .name = "armflash", + .bus = &platform_bus_type, + .probe = armflash_probe, + .remove = armflash_remove, +}; + +static int __init armflash_init(void) +{ + return driver_register(&armflash_driver); } static void __exit armflash_exit(void) { - armflash_cfi_exit(); - iounmap((void *)armflash_map.map_priv_2); - release_mem_region(FLASH_BASE, FLASH_SIZE); - armflash_flash_exit(); + driver_unregister(&armflash_driver); } module_init(armflash_init); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ipaq-flash.c @@ -0,0 +1,464 @@ +/* + * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based) + * + * (C) 2000 Nicolas Pitre + * (C) 2002 Hewlett-Packard Company + * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes + * + * $Id: ipaq-flash.c,v 1.4 2005/01/12 22:34:35 gleixner Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_MTD_CONCAT +#include +#endif + +#include +#include +#include + + +#ifndef CONFIG_IPAQ_HANDHELD +#error This is for iPAQ Handhelds only +#endif +#ifdef CONFIG_SA1100_JORNADA56X + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} + +#endif + +#ifdef CONFIG_SA1100_JORNADA720 + +static void jornada720_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} + +#endif + +#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */ + +#define IPAQ_MAP_INIT(X) \ + { \ + name: "IPAQ flash " X, \ + } + + +static struct map_info ipaq_map[MAX_IPAQ_CS] = { + IPAQ_MAP_INIT("bank 1"), + IPAQ_MAP_INIT("bank 2") +}; + +static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = { + NULL, + NULL +}; + +/* + * Here are partition information for all known IPAQ-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 be no more than + * the value specified in the "struct map_desc *_io_desc" mapping + * definition for the corresponding machine. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + +#ifdef CONFIG_IPAQ_HANDHELD +static unsigned long h3xxx_max_flash_size = 0x04000000; +static struct mtd_partition h3xxx_partitions[] = { + { + name: "H3XXX boot firmware", +#ifndef CONFIG_LAB + size: 0x00040000, +#else + size: 0x00080000, +#endif + offset: 0, +#ifndef CONFIG_LAB + mask_flags: MTD_WRITEABLE, /* force read-only */ +#endif + }, + { + name: "H3XXX root jffs2", +#ifndef CONFIG_LAB + size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */ + offset: 0x00040000, +#else + size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */ + offset: 0x00080000, +#endif + }, + { + name: "asset", + size: 0x40000, + offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; + +#ifndef CONFIG_MTD_CONCAT +static struct mtd_partition h3xxx_partitions_bank2[] = { + /* this is used only on 2 CS machines when concat is not present */ + { + name: "second H3XXX root jffs2", + size: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + offset: 0x00000000, + }, + { + name: "second asset", + size: 0x40000, + offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; +#endif + +static DEFINE_SPINLOCK(ipaq_vpp_lock); + +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + static int nest = 0; + + spin_lock(&ipaq_vpp_lock); + if (vpp) + nest++; + else + nest--; + if (nest) + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1); + else + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0); + spin_unlock(&ipaq_vpp_lock); +} + +#endif + +#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720) +static unsigned long jornada_max_flash_size = 0x02000000; +static struct mtd_partition jornada_partitions[] = { + { + name: "Jornada boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "Jornada root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00040000, + } +}; +#endif + + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +static unsigned long cs_phys[] = { +#ifdef CONFIG_ARCH_SA1100 + SA1100_CS0_PHYS, + SA1100_CS1_PHYS, + SA1100_CS2_PHYS, + SA1100_CS3_PHYS, + SA1100_CS4_PHYS, + SA1100_CS5_PHYS, +#else + PXA_CS0_PHYS, + PXA_CS1_PHYS, + PXA_CS2_PHYS, + PXA_CS3_PHYS, + PXA_CS4_PHYS, + PXA_CS5_PHYS, +#endif +}; + +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; + +static int __init h1900_special_case(void); + +int __init ipaq_mtd_init(void) +{ + struct mtd_partition *parts = NULL; + int nb_parts = 0; + int parsed_nr_parts = 0; + const char *part_type; + int i; /* used when we have >1 flash chips */ + unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */ + + /* Default flash bankwidth */ + // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; + + if (machine_is_h1900()) + { + /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */ + return h1900_special_case(); + } + + if (machine_is_h3100() || machine_is_h1900()) + for(i=0; isize); + + /* do we really need this debugging? --joshua 20030703 */ + // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]); + my_sub_mtd[i]->owner = THIS_MODULE; + tot_flashsize += my_sub_mtd[i]->size; + } +#ifdef CONFIG_MTD_CONCAT + /* fix the asset location */ +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */; +# else + h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000; +# endif + h3xxx_partitions[2].offset = tot_flashsize - 0x40000; + /* and concat the devices */ + mymtd = mtd_concat_create(&my_sub_mtd[0], i, + "ipaq"); + if (!mymtd) { + printk("Cannot create iPAQ concat device\n"); + return -ENXIO; + } +#else + mymtd = my_sub_mtd[0]; + + /* + *In the very near future, command line partition parsing + * will use the device name as 'mtd-id' instead of a value + * passed to the parse_cmdline_partitions() routine. Since + * the bootldr says 'ipaq', make sure it continues to work. + */ + mymtd->name = "ipaq"; + + if ((machine_is_h3600())) { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000; +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000; +# endif + nb_parts = 2; + } else { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */ +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000; +# endif + h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000; + } + + if (my_sub_mtd[1]) { +# ifdef CONFIG_LAB + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000; +# else + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000; +# endif + h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000; + } +#endif + } + else { + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt); + mymtd = do_map_probe("cfi_probe", &ipaq_map[0]); + if (!mymtd) + return -ENXIO; + mymtd->owner = THIS_MODULE; + } + + + /* + * Dynamic partition selection stuff (might override the static ones) + */ + + i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0); + + if (i > 0) { + nb_parts = parsed_nr_parts = i; + parts = parsed_parts; + part_type = "dynamic"; + } + + if (!parts) { + printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_device(my_sub_mtd[1]); +#endif + } else { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2)); +#endif + } + + return 0; +} + +static void __exit ipaq_mtd_cleanup(void) +{ + int i; + + if (mymtd) { + del_mtd_partitions(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + del_mtd_partitions(my_sub_mtd[1]); +#endif + map_destroy(mymtd); +#ifdef CONFIG_MTD_CONCAT + for(i=0; i #include #include +#include +#include #include #include #include @@ -26,127 +28,72 @@ 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 + .name = "IQ80310 flash", + .size = WINDOW_SIZE, + .bankwidth = BUSWIDTH, + .phys = WINDOW_ADDR }; static struct mtd_partition iq80310_partitions[4] = { { - name: "Firmware", - size: 0x00080000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ + .name = "Firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ },{ - name: "Kernel", - size: 0x000a0000, - offset: 0x00080000, + .name = "Kernel", + .size = 0x000a0000, + .offset = 0x00080000, },{ - name: "Filesystem", - size: 0x00600000, - offset: 0x00120000 + .name = "Filesystem", + .size = 0x00600000, + .offset = 0x00120000 },{ - name: "RedBoot", - size: 0x000e0000, - offset: 0x00720000, - mask_flags: MTD_WRITEABLE + .name = "RedBoot", + .size = 0x000e0000, + .offset = 0x00720000, + .mask_flags = MTD_WRITEABLE } }; -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - static struct mtd_info *mymtd; static struct mtd_partition *parsed_parts; - -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; static int __init init_iq80310(void) { struct mtd_partition *parts; int nb_parts = 0; int parsed_nr_parts = 0; - char *part_type = "static"; + int ret; - iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!iq80310_map.map_priv_1) { + iq80310_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + if (!iq80310_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + simple_map_init(&iq80310_map); + mymtd = do_map_probe("cfi_probe", &iq80310_map); if (!mymtd) { - iounmap((void *)iq80310_map.map_priv_1); + iounmap((void *)iq80310_map.virt); return -ENXIO; } - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; -#ifdef CONFIG_MTD_REDBOOT_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_redboot_partitions(mymtd, &parsed_parts); + ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); - if (ret > 0) { - part_type = "RedBoot"; + if (ret > 0) parsed_nr_parts = ret; - } - } -#endif if (parsed_nr_parts > 0) { parts = parsed_parts; nb_parts = parsed_nr_parts; } else { parts = iq80310_partitions; - nb_parts = NB_OF(iq80310_partitions); + nb_parts = ARRAY_SIZE(iq80310_partitions); } - printk(KERN_NOTICE "Using %s partition definition\n", part_type); add_mtd_partitions(mymtd, parts, nb_parts); return 0; } @@ -159,8 +106,8 @@ if (parsed_parts) kfree(parsed_parts); } - if (iq80310_map.map_priv_1) - iounmap((void *)iq80310_map.map_priv_1); + if (iq80310_map.virt) + iounmap((void *)iq80310_map.virt); } module_init(init_iq80310); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ixp2000.c @@ -0,0 +1,280 @@ +/* + * $Id: ixp2000.c,v 1.5 2004/11/16 17:15:48 dsaxena Exp $ + * + * drivers/mtd/maps/ixp2000.c + * + * Mapping for the Intel XScale IXP2000 based systems + * + * Copyright (C) 2002 Intel Corp. + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * Original Author: Naeem M Afzal + * Maintainer: Deepak Saxena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct ixp2000_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *res; + int nr_banks; +}; + +static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) +{ + unsigned long (*set_bank)(unsigned long) = + (unsigned long(*)(unsigned long))map->map_priv_2; + + return (set_bank ? set_bank(ofs) : ofs); +} + +#ifdef __ARMEB__ +/* + * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which + * causes the lower address bits to be XORed with 0x11 on 8 bit accesses + * and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44. + */ +static int erratum44_workaround = 0; + +static inline unsigned long address_fix8_write(unsigned long addr) +{ + if (erratum44_workaround) { + return (addr ^ 3); + } + return addr; +} +#else + +#define address_fix8_write(x) (x) +#endif + +static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs) +{ + map_word val; + + val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs))); + return val; +} + +/* + * We can't use the standard memcpy due to the broken SlowPort + * address translation on rev A0 and A1 silicon and the fact that + * we have banked flash. + */ +static void ixp2000_flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + from = flash_bank_setup(map, from); + while(len--) + *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); +} + +static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs) +{ + *(__u8 *) (address_fix8_write(map->map_priv_1 + + flash_bank_setup(map, ofs))) = d.x[0]; +} + +static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + to = flash_bank_setup(map, to); + while(len--) { + unsigned long tmp = address_fix8_write(map->map_priv_1 + to++); + *(__u8 *)(tmp) = *(__u8 *)(from++); + } +} + + +static int ixp2000_flash_remove(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp2000_flash_info *info = dev_get_drvdata(&dev->dev); + + dev_set_drvdata(&dev->dev, NULL); + + if(!info) + return 0; + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->map.map_priv_1) + iounmap((void *) info->map.map_priv_1); + + if (info->partitions) { + kfree(info->partitions); } + + if (info->res) { + release_resource(info->res); + kfree(info->res); + } + + if (plat->exit) + plat->exit(); + + return 0; +} + + +static int ixp2000_flash_probe(struct device *_dev) +{ + static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + struct platform_device *dev = to_platform_device(_dev); + struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; + struct flash_platform_data *plat; + struct ixp2000_flash_info *info; + unsigned long window_size; + int err = -1; + + if (!ixp_data) + return -ENODEV; + + plat = ixp_data->platform_data; + if (!plat) + return -ENODEV; + + window_size = dev->resource->end - dev->resource->start + 1; + dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", + ixp_data->nr_banks, ((u32)window_size >> 20)); + + if (plat->width != 1) { + dev_err(_dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n", + plat->width * 8); + return -EIO; + } + + info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct ixp2000_flash_info)); + + dev_set_drvdata(&dev->dev, info); + + /* + * Tell the MTD layer we're not 1:1 mapped so that it does + * not attempt to do a direct access on us. + */ + info->map.phys = NO_XIP; + + info->nr_banks = ixp_data->nr_banks; + info->map.size = ixp_data->nr_banks * window_size; + info->map.bankwidth = 1; + + /* + * map_priv_2 is used to store a ptr to to the bank_setup routine + */ + info->map.map_priv_2 = (void __iomem *) ixp_data->bank_setup; + + info->map.name = dev->dev.bus_id; + info->map.read = ixp2000_flash_read8; + info->map.write = ixp2000_flash_write8; + info->map.copy_from = ixp2000_flash_copy_from; + info->map.copy_to = ixp2000_flash_copy_to; + + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + dev->dev.bus_id); + if (!info->res) { + dev_err(_dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto Error; + } + + info->map.map_priv_1 = ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); + if (!info->map.map_priv_1) { + dev_err(_dev, "Failed to ioremap flash region\n"); + err = -EIO; + goto Error; + } + + /* + * Setup read mode for FLASH + */ + *IXP2000_SLOWPORT_FRM = 1; + +#if defined(__ARMEB__) + /* + * Enable erratum 44 workaround for NPUs with broken slowport + */ + + erratum44_workaround = ixp2000_has_broken_slowport(); + dev_info(_dev, "Erratum 44 workaround %s\n", + erratum44_workaround ? "enabled" : "disabled"); +#endif + + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + dev_err(_dev, "map_probe failed\n"); + err = -ENXIO; + goto Error; + } + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if(err) + dev_err(_dev, "Could not parse partitions\n"); + } + + if (err) + goto Error; + + return 0; + +Error: + ixp2000_flash_remove(_dev); + return err; +} + +static struct device_driver ixp2000_flash_driver = { + .name = "IXP2000-Flash", + .bus = &platform_bus_type, + .probe = &ixp2000_flash_probe, + .remove = &ixp2000_flash_remove +}; + +static int __init ixp2000_flash_init(void) +{ + return driver_register(&ixp2000_flash_driver); +} + +static void __exit ixp2000_flash_exit(void) +{ + driver_unregister(&ixp2000_flash_driver); +} + +module_init(ixp2000_flash_init); +module_exit(ixp2000_flash_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Deepak Saxena "); + --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ixp4xx.c @@ -0,0 +1,259 @@ +/* + * $Id: ixp4xx.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $ + * + * drivers/mtd/maps/ixp4xx.c + * + * MTD Map file for IXP4XX based systems. Please do not make per-board + * changes in here. If your board needs special setup, do it in your + * platform level code in arch/arm/mach-ixp4xx/board-setup.c + * + * Original Author: Intel Corporation + * Maintainer: Deepak Saxena + * + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef __ARMEB__ +#define BYTE0(h) ((h) & 0xFF) +#define BYTE1(h) (((h) >> 8) & 0xFF) +#else +#define BYTE0(h) (((h) >> 8) & 0xFF) +#define BYTE1(h) ((h) & 0xFF) +#endif + +static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) +{ + map_word val; + val.x[0] = *(__u16 *) (map->map_priv_1 + ofs); + return val; +} + +/* + * The IXP4xx expansion bus only allows 16-bit wide acceses + * when attached to a 16-bit wide device (such as the 28F128J3A), + * so we can't just memcpy_fromio(). + */ +static void ixp4xx_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + int i; + u8 *dest = (u8 *) to; + u16 *src = (u16 *) (map->map_priv_1 + from); + u16 data; + + for (i = 0; i < (len / 2); i++) { + data = src[i]; + dest[i * 2] = BYTE0(data); + dest[i * 2 + 1] = BYTE1(data); + } + + if (len & 1) + dest[len - 1] = BYTE0(src[i]); +} + +/* + * Unaligned writes are ignored, causing the 8-bit + * probe to fail and proceed to the 16-bit probe (which succeeds). + */ +static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr) +{ + if (!(adr & 1)) + *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; +} + +/* + * Fast write16 function without the probing check above + */ +static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) +{ + *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; +} + +struct ixp4xx_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *res; +}; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static int ixp4xx_flash_remove(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev); + map_word d; + + dev_set_drvdata(&dev->dev, NULL); + + if(!info) + return 0; + + /* + * This is required for a soft reboot to work. + */ + d.x[0] = 0xff; + ixp4xx_write16(&info->map, d, 0x55 * 0x2); + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->map.map_priv_1) + iounmap((void *) info->map.map_priv_1); + + if (info->partitions) + kfree(info->partitions); + + if (info->res) { + release_resource(info->res); + kfree(info->res); + } + + if (plat->exit) + plat->exit(); + + /* Disable flash write */ + *IXP4XX_EXP_CS0 &= ~IXP4XX_FLASH_WRITABLE; + + return 0; +} + +static int ixp4xx_flash_probe(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp4xx_flash_info *info; + int err = -1; + + if (!plat) + return -ENODEV; + + if (plat->init) { + err = plat->init(); + if (err) + return err; + } + + info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct ixp4xx_flash_info)); + + dev_set_drvdata(&dev->dev, info); + + /* + * Enable flash write + * TODO: Move this out to board specific code + */ + *IXP4XX_EXP_CS0 |= IXP4XX_FLASH_WRITABLE; + + /* + * Tell the MTD layer we're not 1:1 mapped so that it does + * not attempt to do a direct access on us. + */ + info->map.phys = NO_XIP; + info->map.size = dev->resource->end - dev->resource->start + 1; + + /* + * We only support 16-bit accesses for now. If and when + * any board use 8-bit access, we'll fixup the driver to + * handle that. + */ + info->map.bankwidth = 2; + info->map.name = dev->dev.bus_id; + info->map.read = ixp4xx_read16, + info->map.write = ixp4xx_probe_write16, + info->map.copy_from = ixp4xx_copy_from, + + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + "IXP4XXFlash"); + if (!info->res) { + printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n"); + err = -ENOMEM; + goto Error; + } + + info->map.map_priv_1 = ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); + if (!info->map.map_priv_1) { + printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); + err = -EIO; + goto Error; + } + + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + printk(KERN_ERR "IXP4XXFlash: map_probe failed\n"); + err = -ENXIO; + goto Error; + } + info->mtd->owner = THIS_MODULE; + + /* Use the fast version */ + info->map.write = ixp4xx_write16, + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if(err) + printk(KERN_ERR "Could not parse partitions\n"); + } + + if (err) + goto Error; + + return 0; + +Error: + ixp4xx_flash_remove(_dev); + return err; +} + +static struct device_driver ixp4xx_flash_driver = { + .name = "IXP4XX-Flash", + .bus = &platform_bus_type, + .probe = ixp4xx_flash_probe, + .remove = ixp4xx_flash_remove, +}; + +static int __init ixp4xx_flash_init(void) +{ + return driver_register(&ixp4xx_flash_driver); +} + +static void __exit ixp4xx_flash_exit(void) +{ + driver_unregister(&ixp4xx_flash_driver); +} + + +module_init(ixp4xx_flash_init); +module_exit(ixp4xx_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems") +MODULE_AUTHOR("Deepak Saxena"); + --- linux-2.4.21/drivers/mtd/maps/l440gx.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/l440gx.c @@ -1,5 +1,5 @@ /* - * $Id: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $ + * $Id: l440gx.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. * @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -27,51 +28,9 @@ static struct mtd_info *mymtd; -__u8 l440gx_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 l440gx_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 l440gx_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} /* Is this really the vpp port? */ -void l440gx_set_vpp(struct map_info *map, int vpp) +static void l440gx_set_vpp(struct map_info *map, int vpp) { unsigned long l; @@ -84,23 +43,16 @@ outl(l, VPP_PORT); } -struct map_info l440gx_map = { - name: "L440GX BIOS", - size: WINDOW_SIZE, - buswidth: BUSWIDTH, - read8: l440gx_read8, - read16: l440gx_read16, - read32: l440gx_read32, - copy_from: l440gx_copy_from, - write8: l440gx_write8, - write16: l440gx_write16, - write32: l440gx_write32, - copy_to: l440gx_copy_to, +static struct map_info l440gx_map = { + .name = "L440GX BIOS", + .size = WINDOW_SIZE, + .bankwidth = BUSWIDTH, + .phys = WINDOW_ADDR, #if 0 /* FIXME verify that this is the * appripriate code for vpp enable/disable */ - set_vpp: l440gx_set_vpp + .set_vpp = l440gx_set_vpp #endif }; @@ -113,7 +65,6 @@ dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, NULL); - pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); @@ -122,15 +73,14 @@ return -ENODEV; } + l440gx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); - l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); - - if (!l440gx_map.map_priv_1) { + if (!l440gx_map.virt) { printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); return -ENOMEM; } - - printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1); + simple_map_init(&l440gx_map); + printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); /* Setup the pm iobase resource * This code should move into some kind of generic bridge @@ -153,7 +103,7 @@ /* Allocate the resource region */ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { printk(KERN_WARNING "Could not allocate pm iobase resource\n"); - iounmap((void *)l440gx_map.map_priv_1); + iounmap(l440gx_map.virt); return -ENXIO; } } @@ -181,13 +131,13 @@ mymtd = do_map_probe("map_rom", &l440gx_map); } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; add_mtd_device(mymtd); return 0; } - iounmap((void *)l440gx_map.map_priv_1); + iounmap(l440gx_map.virt); return -ENXIO; } @@ -196,7 +146,7 @@ del_mtd_device(mymtd); map_destroy(mymtd); - iounmap((void *)l440gx_map.map_priv_1); + iounmap(l440gx_map.virt); } module_init(init_l440gx); --- linux-2.4.21/drivers/mtd/maps/lasat.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/lasat.c @@ -1,111 +1,73 @@ /* - * Flash device on lasat 100 and 200 boards + * Flash device on Lasat 100 and 200 boards * - * Presumably (C) 2002 Brian Murphy or whoever he - * works for. + * (C) 2002 Brian Murphy * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * - * $Id: lasat.c,v 1.1 2003/01/24 14:26:38 dwmw2 Exp $ + * $Id: lasat.c,v 1.9 2004/11/04 13:24:15 gleixner Exp $ * */ #include #include #include +#include #include #include #include #include #include #include -#include - -static struct mtd_info *mymtd; - -static __u8 sp_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -static __u16 sp_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -static __u32 sp_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -static void sp_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} -static void sp_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} +static struct mtd_info *lasat_mtd; -static void sp_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} +static struct mtd_partition partition_info[LASAT_MTD_LAST]; +static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; -static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void lasat_set_vpp(struct map_info *map, int vpp) { - memcpy_toio(map->map_priv_1 + to, from, len); + if (vpp) + *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; + else + *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit); } -static struct map_info sp_map = { - .name = "SP flash", - .buswidth = 4, - .read8 = sp_read8, - .read16 = sp_read16, - .read32 = sp_read32, - .copy_from = sp_copy_from, - .write8 = sp_write8, - .write16 = sp_write16, - .write32 = sp_write32, - .copy_to = sp_copy_to +static struct map_info lasat_map = { + .name = "LASAT flash", + .bankwidth = 4, + .set_vpp = lasat_set_vpp }; -static struct mtd_partition partition_info[LASAT_MTD_LAST]; -static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; - -static int __init init_sp(void) +static int __init init_lasat(void) { int i; - /* this does not play well with the old flash code which - * protects and uprotects the flash when necessary */ + /* since we use AMD chips and set_vpp is not implimented + * for these (yet) we still have to permanently enable flash write */ printk(KERN_NOTICE "Unprotecting flash\n"); - *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; + ENABLE_VPP((&lasat_map)); - sp_map.map_priv_1 = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); - sp_map.size = lasat_board_info.li_flash_size; + lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); + lasat_map.virt = ioremap_nocache( + lasat_map.phys, lasat_board_info.li_flash_size); + lasat_map.size = lasat_board_info.li_flash_size; - printk(KERN_NOTICE "sp flash device: %lx at %lx\n", - sp_map.size, sp_map.map_priv_1); + simple_map_init(&lasat_map); for (i=0; i < LASAT_MTD_LAST; i++) partition_info[i].name = lasat_mtd_partnames[i]; - mymtd = do_map_probe("cfi_probe", &sp_map); - if (mymtd) { + lasat_mtd = do_map_probe("cfi_probe", &lasat_map); + + if (!lasat_mtd) + lasat_mtd = do_map_probe("jedec_probe", &lasat_map); + + if (lasat_mtd) { u32 size, offset = 0; - mymtd->module = THIS_MODULE; + lasat_mtd->owner = THIS_MODULE; for (i=0; i < LASAT_MTD_LAST; i++) { size = lasat_flash_partition_size(i); @@ -114,26 +76,26 @@ offset += size; } - add_mtd_partitions( mymtd, partition_info, LASAT_MTD_LAST ); + add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST ); return 0; } return -ENXIO; } -static void __exit cleanup_sp(void) +static void __exit cleanup_lasat(void) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); + if (lasat_mtd) { + del_mtd_partitions(lasat_mtd); + map_destroy(lasat_mtd); } - if (sp_map.map_priv_1) { - sp_map.map_priv_1 = 0; + if (lasat_map.virt) { + lasat_map.virt = 0; } } -module_init(init_sp); -module_exit(cleanup_sp); +module_init(init_lasat); +module_exit(cleanup_lasat); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Brian Murphy "); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/lubbock-flash.c @@ -0,0 +1,168 @@ +/* + * $Id: lubbock-flash.c,v 1.19 2004/11/04 13:24:15 gleixner Exp $ + * + * Map driver for the Lubbock developer platform. + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define ROM_ADDR 0x00000000 +#define FLASH_ADDR 0x04000000 + +#define WINDOW_SIZE 64*1024*1024 + +static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) +{ + consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); +} + +static struct map_info lubbock_maps[2] = { { + .size = WINDOW_SIZE, + .phys = 0x00000000, + .inval_cache = lubbock_map_inval_cache, +}, { + .size = WINDOW_SIZE, + .phys = 0x04000000, + .inval_cache = lubbock_map_inval_cache, +} }; + +static struct mtd_partition lubbock_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ + .name = "Kernel", + .size = 0x00100000, + .offset = 0x00040000, + },{ + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00140000 + } +}; + +static struct mtd_info *mymtds[2]; +static struct mtd_partition *parsed_parts[2]; +static int nr_parsed_parts[2]; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static int __init init_lubbock(void) +{ + int flashboot = (LUB_CONF_SWITCHES & 1); + int ret = 0, i; + + lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth = + (BOOT_DEF & 1) ? 2 : 4; + + /* Compensate for the nROMBT switch which swaps the flash banks */ + printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", + flashboot?"Flash":"ROM", flashboot); + + lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; + lubbock_maps[flashboot].name = "Lubbock Boot ROM"; + + for (i = 0; i < 2; i++) { + lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE); + if (!lubbock_maps[i].virt) { + printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); + if (!ret) + ret = -ENOMEM; + continue; + } + lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE); + if (!lubbock_maps[i].cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); + simple_map_init(&lubbock_maps[i]); + + printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", + lubbock_maps[i].name, lubbock_maps[i].phys, + lubbock_maps[i].bankwidth * 8); + + mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); + + if (!mymtds[i]) { + iounmap((void *)lubbock_maps[i].virt); + if (lubbock_maps[i].cached) + iounmap(lubbock_maps[i].cached); + if (!ret) + ret = -EIO; + continue; + } + mymtds[i]->owner = THIS_MODULE; + + ret = parse_mtd_partitions(mymtds[i], probes, + &parsed_parts[i], 0); + + if (ret > 0) + nr_parsed_parts[i] = ret; + } + + if (!mymtds[0] && !mymtds[1]) + return ret; + + for (i = 0; i < 2; i++) { + if (!mymtds[i]) { + printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); + } else if (nr_parsed_parts[i]) { + add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); + } else if (!i) { + printk("Using static partitions on %s\n", lubbock_maps[i].name); + add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); + } else { + printk("Registering %s as whole device\n", lubbock_maps[i].name); + add_mtd_device(mymtds[i]); + } + } + return 0; +} + +static void __exit cleanup_lubbock(void) +{ + int i; + for (i = 0; i < 2; i++) { + if (!mymtds[i]) + continue; + + if (nr_parsed_parts[i] || !i) + del_mtd_partitions(mymtds[i]); + else + del_mtd_device(mymtds[i]); + + map_destroy(mymtds[i]); + iounmap((void *)lubbock_maps[i].virt); + if (lubbock_maps[i].cached) + iounmap(lubbock_maps[i].cached); + + if (parsed_parts[i]) + kfree(parsed_parts[i]); + } +} + +module_init(init_lubbock); +module_exit(cleanup_lubbock); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Pitre "); +MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/map_funcs.c @@ -0,0 +1,44 @@ +/* + * $Id: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 Exp $ + * + * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS + * is enabled. + */ + +#include +#include + +#include + +static map_word simple_map_read(struct map_info *map, unsigned long ofs) +{ + return inline_map_read(map, ofs); +} + +static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs) +{ + inline_map_write(map, datum, ofs); +} + +static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + inline_map_copy_from(map, to, from, len); +} + +static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + inline_map_copy_to(map, to, from, len); +} + +void simple_map_init(struct map_info *map) +{ + BUG_ON(!map_bankwidth_supported(map->bankwidth)); + + map->read = simple_map_read; + map->write = simple_map_write; + map->copy_from = simple_map_copy_from; + map->copy_to = simple_map_copy_to; +} + +EXPORT_SYMBOL(simple_map_init); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/maps/mbx860.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/mbx860.c @@ -1,5 +1,5 @@ /* - * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $ + * $Id: mbx860.c,v 1.8 2004/11/04 13:24:15 gleixner Exp $ * * Handle mapping of the flash on MBX860 boards * @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -36,91 +37,46 @@ * 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: "MBX flash BOOT partition", - offset: 0, - size: BOOT_PARTITION_SIZE_KiB*1024 }, - { name: "MBX flash DATA partition", - offset: BOOT_PARTITION_SIZE_KiB*1024, - size: (KERNEL_PARTITION_SIZE_KiB)*1024 }, - { name: "MBX flash APPLICATION partition", - offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } + { .name = "MBX flash BOOT partition", + .offset = 0, + .size = BOOT_PARTITION_SIZE_KiB*1024 }, + { .name = "MBX flash DATA partition", + .offset = BOOT_PARTITION_SIZE_KiB*1024, + .size = (KERNEL_PARTITION_SIZE_KiB)*1024 }, + { .name = "MBX flash APPLICATION partition", + .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } }; static struct mtd_info *mymtd; -__u8 mbx_read8(struct map_info *map, unsigned long ofs) -{ - return readb(map->map_priv_1 + ofs); -} - -__u16 mbx_read16(struct map_info *map, unsigned long ofs) -{ - return readw(map->map_priv_1 + ofs); -} - -__u32 mbx_read32(struct map_info *map, unsigned long ofs) -{ - return readl(map->map_priv_1 + ofs); -} - -void mbx_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 mbx_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -void mbx_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -void mbx_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -void mbx_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 mbx_map = { - name: "MBX flash", - size: WINDOW_SIZE, - buswidth: 4, - read8: mbx_read8, - read16: mbx_read16, - read32: mbx_read32, - copy_from: mbx_copy_from, - write8: mbx_write8, - write16: mbx_write16, - write32: mbx_write32, - copy_to: mbx_copy_to + .name = "MBX flash", + .size = WINDOW_SIZE, + .phys = WINDOW_ADDR, + .bankwidth = 4, }; int __init init_mbx(void) { - printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); - mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); + mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); - if (!mbx_map.map_priv_1) { + if (!mbx_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + simple_map_init(&mbx_map); + mymtd = do_map_probe("jedec_probe", &mbx_map); if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; add_mtd_device(mymtd); add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); return 0; } - iounmap((void *)mbx_map.map_priv_1); + iounmap((void *)mbx_map.virt); return -ENXIO; } @@ -130,9 +86,9 @@ del_mtd_device(mymtd); map_destroy(mymtd); } - if (mbx_map.map_priv_1) { - iounmap((void *)mbx_map.map_priv_1); - mbx_map.map_priv_1 = 0; + if (mbx_map.virt) { + iounmap((void *)mbx_map.virt); + mbx_map.virt = 0; } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/mpc1211.c @@ -0,0 +1,81 @@ +/* + * Flash on MPC-1211 + * + * $Id: mpc1211.c,v 1.4 2004/09/16 23:27:13 gleixner Exp $ + * + * (C) 2002 Interface, Saito.K & Jeanne + * + * GPL'd + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash_mtd; +static struct mtd_partition *parsed_parts; + +struct map_info mpc1211_flash_map = { + .name = "MPC-1211 FLASH", + .size = 0x80000, + .bankwidth = 1, +}; + +static struct mtd_partition mpc1211_partitions[] = { + { + .name = "IPL & ETH-BOOT", + .offset = 0x00000000, + .size = 0x10000, + }, + { + .name = "Flash FS", + .offset = 0x00010000, + .size = MTDPART_SIZ_FULL, + } +}; + +static int __init init_mpc1211_maps(void) +{ + int nr_parts; + + mpc1211_flash_map.phys = 0; + mpc1211_flash_map.virt = (void __iomem *)P2SEGADDR(0); + + simple_map_init(&mpc1211_flash_map); + + printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); + flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map); + if (!flash_mtd) { + printk(KERN_NOTICE "Flash chips not detected at either possible location.\n"); + return -ENXIO; + } + printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff); + flash_mtd->module = THIS_MODULE; + + parsed_parts = mpc1211_partitions; + nr_parts = ARRAY_SIZE(mpc1211_partitions); + + add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + return 0; +} + +static void __exit cleanup_mpc1211_maps(void) +{ + if (parsed_parts) + del_mtd_partitions(flash_mtd); + else + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +} + +module_init(init_mpc1211_maps); +module_exit(cleanup_mpc1211_maps); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Saito.K & Jeanne "); +MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface"); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/mphysmap.c @@ -0,0 +1,282 @@ +/* + * $Id: mphysmap.c,v 1.2 2005/03/07 23:15:48 joern Exp $ + * + * Several mappings of NOR chips + * + * Copyright (c) 2001-2005 Jörn Engel + */ +#include +#include +#include +#include + + +#define NO_DEVICES 8 +struct map_info maps[NO_DEVICES] = { +#if CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_1_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_1_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_1_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_1_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_2_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_2_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_2_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_2_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_3_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_3_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_3_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_3_WIDTH, + }, +#endif +#if CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH + { + .name = CONFIG_MTD_MULTI_PHYSMAP_4_NAME, + .phys = CONFIG_MTD_MULTI_PHYSMAP_4_START, + .size = CONFIG_MTD_MULTI_PHYSMAP_4_LEN, + .bankwidth = CONFIG_MTD_MULTI_PHYSMAP_4_WIDTH, + }, +#endif + { + .name = NULL, + }, +}; +DECLARE_MUTEX(map_mutex); + + +static int map_one(struct map_info *map) +{ + struct mtd_info *mtd; + + map->virt = ioremap(map->phys, map->size); + if (!map->virt) + return -EIO; + + simple_map_init(map); + + mtd = do_map_probe("cfi_probe", map); + if (!mtd) { + iounmap(map->virt); + return -ENXIO; + } + + map->map_priv_1 = (unsigned long)mtd; + mtd->owner = THIS_MODULE; + /* TODO: partitioning */ + return add_mtd_device(mtd); +} + + +static void unmap_one(struct map_info *map) +{ + struct mtd_info *mtd = (struct mtd_info*)map->map_priv_1; + + if (map->map_priv_2) + kfree(map->name); + + if (!map->virt) + return; + + BUG_ON(!mtd); + BUG_ON(map->map_priv_2 > 1); + + del_mtd_device(mtd); + map_destroy(mtd); + iounmap(map->virt); + + map->map_priv_1 = 0; + map->map_priv_2 = 0; + map->virt = NULL; +} + + +static struct map_info *next_free_map(void) +{ + int i; + for (i=0; ivirt) + return map; + } + return NULL; +} + + +static int add_one_map(const char *name, unsigned long start, + unsigned long len, int width) +{ + struct map_info *map = next_free_map(); + if (!map) + return -ENOSPC; + + map->name = kmalloc(strlen(name)+1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(map->name, name); + map->phys = start; + map->size = len; + map->bankwidth = width; + map->map_priv_2 = 1; /* marker to free map->name */ + + return map_one(map); +} + + +static int parse_ulong(unsigned long *num, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num = n; + return 0; +} + + +static int parse_uint(unsigned int *num, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + if ((int)n != n) + return -EINVAL; + + *num = n; + return 0; +} + + +static int parse_name(char **pname, const char *token, int limit) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > limit) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + memcpy(name, token, len); + + *pname = name; + return 0; +} + + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + +#define parse_err(fmt, args...) do { \ + printk(KERN_ERR fmt "\n", ## args); \ + return 0; \ +} while(0) + +/* mphysmap=name,start,len,width */ +static int mphysmap_setup(const char *val, struct kernel_param *kp) +{ + char buf[64+14+14+14], *str = buf; + char *token[4]; + char *name; + unsigned long start; + unsigned long len; + unsigned int width; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long"); + + strcpy(str, val); + kill_final_newline(str); + + for (i=0; i<4; i++) + token[i] = strsep(&str, ","); + + if (str) + parse_err("too many arguments"); + if (!token[3]) + parse_err("not enough arguments"); + + ret = parse_name(&name, token[0], 64); + if (ret == -ENOMEM) + parse_err("out of memory"); + if (ret == -ENOSPC) + parse_err("name too long"); + if (ret) + parse_err("illegal name: %d", ret); + + ret = parse_ulong(&start, token[1]); + if (ret) + parse_err("illegal start address"); + + ret = parse_ulong(&len, token[2]); + if (ret) + parse_err("illegal length"); + + ret = parse_uint(&width, token[3]); + if (ret) + parse_err("illegal bus width"); + + down(&map_mutex); + ret = add_one_map(name, start, len, width); + up(&map_mutex); + if (ret == -ENOSPC) + parse_err("no free space for new map"); + if (ret) + parse_err("error while mapping: %d", ret); + + return 0; +} + + +static int __init mphysmap_init(void) +{ + int i; + down(&map_mutex); + for (i=0; i"); +MODULE_DESCRIPTION("Generic configurable extensible MTD map driver"); --- linux-2.4.21/drivers/mtd/maps/netsc520.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/netsc520.c @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: netsc520.c,v 1.13 2004/11/28 09:40:40 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 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -50,95 +51,41 @@ ** 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 boot kernel", + .offset = 0, + .size = 0xc0000 }, { - name: "NetSc520 Low BIOS", - offset: 0xc0000, - size: 0x40000 + .name = "NetSc520 Low BIOS", + .offset = 0xc0000, + .size = 0x40000 }, { - name: "NetSc520 file system", - offset: 0x100000, - size: 0xe80000 + .name = "NetSc520 file system", + .offset = 0x100000, + .size = 0xe80000 }, { - name: "NetSc520 High BIOS", - offset: 0xf80000, - size: 0x80000 + .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 + .name = "netsc520 Flash Bank", + .size = WINDOW_SIZE, + .bankwidth = 4, + .phys = WINDOW_ADDR, }; #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) @@ -147,13 +94,16 @@ 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); + printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); + netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); - if (!netsc520_map.map_priv_1) { + if (!netsc520_map.virt) { printk("Failed to ioremap_nocache\n"); return -EIO; } + + simple_map_init(&netsc520_map); + mymtd = do_map_probe("cfi_probe", &netsc520_map); if(!mymtd) mymtd = do_map_probe("map_ram", &netsc520_map); @@ -161,11 +111,11 @@ mymtd = do_map_probe("map_rom", &netsc520_map); if (!mymtd) { - iounmap((void *)netsc520_map.map_priv_1); + iounmap(netsc520_map.virt); return -ENXIO; } - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); return 0; } @@ -176,9 +126,9 @@ 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; + if (netsc520_map.virt) { + iounmap(netsc520_map.virt); + netsc520_map.virt = NULL; } } --- linux-2.4.21/drivers/mtd/maps/nettel.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/nettel.c @@ -6,7 +6,7 @@ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) * - * $Id: nettel.c,v 1.1 2002/08/08 06:30:13 gerg Exp $ + * $Id: nettel.c,v 1.10 2005/01/05 17:11:29 dwmw2 Exp $ */ /****************************************************************************/ @@ -59,128 +59,72 @@ /****************************************************************************/ -static __u8 nettel_read8(struct map_info *map, unsigned long ofs) -{ - return(readb(map->map_priv_1 + ofs)); -} - -static __u16 nettel_read16(struct map_info *map, unsigned long ofs) -{ - return(readw(map->map_priv_1 + ofs)); -} - -static __u32 nettel_read32(struct map_info *map, unsigned long ofs) -{ - return(readl(map->map_priv_1 + ofs)); -} - -static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} - /****************************************************************************/ #ifdef CONFIG_MTD_CFI_INTELEXT static struct map_info nettel_intel_map = { - name: "SnapGear Intel", - size: 0, - buswidth: INTEL_BUSWIDTH, - read8: nettel_read8, - read16: nettel_read16, - read32: nettel_read32, - copy_from: nettel_copy_from, - write8: nettel_write8, - write16: nettel_write16, - write32: nettel_write32, - copy_to: nettel_copy_to + .name = "SnapGear Intel", + .size = 0, + .bankwidth = INTEL_BUSWIDTH, }; static struct mtd_partition nettel_intel_partitions[] = { { - name: "SnapGear kernel", - offset: 0, - size: 0x000e0000 + .name = "SnapGear kernel", + .offset = 0, + .size = 0x000e0000 }, { - name: "SnapGear filesystem", - offset: 0x00100000, + .name = "SnapGear filesystem", + .offset = 0x00100000, }, { - name: "SnapGear config", - offset: 0x000e0000, - size: 0x00020000 + .name = "SnapGear config", + .offset = 0x000e0000, + .size = 0x00020000 }, { - name: "SnapGear Intel", - offset: 0 + .name = "SnapGear Intel", + .offset = 0 }, { - name: "SnapGear BIOS Config", - offset: 0x007e0000, - size: 0x00020000 + .name = "SnapGear BIOS Config", + .offset = 0x007e0000, + .size = 0x00020000 }, { - name: "SnapGear BIOS", - offset: 0x007e0000, - size: 0x00020000 + .name = "SnapGear BIOS", + .offset = 0x007e0000, + .size = 0x00020000 }, }; #endif static struct map_info nettel_amd_map = { - name: "SnapGear AMD", - size: AMD_WINDOW_MAXSIZE, - buswidth: AMD_BUSWIDTH, - read8: nettel_read8, - read16: nettel_read16, - read32: nettel_read32, - copy_from: nettel_copy_from, - write8: nettel_write8, - write16: nettel_write16, - write32: nettel_write32, - copy_to: nettel_copy_to + .name = "SnapGear AMD", + .size = AMD_WINDOW_MAXSIZE, + .bankwidth = AMD_BUSWIDTH, }; static struct mtd_partition nettel_amd_partitions[] = { { - name: "SnapGear BIOS config", - offset: 0x000e0000, - size: 0x00010000 + .name = "SnapGear BIOS config", + .offset = 0x000e0000, + .size = 0x00010000 }, { - name: "SnapGear BIOS", - offset: 0x000f0000, - size: 0x00010000 + .name = "SnapGear BIOS", + .offset = 0x000f0000, + .size = 0x00010000 }, { - name: "SnapGear AMD", - offset: 0 + .name = "SnapGear AMD", + .offset = 0 }, { - name: "SnapGear high BIOS", - offset: 0x001f0000, - size: 0x00010000 + .name = "SnapGear high BIOS", + .offset = 0x001f0000, + .size = 0x00010000 } }; @@ -236,7 +180,7 @@ if (mtd) { nettel_erase.mtd = mtd; nettel_erase.callback = nettel_erasecallback; - nettel_erase.callback = 0; + nettel_erase.callback = NULL; nettel_erase.addr = 0; nettel_erase.len = mtd->size; nettel_erase.priv = (u_long) &wait_q; @@ -328,18 +272,19 @@ *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); __asm__ ("wbinvd"); - nettel_amd_map.map_priv_1 = (unsigned long) - ioremap_nocache(amdaddr, maxsize); - if (!nettel_amd_map.map_priv_1) { + nettel_amd_map.phys = amdaddr; + nettel_amd_map.virt = ioremap_nocache(amdaddr, maxsize); + if (!nettel_amd_map.virt) { printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); return(-EIO); } + simple_map_init(&nettel_amd_map); if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", amd_mtd->size>>10); - amd_mtd->module = THIS_MODULE; + amd_mtd->owner = THIS_MODULE; /* The high BIOS partition is only present for 2MB units */ num_amd_partitions = NUM_AMD_PARTITIONS; @@ -387,8 +332,8 @@ /* Destroy useless AMD MTD mapping */ amd_mtd = NULL; - iounmap((void *) nettel_amd_map.map_priv_1); - nettel_amd_map.map_priv_1 = (unsigned long) NULL; + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; #else /* Only AMD flash supported */ return(-ENXIO); @@ -411,16 +356,17 @@ /* Probe for the the size of the first Intel flash */ nettel_intel_map.size = maxsize; - nettel_intel_map.map_priv_1 = (unsigned long) - ioremap_nocache(intel0addr, maxsize); - if (!nettel_intel_map.map_priv_1) { + nettel_intel_map.phys = intel0addr; + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); + if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); return(-EIO); } + simple_map_init(&nettel_intel_map); intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); - if (! intel_mtd) { - iounmap((void *) nettel_intel_map.map_priv_1); + if (!intel_mtd) { + iounmap(nettel_intel_map.virt); return(-ENXIO); } @@ -441,19 +387,18 @@ /* Delete the old map and probe again to do both chips */ map_destroy(intel_mtd); intel_mtd = NULL; - iounmap((void *) nettel_intel_map.map_priv_1); + iounmap(nettel_intel_map.virt); nettel_intel_map.size = maxsize; - nettel_intel_map.map_priv_1 = (unsigned long) - ioremap_nocache(intel0addr, maxsize); - if (!nettel_intel_map.map_priv_1) { + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); + if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); return(-EIO); } intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); if (! intel_mtd) { - iounmap((void *) nettel_intel_map.map_priv_1); + iounmap((void *) nettel_intel_map.virt); return(-ENXIO); } @@ -468,7 +413,7 @@ printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", (intel_mtd->size >> 10)); - intel_mtd->module = THIS_MODULE; + intel_mtd->owner = THIS_MODULE; #ifndef CONFIG_BLK_DEV_INITRD ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); @@ -523,18 +468,18 @@ del_mtd_partitions(amd_mtd); map_destroy(amd_mtd); } - if (nettel_amd_map.map_priv_1) { - iounmap((void *)nettel_amd_map.map_priv_1); - nettel_amd_map.map_priv_1 = 0; + if (nettel_amd_map.virt) { + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; } #ifdef CONFIG_MTD_CFI_INTELEXT if (intel_mtd) { del_mtd_partitions(intel_mtd); map_destroy(intel_mtd); } - if (nettel_intel_map.map_priv_1) { - iounmap((void *)nettel_intel_map.map_priv_1); - nettel_intel_map.map_priv_1 = 0; + if (nettel_intel_map.virt) { + iounmap(nettel_intel_map.virt); + nettel_intel_map.virt = 0; } #endif } --- linux-2.4.21/drivers/mtd/maps/ocelot.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/ocelot.c @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: ocelot.c,v 1.16 2005/01/05 18:05:13 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -20,47 +21,23 @@ #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) +static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - memcpy_fromio(to, map->map_priv_1 + from, len); -} + struct map_info *map = mtd->priv; + size_t done = 0; -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 */ + *retlen = len; while(len) { - __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); + __raw_writeb(*(unsigned char *) from, map->virt + to); from++; to++; len--; @@ -70,24 +47,21 @@ 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, + .name = "Ocelot boot flash", + .size = FLASH_WINDOW_SIZE, + .bankwidth = FLASH_BUSWIDTH, + .phys = FLASH_WINDOW_ADDR, }; 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 + .name = "Ocelot NVRAM", + .size = NVRAM_WINDOW_SIZE, + .bankwidth = NVRAM_BUSWIDTH, + .phys = NVRAM_WINDOW_ADDR, }; +static const char *probes[] = { "RedBoot", NULL }; + static int __init init_ocelot_maps(void) { void *pld; @@ -107,12 +81,13 @@ 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) { + ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); + if (!ocelot_nvram_map.virt) { printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); return -EIO; } - // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1; + + simple_map_init(&ocelot_nvram_map); /* And do the RAM probe on it to get an MTD device */ nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); @@ -120,22 +95,21 @@ printk("NVRAM probe failed\n"); goto fail_1; } - nvram_mtd->module = THIS_MODULE; + nvram_mtd->owner = THIS_MODULE; nvram_mtd->erasesize = 16; + /* Override the write() method */ + nvram_mtd->write = ocelot_ram_write; /* 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) { + ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); + if (!ocelot_flash_map.virt) { 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); + ocelot_flash_map.cached = (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; - } + simple_map_init(&ocelot_flash_map); /* Only probe for flash if the write jumper is present */ if (brd_status & 0x40) { @@ -155,10 +129,10 @@ add_mtd_device(nvram_mtd); - flash_mtd->module = THIS_MODULE; - nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); + flash_mtd->owner = THIS_MODULE; + nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); - if (nr_parts) + if (nr_parts > 0) add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); else add_mtd_device(flash_mtd); @@ -166,14 +140,13 @@ 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); + iounmap((void *)ocelot_flash_map.virt); + if (ocelot_flash_map.cached) + iounmap((void *)ocelot_flash_map.cached); fail_2: map_destroy(nvram_mtd); fail_1: - iounmap((void *)ocelot_nvram_map.map_priv_1); + iounmap((void *)ocelot_nvram_map.virt); return -ENXIO; } @@ -182,16 +155,16 @@ { del_mtd_device(nvram_mtd); map_destroy(nvram_mtd); - iounmap((void *)ocelot_nvram_map.map_priv_1); + iounmap((void *)ocelot_nvram_map.virt); 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); + iounmap((void *)ocelot_flash_map.virt); + if (ocelot_flash_map.cached) + iounmap((void *)ocelot_flash_map.cached); } module_init(init_ocelot_maps); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ocotea.c @@ -0,0 +1,154 @@ +/* + * Mapping for Ocotea user flash + * + * Matt Porter + * + * Copyright 2002-2004 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash; + +static struct map_info ocotea_small_map = { + .name = "Ocotea small flash", + .size = OCOTEA_SMALL_FLASH_SIZE, + .buswidth = 1, +}; + +static struct map_info ocotea_large_map = { + .name = "Ocotea large flash", + .size = OCOTEA_LARGE_FLASH_SIZE, + .buswidth = 1, +}; + +static struct mtd_partition ocotea_small_partitions[] = { + { + .name = "pibs", + .offset = 0x0, + .size = 0x100000, + } +}; + +static struct mtd_partition ocotea_large_partitions[] = { + { + .name = "fs", + .offset = 0, + .size = 0x300000, + }, + { + .name = "firmware", + .offset = 0x300000, + .size = 0x100000, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_ocotea(void) +{ + u8 fpga0_reg; + u8 *fpga0_adr; + unsigned long long small_flash_base, large_flash_base; + + fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16); + if (!fpga0_adr) + return -ENOMEM; + + fpga0_reg = readb((unsigned long)fpga0_adr); + iounmap(fpga0_adr); + + if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) { + small_flash_base = OCOTEA_SMALL_FLASH_HIGH; + large_flash_base = OCOTEA_LARGE_FLASH_LOW; + } + else { + small_flash_base = OCOTEA_SMALL_FLASH_LOW; + large_flash_base = OCOTEA_LARGE_FLASH_HIGH; + } + + ocotea_small_map.phys = small_flash_base; + ocotea_small_map.virt = ioremap64(small_flash_base, + ocotea_small_map.size); + + if (!ocotea_small_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_small_map); + + flash = do_map_probe("map_rom", &ocotea_small_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_small_partitions, + NB_OF(ocotea_small_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + ocotea_large_map.phys = large_flash_base; + ocotea_large_map.virt = ioremap64(large_flash_base, + ocotea_large_map.size); + + if (!ocotea_large_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_large_map); + + flash = do_map_probe("cfi_probe", &ocotea_large_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_large_partitions, + NB_OF(ocotea_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ocotea(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ocotea_small_map.virt) { + iounmap((void *)ocotea_small_map.virt); + ocotea_small_map.virt = 0; + } + + if (ocotea_large_map.virt) { + iounmap((void *)ocotea_large_map.virt); + ocotea_large_map.virt = 0; + } +} + +module_init(init_ocotea); +module_exit(cleanup_ocotea); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards"); --- linux-2.4.21/drivers/mtd/maps/octagon-5066.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.27 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -31,6 +31,7 @@ #include #include +#include #define WINDOW_START 0xe8000 #define WINDOW_LENGTH 0x8000 @@ -40,7 +41,7 @@ static volatile char page_n_dev = 0; static unsigned long iomapadr; -static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(oct5066_spin); /* * We use map_priv_1 to identify which device we are. @@ -61,32 +62,12 @@ } -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) +static map_word oct5066_read8(struct map_info *map, unsigned long ofs) { - __u32 ret; + map_word ret; spin_lock(&oct5066_spin); oct5066_page(map, ofs); - ret = readl(iomapadr + (ofs & WINDOW_MASK)); + ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); spin_unlock(&oct5066_spin); return ret; } @@ -108,27 +89,11 @@ } } -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) +static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&oct5066_spin); oct5066_page(map, adr); - writel(d, iomapadr + (adr & WINDOW_MASK)); + writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); spin_unlock(&oct5066_spin); } @@ -151,32 +116,26 @@ 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 Socket", + .phys = NO_XIP, + .size = 512 * 1024, + .bankwidth = 1, + .read = oct5066_read8, + .copy_from = oct5066_copy_from, + .write = oct5066_write8, + .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 + .name = "Octagon 5066 Internal Flash", + .phys = NO_XIP, + .size = 2 * 1024 * 1024, + .bankwidth = 1, + .read = oct5066_read8, + .copy_from = oct5066_copy_from, + .write = oct5066_write8, + .copy_to = oct5066_copy_to, + .map_priv_1 = 2<<6 } }; @@ -262,7 +221,7 @@ if (!oct5066_mtd[i]) oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); if (oct5066_mtd[i]) { - oct5066_mtd[i]->module = THIS_MODULE; + oct5066_mtd[i]->owner = THIS_MODULE; add_mtd_device(oct5066_mtd[i]); } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/omap-toto-flash.c @@ -0,0 +1,137 @@ +/* + * NOR Flash memory access on TI Toto board + * + * jzhang@ti.com (C) 2003 Texas Instruments. + * + * (C) 2002 MontVista Software, Inc. + * + * $Id: omap-toto-flash.c,v 1.3 2004/09/16 23:27:13 gleixner Exp $ + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + + +#ifndef CONFIG_ARCH_OMAP +#error This is for OMAP architecture only +#endif + +//these lines need be moved to a hardware header file +#define OMAP_TOTO_FLASH_BASE 0xd8000000 +#define OMAP_TOTO_FLASH_SIZE 0x80000 + +static struct map_info omap_toto_map_flash = { + .name = "OMAP Toto flash", + .bankwidth = 2, + .virt = (void __iomem *)OMAP_TOTO_FLASH_BASE, +}; + + +static struct mtd_partition toto_flash_partitions[] = { + { + .name = "BootLoader", + .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/ + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "ReservedSpace", + .size = 0x00030000, + .offset = MTDPART_OFS_APPEND, + //mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + .name = "EnvArea", /* bottom 64KiB for env vars */ + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct mtd_partition *parsed_parts; + +static struct mtd_info *flash_mtd; + +static int __init init_flash (void) +{ + + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; + const char *part_type; + + /* + * Static partition definition selection + */ + part_type = "static"; + + parts = toto_flash_partitions; + nb_parts = ARRAY_SIZE(toto_flash_partitions); + omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE; + omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE); + + simple_map_init(&omap_toto_map_flash); + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n", + omap_toto_map_flash.bankwidth*8); + flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash); + if (!flash_mtd) + return -ENXIO; + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } + + if (nb_parts == 0) { + printk(KERN_NOTICE "OMAP toto flash: no partition info available," + "registering whole flash at once\n"); + if (add_mtd_device(flash_mtd)){ + return -ENXIO; + } + } else { + printk(KERN_NOTICE "Using %s partition definition\n", + part_type); + return add_mtd_partitions(flash_mtd, parts, nb_parts); + } + return 0; +} + +int __init omap_toto_mtd_init(void) +{ + int status; + + if (status = init_flash()) { + printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n"); + } + return status; +} + +static void __exit omap_toto_mtd_cleanup(void) +{ + if (flash_mtd) { + del_mtd_partitions(flash_mtd); + map_destroy(flash_mtd); + if (parsed_parts) + kfree(parsed_parts); + } +} + +module_init(omap_toto_mtd_init); +module_exit(omap_toto_mtd_cleanup); + +MODULE_AUTHOR("Jian Zhang"); +MODULE_DESCRIPTION("OMAP Toto board map driver"); +MODULE_LICENSE("GPL"); --- linux-2.4.21/drivers/mtd/maps/pci.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/pci.c @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.2 2003/01/24 13:11:43 dwmw2 Exp $ + * $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. @@ -33,12 +33,80 @@ struct map_pci_info { struct map_info map; - void *base; + void __iomem *base; void (*exit)(struct pci_dev *dev, struct map_pci_info *map); unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); struct pci_dev *dev; }; +static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0]= readb(map->base + map->translate(map, ofs)); +// printk("read8 : %08lx => %02x\n", ofs, val.x[0]); + return val; +} + +#if 0 +static map_word mtd_pci_read16(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0] = readw(map->base + map->translate(map, ofs)); +// printk("read16: %08lx => %04x\n", ofs, val.x[0]); + return val; +} +#endif +static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0] = readl(map->base + map->translate(map, ofs)); +// printk("read32: %08lx => %08x\n", ofs, val.x[0]); + return val; +} + +static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_fromio(to, map->base + map->translate(map, from), len); +} + +static void mtd_pci_write8(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write8 : %08lx <= %02x\n", ofs, val.x[0]); + writeb(val.x[0], map->base + map->translate(map, ofs)); +} + +#if 0 +static void mtd_pci_write16(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write16: %08lx <= %04x\n", ofs, val.x[0]); + writew(val.x[0], map->base + map->translate(map, ofs)); +} +#endif +static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write32: %08lx <= %08x\n", ofs, val.x[0]); + writel(val.x[0], map->base + map->translate(map, ofs)); +} + +static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_toio(map->base + map->translate(map, to), from, len); +} + +static struct map_info mtd_pci_map = { + .phys = NO_XIP, + .copy_from = mtd_pci_copyfrom, + .copy_to = mtd_pci_copyto, +}; + /* * Intel IOP80310 Flash driver */ @@ -48,7 +116,10 @@ { u32 win_base; - map->map.buswidth = 1; + map->map.bankwidth = 1; + map->map.read = mtd_pci_read8, + map->map.write = mtd_pci_write8, + map->map.size = 0x00800000; map->base = ioremap_nocache(pci_resource_start(dev, 0), pci_resource_len(dev, 0)); @@ -72,7 +143,7 @@ intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map) { if (map->base) - iounmap((void *)map->base); + iounmap(map->base); pci_write_config_dword(dev, 0x44, map->map.map_priv_2); } @@ -98,10 +169,10 @@ } static struct mtd_pci_info intel_iq80310_info = { - init: intel_iq80310_init, - exit: intel_iq80310_exit, - translate: intel_iq80310_translate, - map_name: "cfi_probe", + .init = intel_iq80310_init, + .exit = intel_iq80310_exit, + .translate = intel_iq80310_translate, + .map_name = "cfi_probe", }; /* @@ -140,14 +211,16 @@ pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); val |= PCI_ROM_ADDRESS_ENABLE; pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); - printk("%s: enabling expansion ROM\n", dev->slot_name); + printk("%s: enabling expansion ROM\n", pci_name(dev)); } } if (!len || !base) return -ENXIO; - map->map.buswidth = 4; + map->map.bankwidth = 4; + map->map.read = mtd_pci_read32, + map->map.write = mtd_pci_write32, map->map.size = len; map->base = ioremap_nocache(base, len); @@ -163,7 +236,7 @@ u32 val; if (map->base) - iounmap((void *)map->base); + iounmap(map->base); /* * We need to undo the PCI BAR2/PCI ROM BAR address alteration. @@ -181,34 +254,32 @@ } static struct mtd_pci_info intel_dc21285_info = { - init: intel_dc21285_init, - exit: intel_dc21285_exit, - translate: intel_dc21285_translate, - map_name: "jedec_probe", + .init = intel_dc21285_init, + .exit = intel_dc21285_exit, + .translate = intel_dc21285_translate, + .map_name = "jedec_probe", }; /* * PCI device ID table */ -static struct pci_device_id mtd_pci_ids[] __devinitdata = { +static struct pci_device_id mtd_pci_ids[] = { { - vendor: PCI_VENDOR_ID_INTEL, - device: 0x530d, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - class: PCI_CLASS_MEMORY_OTHER << 8, - class_mask: 0xffff00, - driver_data: (unsigned long)&intel_iq80310_info, + .vendor = PCI_VENDOR_ID_INTEL, + .device = 0x530d, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_MEMORY_OTHER << 8, + .class_mask = 0xffff00, + .driver_data = (unsigned long)&intel_iq80310_info, }, { - vendor: PCI_VENDOR_ID_DEC, - device: PCI_DEVICE_ID_DEC_21285, - subvendor: 0, /* DC21285 defaults to 0 on reset */ - subdevice: 0, /* DC21285 defaults to 0 on reset */ - class: 0, - class_mask: 0, - driver_data: (unsigned long)&intel_dc21285_info, + .vendor = PCI_VENDOR_ID_DEC, + .device = PCI_DEVICE_ID_DEC_21285, + .subvendor = 0, /* DC21285 defaults to 0 on reset */ + .subdevice = 0, /* DC21285 defaults to 0 on reset */ + .driver_data = (unsigned long)&intel_dc21285_info, }, { 0, } }; @@ -217,74 +288,6 @@ * Generic code follows. */ -static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u8 val = readb(map->base + map->translate(map, ofs)); -// printk("read8 : %08lx => %02x\n", ofs, val); - return val; -} - -static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u16 val = readw(map->base + map->translate(map, ofs)); -// printk("read16: %08lx => %04x\n", ofs, val); - return val; -} - -static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u32 val = readl(map->base + map->translate(map, ofs)); -// printk("read32: %08lx => %08x\n", ofs, val); - return val; -} - -static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - memcpy_fromio(to, map->base + map->translate(map, from), len); -} - -static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write8 : %08lx <= %02x\n", ofs, val); - writeb(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write16: %08lx <= %04x\n", ofs, val); - writew(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write32: %08lx <= %08x\n", ofs, val); - writel(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - memcpy_toio(map->base + map->translate(map, to), from, len); -} - -static struct map_info mtd_pci_map = { - read8: mtd_pci_read8, - read16: mtd_pci_read16, - read32: mtd_pci_read32, - copy_from: mtd_pci_copyfrom, - write8: mtd_pci_write8, - write16: mtd_pci_write16, - write32: mtd_pci_write32, - copy_to: mtd_pci_copyto, -}; - static int __devinit mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { @@ -307,7 +310,7 @@ goto release; map->map = mtd_pci_map; - map->map.name = dev->slot_name; + map->map.name = pci_name(dev); map->dev = dev; map->exit = info->exit; map->translate = info->translate; @@ -322,7 +325,7 @@ if (!mtd) goto release; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; add_mtd_device(mtd); pci_set_drvdata(dev, mtd); @@ -359,10 +362,10 @@ } static struct pci_driver mtd_pci_driver = { - name: "MTD PCI", - probe: mtd_pci_probe, - remove: __devexit_p(mtd_pci_remove), - id_table: mtd_pci_ids, + .name = "MTD PCI", + .probe = mtd_pci_probe, + .remove = __devexit_p(mtd_pci_remove), + .id_table = mtd_pci_ids, }; static int __init mtd_pci_maps_init(void) --- linux-2.4.21/drivers/mtd/maps/pcmciamtd.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/pcmciamtd.c @@ -1,5 +1,5 @@ /* - * $Id: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $ + * $Id: pcmciamtd.c,v 1.51 2004/07/12 22:38:29 dwmw2 Exp $ * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,7 @@ #include #include +#include #ifdef CONFIG_MTD_DEBUG static int debug = CONFIG_MTD_DEBUG_VERBOSE; @@ -47,7 +49,7 @@ #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.39 $" +#define DRIVER_VERSION "$Revision: 1.51 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -71,7 +73,7 @@ /* Module parameters */ /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ -static int buswidth = 2; +static int bankwidth = 2; /* Speed of memory accesses, in ns */ static int mem_speed; @@ -91,12 +93,12 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_PARM(buswidth, "i"); -MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)"); +MODULE_PARM(bankwidth, "i"); +MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)"); MODULE_PARM(mem_speed, "i"); MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); MODULE_PARM(force_size, "i"); -MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); +MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)"); MODULE_PARM(setvpp, "i"); MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); MODULE_PARM(vpp, "i"); @@ -105,16 +107,7 @@ MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); - -static inline void cs_error(client_handle_t handle, int func, int ret) -{ - error_info_t err = { func, ret }; - CardServices(ReportError, handle, &err); -} - - /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ - static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; @@ -132,7 +125,7 @@ DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x", dev->offset, mrq.CardOffset); mrq.Page = 0; - if( (ret = CardServices(MapMemPage, win, &mrq)) != CS_SUCCESS) { + if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) { cs_error(dev->link.handle, MapMemPage, ret); return NULL; } @@ -142,32 +135,32 @@ } -static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) { caddr_t addr; - u8 d; + map_word d = {{0}}; addr = remap_window(map, ofs); if(!addr) - return 0; + return d; - d = readb(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d); + d.x[0] = readb(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); return d; } -static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) { caddr_t addr; - u16 d; + map_word d = {{0}}; addr = remap_window(map, ofs); if(!addr) - return 0; + return d; - d = readw(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d); + d.x[0] = readw(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); return d; } @@ -198,26 +191,26 @@ } -static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr) +static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr) { caddr_t addr = remap_window(map, adr); if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d); - writeb(d, addr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); + writeb(d.x[0], addr); } -static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr) +static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr) { caddr_t addr = remap_window(map, adr); if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d); - writew(d, addr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); + writew(d.x[0], addr); } @@ -251,30 +244,30 @@ #define DEV_REMOVED(x) (!(*(u_int *)x->map_priv_1 & DEV_PRESENT)) -static u8 pcmcia_read8(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) { caddr_t win_base = (caddr_t)map->map_priv_2; - u8 d; + map_word d = {{0}}; if(DEV_REMOVED(map)) - return 0; + return d; - d = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d); + d.x[0] = readb(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); return d; } -static u16 pcmcia_read16(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) { caddr_t win_base = (caddr_t)map->map_priv_2; - u16 d; + map_word d = {{0}}; if(DEV_REMOVED(map)) - return 0; + return d; - d = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d); + d.x[0] = readw(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); return d; } @@ -339,7 +332,7 @@ mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); - ret = CardServices(ModifyConfiguration, link->handle, &mod); + ret = pcmcia_modify_configuration(link->handle, &mod); if(ret != CS_SUCCESS) { cs_error(link->handle, ModifyConfiguration, ret); } @@ -351,9 +344,8 @@ * still open, this will be postponed until it is closed. */ -static void pcmciamtd_release(u_long arg) +static void pcmciamtd_release(dev_link_t *link) { - dev_link_t *link = (dev_link_t *)arg; struct pcmciamtd_dev *dev = link->priv; DEBUG(3, "link = 0x%p", link); @@ -363,9 +355,9 @@ iounmap(dev->win_base); dev->win_base = NULL; } - CardServices(ReleaseWindow, link->win); + pcmcia_release_window(link->win); } - CardServices(ReleaseConfiguration, link->handle); + pcmcia_release_configuration(link->handle); link->state &= ~DEV_CONFIG; } @@ -383,14 +375,14 @@ tuple.TupleOffset = 0; tuple.DesiredTuple = RETURN_FIRST_TUPLE; - rc = CardServices(GetFirstTuple, link->handle, &tuple); + rc = pcmcia_get_first_tuple(link->handle, &tuple); while(rc == CS_SUCCESS) { - rc = CardServices(GetTupleData, link->handle, &tuple); + rc = pcmcia_get_tuple_data(link->handle, &tuple); if(rc != CS_SUCCESS) { cs_error(link->handle, GetTupleData, rc); break; } - rc = CardServices(ParseTuple, link->handle, &tuple, &parse); + rc = pcmcia_parse_tuple(link->handle, &tuple, &parse); if(rc != CS_SUCCESS) { cs_error(link->handle, ParseTuple, rc); break; @@ -447,9 +439,9 @@ case CISTPL_DEVICE_GEO: { cistpl_device_geo_t *t = &parse.device_geo; int i; - dev->pcmcia_map.buswidth = t->geo[0].buswidth; + dev->pcmcia_map.bankwidth = t->geo[0].buswidth; for(i = 0; i < t->ngeo; i++) { - DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth); DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); @@ -463,22 +455,22 @@ DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); } - rc = CardServices(GetNextTuple, link->handle, &tuple, &parse); + rc = pcmcia_get_next_tuple(link->handle, &tuple); } if(!dev->pcmcia_map.size) dev->pcmcia_map.size = MAX_PCMCIA_ADDR; - if(!dev->pcmcia_map.buswidth) - dev->pcmcia_map.buswidth = 2; + if(!dev->pcmcia_map.bankwidth) + dev->pcmcia_map.bankwidth = 2; if(force_size) { dev->pcmcia_map.size = force_size << 20; DEBUG(2, "size forced to %dM", force_size); } - if(buswidth) { - dev->pcmcia_map.buswidth = buswidth; - DEBUG(2, "buswidth forced to %d", buswidth); + if(bankwidth) { + dev->pcmcia_map.bankwidth = bankwidth; + DEBUG(2, "bankwidth forced to %d", bankwidth); } dev->pcmcia_map.name = dev->mtd_name; @@ -488,7 +480,7 @@ } DEBUG(1, "Device: Size: %lu Width:%d Name: %s", - dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name); + dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); } @@ -497,8 +489,8 @@ * MTD device available to the system. */ -#define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) static void pcmciamtd_config(dev_link_t *link) { @@ -520,7 +512,7 @@ link->state |= DEV_CONFIG; DEBUG(2, "Validating CIS"); - ret = CardServices(ValidateCIS, link->handle, &cisinfo); + ret = pcmcia_validate_cis(link->handle, &cisinfo); if(ret != CS_SUCCESS) { cs_error(link->handle, GetTupleData, ret); } else { @@ -529,21 +521,25 @@ card_settings(dev, link, &new_name); - dev->pcmcia_map.read8 = pcmcia_read8_remap; - dev->pcmcia_map.read16 = pcmcia_read16_remap; + dev->pcmcia_map.phys = NO_XIP; dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; - dev->pcmcia_map.write8 = pcmcia_write8_remap; - dev->pcmcia_map.write16 = pcmcia_write16_remap; dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; + if (dev->pcmcia_map.bankwidth == 1) { + dev->pcmcia_map.read = pcmcia_read8_remap; + dev->pcmcia_map.write = pcmcia_write8_remap; + } else { + dev->pcmcia_map.read = pcmcia_read16_remap; + dev->pcmcia_map.write = pcmcia_write16_remap; + } if(setvpp == 1) dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum - that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the + that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the whole card - otherwise we try smaller windows until we succeed */ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; - req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; req.Base = 0; req.AccessSpeed = mem_speed; link->win = (window_handle_t)link->handle; @@ -552,15 +548,14 @@ do { int ret; - DEBUG(2, "requesting window with size = %dKB memspeed = %d", + DEBUG(2, "requesting window with size = %dKiB memspeed = %d", req.Size >> 10, req.AccessSpeed); - link->win = (window_handle_t)link->handle; - ret = CardServices(RequestWindow, &link->win, &req); + ret = pcmcia_request_window(&link->handle, &req, &link->win); DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); if(ret) { req.Size >>= 1; } else { - DEBUG(2, "Got window of size %dKB", req.Size >> 10); + DEBUG(2, "Got window of size %dKiB", req.Size >> 10); dev->win_size = req.Size; break; } @@ -570,19 +565,19 @@ if(!dev->win_size) { err("Cant allocate memory window"); - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); return; } - DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); + DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); /* Get write protect status */ - CS_CHECK(GetStatus, link->handle, &status); + CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status)); DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", status.CardState, (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { err("ioremap(%lu, %u) failed", req.Base, req.Size); - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); return; } DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", @@ -593,7 +588,7 @@ dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; DEBUG(2, "Getting configuration"); - CS_CHECK(GetConfigurationInfo, link->handle, &t); + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t)); DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); dev->vpp = (vpp) ? vpp : t.Vpp1; link->conf.Attributes = 0; @@ -615,7 +610,7 @@ link->conf.ConfigIndex = 0; link->conf.Present = t.Present; DEBUG(2, "Setting Configuration"); - ret = CardServices(RequestConfiguration, link->handle, &link->conf); + ret = pcmcia_request_configuration(link->handle, &link->conf); if(ret != CS_SUCCESS) { cs_error(link->handle, RequestConfiguration, ret); } @@ -637,26 +632,26 @@ if(!mtd) { DEBUG(1, "Cant find an MTD"); - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); return; } dev->mtd_info = mtd; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; if(new_name) { int size = 0; char unit = ' '; /* Since we are using a default name, make it better by adding in the size */ - if(mtd->size < 1048576) { /* <1MB in size, show size in K */ + if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ size = mtd->size >> 10; unit = 'K'; } else { size = mtd->size >> 20; unit = 'M'; } - snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card"); + snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card"); } /* If the memory found is fits completely into the mapped PCMCIA window, @@ -665,11 +660,14 @@ DEBUG(1, "Using non remapping memory functions"); dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state); dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; - dev->pcmcia_map.read8 = pcmcia_read8; - dev->pcmcia_map.read16 = pcmcia_read16; + if (dev->pcmcia_map.bankwidth == 1) { + dev->pcmcia_map.read = pcmcia_read8; + dev->pcmcia_map.write = pcmcia_write8; + } else { + dev->pcmcia_map.read = pcmcia_read16; + dev->pcmcia_map.write = pcmcia_write16; + } dev->pcmcia_map.copy_from = pcmcia_copy_from; - dev->pcmcia_map.write8 = pcmcia_write8; - dev->pcmcia_map.write16 = pcmcia_write16; dev->pcmcia_map.copy_to = pcmcia_copy_to; } @@ -677,7 +675,7 @@ map_destroy(mtd); dev->mtd_info = NULL; err("Couldnt register MTD device"); - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); return; } snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); @@ -689,7 +687,7 @@ cs_failed: cs_error(link->handle, last_fn, last_ret); err("CS Error, exiting"); - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); return; } @@ -716,7 +714,7 @@ del_mtd_device(dev->mtd_info); info("mtd%d: Removed", dev->mtd_info->index); } - mod_timer(&link->release, jiffies + HZ/20); + pcmciamtd_release(link); } break; case CS_EVENT_CARD_INSERTION: @@ -757,16 +755,14 @@ { DEBUG(3, "link=0x%p", link); - del_timer(&link->release); - if(link->state & DEV_CONFIG) { - pcmciamtd_release((u_long)link); + pcmciamtd_release(link); } if (link->handle) { int ret; DEBUG(2, "Deregistering with card services"); - ret = CardServices(DeregisterClient, link->handle); + ret = pcmcia_deregister_client(link->handle); if (ret != CS_SUCCESS) cs_error(link->handle, DeregisterClient, ret); } @@ -796,10 +792,6 @@ link = &dev->link; link->priv = dev; - init_timer(&link->release); - link->release.function = &pcmciamtd_release; - link->release.data = (u_long)link; - link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY; @@ -817,7 +809,7 @@ client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; DEBUG(2, "Calling RegisterClient"); - ret = CardServices(RegisterClient, &link->handle, &client_reg); + ret = pcmcia_register_client(&link->handle, &client_reg); if (ret != 0) { cs_error(link->handle, RegisterClient, ret); pcmciamtd_detach(link); @@ -828,20 +820,23 @@ } +static struct pcmcia_driver pcmciamtd_driver = { + .drv = { + .name = "pcmciamtd" + }, + .attach = pcmciamtd_attach, + .detach = pcmciamtd_detach, + .owner = THIS_MODULE +}; + + static int __init init_pcmciamtd(void) { - servinfo_t serv; - info(DRIVER_DESC " " DRIVER_VERSION); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - err("Card Services release does not match!"); - return -1; - } - if(buswidth && buswidth != 1 && buswidth != 2) { - info("bad buswidth (%d), using default", buswidth); - buswidth = 2; + if(bankwidth && bankwidth != 1 && bankwidth != 2) { + info("bad bankwidth (%d), using default", bankwidth); + bankwidth = 2; } if(force_size && (force_size < 1 || force_size > 64)) { info("bad force_size (%d), using default", force_size); @@ -851,15 +846,14 @@ info("bad mem_type (%d), using default", mem_type); mem_type = 0; } - register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); - return 0; + return pcmcia_register_driver(&pcmciamtd_driver); } static void __exit exit_pcmciamtd(void) { DEBUG(1, DRIVER_DESC " unloading"); - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&pcmciamtd_driver); while(dev_list) { dev_link_t *link = dev_list; --- linux-2.4.21/drivers/mtd/maps/physmap.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/physmap.c @@ -1,179 +1,119 @@ /* - * $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $ + * $Id: physmap.c,v 1.37 2004/11/28 09:40:40 dwmw2 Exp $ * * Normal mappings of chips in physical memory + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 031022 - [jsun] add run-time configure and partition setup */ #include #include #include +#include +#include #include #include #include #include - -#ifdef CONFIG_MTD_PARTITIONS #include -#endif - -#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 + .name = "phys_mapped_flash", + .phys = CONFIG_MTD_PHYSMAP_START, + .size = CONFIG_MTD_PHYSMAP_LEN, + .bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH, }; #ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS -static struct mtd_partition *mtd_parts = 0; -static int mtd_parts_nb = 0; -#else -static struct mtd_partition physmap_partitions[] = { -/* Put your own partition definitions here */ -#if 0 - { - name: "bootROM", - size: 0x80000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "zImage", - size: 0x100000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, - } -#endif -}; +static struct mtd_partition *mtd_parts; +static int mtd_parts_nb; -#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) +static int num_physmap_partitions; +static struct mtd_partition *physmap_partitions; -#endif -#endif +static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; -int __init init_physmap(void) +void physmap_set_partitions(struct mtd_partition *parts, int num_parts) { - static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + physmap_partitions=parts; + num_physmap_partitions=num_parts; +} +#endif /* CONFIG_MTD_PARTITIONS */ + +static int __init init_physmap(void) +{ + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; const char **type; - 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); + printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); + physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size); - if (!physmap_map.map_priv_1) { + if (!physmap_map.virt) { printk("Failed to ioremap\n"); return -EIO; } - mymtd = 0; + simple_map_init(&physmap_map); + + mymtd = NULL; type = rom_probe_types; for(; !mymtd && *type; type++) { mymtd = do_map_probe(*type, &physmap_map); } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; - add_mtd_device(mymtd); #ifdef CONFIG_MTD_PARTITIONS -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, - "phys"); + mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, + &mtd_parts, 0); + if (mtd_parts_nb > 0) { - printk(KERN_NOTICE - "Using command line partition definition\n"); add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); + return 0; } -#else - if (NUM_PARTITIONS != 0) + + if (num_physmap_partitions != 0) { printk(KERN_NOTICE "Using physmap partition definition\n"); - add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); + add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); + return 0; } #endif -#endif + add_mtd_device(mymtd); + return 0; } - iounmap((void *)physmap_map.map_priv_1); + iounmap(physmap_map.virt); return -ENXIO; } static void __exit cleanup_physmap(void) { - if (mymtd) { +#ifdef CONFIG_MTD_PARTITIONS + if (mtd_parts_nb) { + del_mtd_partitions(mymtd); + kfree(mtd_parts); + } else if (num_physmap_partitions) { + del_mtd_partitions(mymtd); + } else { 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; } +#else + del_mtd_device(mymtd); +#endif + map_destroy(mymtd); + + iounmap(physmap_map.virt); + physmap_map.virt = NULL; } module_init(init_physmap); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/plat-ram.c @@ -0,0 +1,286 @@ +/* drivers/mtd/maps/plat-ram.c + * + * (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks + * + * Generic platfrom device based RAM map + * + * $Id: plat-ram.c,v 1.1 2005/01/24 00:37:02 bjd 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 +*/ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* private structure for each mtd platform ram device created */ + +struct platram_info { + struct device *dev; + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *area; + struct platdata_mtd_ram *pdata; +}; + +/* to_platram_info() + * + * device private data to struct platram_info conversion +*/ + +static inline struct platram_info *to_platram_info(struct device *dev) +{ + return (struct platram_info *)dev_get_drvdata(dev); +} + +/* platram_setrw + * + * call the platform device's set rw/ro control + * + * to = 0 => read-only + * = 1 => read-write +*/ + +static inline void platram_setrw(struct platram_info *info, int to) +{ + if (info->pdata == NULL) + return; + + if (info->pdata->set_rw != NULL) + (info->pdata->set_rw)(info->dev, to); +} + +/* platram_remove + * + * called to remove the device from the driver's control +*/ + +static int platram_remove(struct device *dev) +{ + struct platram_info *info = to_platram_info(dev); + + dev_set_drvdata(dev, NULL); + + dev_dbg(dev, "removing device\n"); + + if (info == NULL) + return 0; + + if (info->mtd) { +#ifdef CONFIG_MTD_PARTITIONS + if (info->partitions) { + del_mtd_partitions(info->mtd); + kfree(info->partitions); + } +#endif + del_mtd_device(info->mtd); + map_destroy(info->mtd); + } + + /* ensure ram is left read-only */ + + platram_setrw(info, PLATRAM_RO); + + /* release resources */ + + if (info->area) { + release_resource(info->area); + kfree(info->area); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + kfree(info); + + return 0; +} + +/* platram_probe + * + * called from device drive system when a device matching our + * driver is found. +*/ + +static int platram_probe(struct device *dev) +{ + struct platform_device *pd = to_platform_device(dev); + struct platdata_mtd_ram *pdata; + struct platram_info *info; + struct resource *res; + int err = 0; + + dev_dbg(dev, "probe entered\n"); + + if (dev->platform_data == NULL) { + dev_err(dev, "no platform data supplied\n"); + err = -ENOENT; + goto exit_error; + } + + pdata = dev->platform_data; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(dev, "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info, sizeof(*info)); + dev_set_drvdata(dev, info); + + info->dev = dev; + info->pdata = pdata; + + /* get the resource for the memory mapping */ + + res = platform_get_resource(pd, IORESOURCE_MEM, 0); + + if (res == NULL) { + dev_err(dev, "no memory resource specified\n"); + err = -ENOENT; + goto exit_free; + } + + dev_dbg(dev, "got platform resource %p (0x%lx)\n", res, res->start); + + /* setup map parameters */ + + info->map.phys = res->start; + info->map.size = (res->end - res->start) + 1; + info->map.name = pdata->mapname != NULL ? pdata->mapname : pd->name; + info->map.bankwidth = pdata->bankwidth; + + /* register our usage of the memory area */ + + info->area = request_mem_region(res->start, info->map.size, pd->name); + if (info->area == NULL) { + dev_err(dev, "failed to request memory region\n"); + err = -EIO; + goto exit_free; + } + + /* remap the memory area */ + + info->map.virt = ioremap(res->start, info->map.size); + dev_dbg(dev, "virt %p, %d bytes\n", info->map.virt, info->map.size); + + if (info->map.virt == NULL) { + dev_err(dev, "failed to ioremap() region\n"); + err = -EIO; + goto exit_free; + } + + { + unsigned int *p = (unsigned int *)info->map.virt; + printk("%08x %08x %08x %08x\n", + readl(p), readl(p+1), readl(p+2), readl(p+3)); + } + + simple_map_init(&info->map); + + dev_dbg(dev, "initialised map, probing for mtd\n"); + + /* probe for the right mtd map driver */ + + info->mtd = do_map_probe("map_ram" , &info->map); + if (info->mtd == NULL) { + dev_err(dev, "failed to probe for map_ram\n"); + err = -ENOMEM; + goto exit_free; + } + + info->mtd->owner = THIS_MODULE; + + platram_setrw(info, PLATRAM_RW); + + /* check to see if there are any available partitions, or wether + * to add this device whole */ + +#ifdef CONFIG_MTD_PARTITIONS + if (pdata->nr_partitions > 0) { + const char **probes = { NULL }; + + if (pdata->probes) + probes = (const char **)pdata->probes; + + err = parse_mtd_partitions(info->mtd, probes, + &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, + err); + } + } +#endif /* CONFIG_MTD_PARTITIONS */ + + if (add_mtd_device(info->mtd)) { + dev_err(dev, "add_mtd_device() failed\n"); + err = -ENOMEM; + } + + dev_info(dev, "registered mtd device\n"); + return err; + + exit_free: + platram_remove(dev); + exit_error: + return err; +} + +/* device driver info */ + +static struct device_driver platram_driver = { + .name = "mtd-ram", + .bus = &platform_bus_type, + .probe = platram_probe, + .remove = platram_remove, +}; + +/* module init/exit */ + +static int __init platram_init(void) +{ + printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n"); + return driver_register(&platram_driver); +} + +static void __exit platram_exit(void) +{ + driver_unregister(&platram_driver); +} + +module_init(platram_init); +module_exit(platram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("MTD platform RAM map driver"); --- linux-2.4.21/drivers/mtd/maps/pnc2000.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/pnc2000.c @@ -5,12 +5,13 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.17 2004/11/16 18:29:02 dwmw2 Exp $ */ #include #include #include +#include #include #include @@ -24,58 +25,13 @@ * 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 +static struct map_info pnc_map = { + .name = "PNC-2000", + .size = WINDOW_SIZE, + .bankwidth = 4, + .phys = 0xFFFFFFFF, + .virt = (void __iomem *)WINDOW_ADDR, }; @@ -84,19 +40,19 @@ */ static struct mtd_partition pnc_partitions[3] = { { - name: "PNC-2000 boot firmware", - size: 0x20000, - offset: 0 + .name = "PNC-2000 boot firmware", + .size = 0x20000, + .offset = 0 }, { - name: "PNC-2000 kernel", - size: 0x1a0000, - offset: 0x20000 + .name = "PNC-2000 kernel", + .size = 0x1a0000, + .offset = 0x20000 }, { - name: "PNC-2000 filesystem", - size: 0x240000, - offset: 0x1c0000 + .name = "PNC-2000 filesystem", + .size = 0x240000, + .offset = 0x1c0000 } }; @@ -106,13 +62,15 @@ */ static struct mtd_info *mymtd; -int __init init_pnc2000(void) +static int __init init_pnc2000(void) { printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + simple_map_init(&pnc_map); + mymtd = do_map_probe("cfi_probe", &pnc_map); if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; return add_mtd_partitions(mymtd, pnc_partitions, 3); } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ramses.c @@ -0,0 +1,124 @@ +/* + * Map driver for the Ramses developer platform. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define WINDOW_ADDR 0 +#define WINDOW_SIZE 32*1024*1024 + +static struct map_info ramses_map_flash = { + .name = "Flash", + .phys = WINDOW_ADDR, + .size = WINDOW_SIZE, + .bankwidth = 4, +}; + +static struct mtd_partition ramses_flash_partitions[] = { + { + name: "Bootloader", + size: 0x00040000, + offset: 0, + },{ + name: "Kernel", + size: 0x00100000, + offset: 0x00040000, + },{ + name: "Filesystem", + size: MTDPART_SIZ_FULL, + offset: 0x00140000 + } +}; + +static struct mtd_partition *parsed_parts; + +static struct mtd_info *flash_mtd; +static int __init init_ramses(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; + const char *part_type; + + /* + * Static partition definition selection + */ + part_type = "static"; + parts = ramses_flash_partitions; + nb_parts = ARRAY_SIZE(ramses_flash_partitions); + + simple_map_init(&ramses_map_flash); + + printk( "Probing flash at physical address 0x%08x (%d-bit buswidth)\n", + WINDOW_ADDR, ramses_map_flash.bankwidth * 8 ); +#ifdef CONFIG_ARCH_RAMSES + FLASH_WRITE_PROTECT_DISABLE(); +#endif + + + ramses_map_flash.virt = __ioremap(WINDOW_ADDR, WINDOW_SIZE, 0); + if (!ramses_map_flash.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + flash_mtd = do_map_probe("cfi_probe", &ramses_map_flash); + + if (!flash_mtd) { + iounmap((void *)ramses_map_flash.virt); + return -ENXIO; + } + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } + + if (nb_parts == 0) { + printk(KERN_NOTICE "Ramses flash: no partition info available," + "registering whole flash at once\n"); + if (add_mtd_device(flash_mtd)){ + return -ENXIO; + } + } else { + printk(KERN_NOTICE "Using %s partition definition\n", + part_type); + return add_mtd_partitions(flash_mtd, parts, nb_parts); + } + return 0; +} + +static void __exit cleanup_ramses(void) +{ + if (flash_mtd) { + del_mtd_partitions(flash_mtd); + map_destroy(flash_mtd); + if (parsed_parts) + kfree(parsed_parts); + } + if (ramses_map_flash.virt) + iounmap(ramses_map_flash.virt); +#ifdef CONFIG_ARCH_RAMSES + FLASH_WRITE_PROTECT_ENABLE(); +#endif + return; +} + +module_init(init_ramses); +module_exit(cleanup_ramses); --- linux-2.4.21/drivers/mtd/maps/redwood.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/redwood.c @@ -1,38 +1,23 @@ /* - * $Id: - * - * redwood.c - mapper for IBM Redwood-4/5 board. - * - * Copyright 2001 MontaVista Softare 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. + * $Id: redwood.c,v 1.10 2004/11/04 13:24:15 gleixner Exp $ * - * 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. + * drivers/mtd/maps/redwood.c * - * You 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. + * FLASH map for the IBM Redwood 4/5/6 boards. * - * History: 12/17/2001 - Armin - * migrated to use do_map_probe + * Author: MontaVista Software, Inc. * + * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. */ +#include #include #include #include +#include #include #include @@ -40,96 +25,102 @@ #include +#if !defined (CONFIG_REDWOOD_6) + #define WINDOW_ADDR 0xffc00000 #define WINDOW_SIZE 0x00400000 -__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(map->map_priv_1 + ofs); -} - -__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(map->map_priv_1 + ofs); -} - -__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs) -{ - return *(volatile unsigned int *)(map->map_priv_1 + ofs); -} - -void redwood_flash_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(map->map_priv_1 + from), len); -} - -void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(map->map_priv_1 + adr) = d; -} - -void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(map->map_priv_1 + adr) = d; -} - -void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(map->map_priv_1 + adr) = d; -} - -void redwood_flash_copy_to(struct map_info *map, unsigned long to, - const void *from, ssize_t len) -{ - memcpy((void *)(map->map_priv_1 + to), from, len); -} +#define RW_PART0_OF 0 +#define RW_PART0_SZ 0x10000 +#define RW_PART1_OF RW_PART0_SZ +#define RW_PART1_SZ 0x200000 - 0x10000 +#define RW_PART2_OF 0x200000 +#define RW_PART2_SZ 0x10000 +#define RW_PART3_OF 0x210000 +#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000) +#define RW_PART4_OF 0x3e0000 +#define RW_PART4_SZ 0x20000 -struct map_info redwood_flash_map = { - name: "IBM Redwood", - size: WINDOW_SIZE, - buswidth: 2, - read8: redwood_flash_read8, - read16: redwood_flash_read16, - read32: redwood_flash_read32, - copy_from: redwood_flash_copy_from, - write8: redwood_flash_write8, - write16: redwood_flash_write16, - write32: redwood_flash_write32, - copy_to: redwood_flash_copy_to +static struct mtd_partition redwood_flash_partitions[] = { + { + .name = "Redwood OpenBIOS Vital Product Data", + .offset = RW_PART0_OF, + .size = RW_PART0_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { + .name = "Redwood kernel", + .offset = RW_PART1_OF, + .size = RW_PART1_SZ + }, + { + .name = "Redwood OpenBIOS non-volatile storage", + .offset = RW_PART2_OF, + .size = RW_PART2_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { + .name = "Redwood filesystem", + .offset = RW_PART3_OF, + .size = RW_PART3_SZ + }, + { + .name = "Redwood OpenBIOS", + .offset = RW_PART4_OF, + .size = RW_PART4_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ + } }; +#else /* CONFIG_REDWOOD_6 */ +/* FIXME: the window is bigger - armin */ +#define WINDOW_ADDR 0xff800000 +#define WINDOW_SIZE 0x00800000 + +#define RW_PART0_OF 0 +#define RW_PART0_SZ 0x400000 /* 4 MiB data */ +#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ +#define RW_PART1_SZ 0x10000 /* 64K VPD */ +#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ +#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000) +#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ +#define RW_PART3_SZ 0x20000 static struct mtd_partition redwood_flash_partitions[] = { { - name: "Redwood OpenBIOS Vital Product Data", - offset: 0, - size: 0x10000, - mask_flags: MTD_WRITEABLE /* force read-only */ - }, - { - name: "Redwood kernel", - offset: 0x10000, - size: 0x200000 - 0x10000 + .name = "Redwood filesystem", + .offset = RW_PART0_OF, + .size = RW_PART0_SZ }, { - name: "Redwood OpenBIOS non-volatile storage", - offset: 0x200000, - size: 0x10000, - mask_flags: MTD_WRITEABLE /* force read-only */ + .name = "Redwood OpenBIOS Vital Product Data", + .offset = RW_PART1_OF, + .size = RW_PART1_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ }, { - name: "Redwood filesystem", - offset: 0x210000, - size: 0x200000 - (0x10000 + 0x20000) + .name = "Redwood kernel", + .offset = RW_PART2_OF, + .size = RW_PART2_SZ }, { - name: "Redwood OpenBIOS", - offset: 0x3e0000, - size: 0x20000, - mask_flags: MTD_WRITEABLE /* force read-only */ + .name = "Redwood OpenBIOS", + .offset = RW_PART3_OF, + .size = RW_PART3_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ } }; + +#endif /* CONFIG_REDWOOD_6 */ + +struct map_info redwood_flash_map = { + .name = "IBM Redwood", + .size = WINDOW_SIZE, + .bankwidth = 2, + .phys = WINDOW_ADDR, +}; + + #define NUM_REDWOOD_FLASH_PARTITIONS \ (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0])) @@ -140,18 +131,18 @@ printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - redwood_flash_map.map_priv_1 = - (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + redwood_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!redwood_flash_map.map_priv_1) { + if (!redwood_flash_map.virt) { printk("init_redwood_flash: failed to ioremap\n"); return -EIO; } + simple_map_init(&redwood_flash_map); redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map); if (redwood_mtd) { - redwood_mtd->module = THIS_MODULE; + redwood_mtd->owner = THIS_MODULE; return add_mtd_partitions(redwood_mtd, redwood_flash_partitions, NUM_REDWOOD_FLASH_PARTITIONS); @@ -164,10 +155,15 @@ { if (redwood_mtd) { del_mtd_partitions(redwood_mtd); - iounmap((void *)redwood_flash_map.map_priv_1); + /* moved iounmap after map_destroy - armin */ map_destroy(redwood_mtd); + iounmap((void *)redwood_flash_map.virt); } } module_init(init_redwood_flash); module_exit(cleanup_redwood_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("MontaVista Software "); +MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); --- linux-2.4.21/drivers/mtd/maps/rpxlite.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/rpxlite.c @@ -1,5 +1,5 @@ /* - * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $ * * Handle mapping of the flash on the RPX Lite and CLLF boards */ @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -17,80 +18,31 @@ static struct mtd_info *mymtd; -__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_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) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -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 +static struct map_info rpxlite_map = { + .name = "RPX", + .size = WINDOW_SIZE, + .bankwidth = 4, + .phys = WINDOW_ADDR, }; 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); + rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); - if (!rpxlite_map.map_priv_1) { + if (!rpxlite_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + simple_map_init(&rpxlite_map); mymtd = do_map_probe("cfi_probe", &rpxlite_map); if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; add_mtd_device(mymtd); return 0; } - iounmap((void *)rpxlite_map.map_priv_1); + iounmap((void *)rpxlite_map.virt); return -ENXIO; } @@ -100,9 +52,9 @@ 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; + if (rpxlite_map.virt) { + iounmap((void *)rpxlite_map.virt); + rpxlite_map.virt = 0; } } --- linux-2.4.21/drivers/mtd/maps/sa1100-flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/sa1100-flash.c @@ -3,7 +3,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: sa1100-flash.c,v 1.29 2002/09/06 14:36:19 abz Exp $ + * $Id: sa1100-flash.c,v 1.47 2004/11/01 13:44:36 rmk Exp $ */ #include @@ -11,330 +11,212 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include +#include #include +#include +#include #ifndef CONFIG_ARCH_SA1100 #error This is for SA1100 architecture only #endif +/* + * This isnt complete yet, so... + */ +#define CONFIG_MTD_SA1100_STATICMAP 1 -#define WINDOW_ADDR 0xe8000000 - -static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) -{ - return readb(map->map_priv_1 + ofs); -} - -static __u16 sa1100_read16(struct map_info *map, unsigned long ofs) -{ - return readw(map->map_priv_1 + ofs); -} - -static __u32 sa1100_read32(struct map_info *map, unsigned long ofs) -{ - return readl(map->map_priv_1 + ofs); -} - -static void sa1100_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 sa1100_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -static void sa1100_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 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, - - map_priv_1: WINDOW_ADDR, - map_priv_2: -1, -}; - - +#ifdef CONFIG_MTD_SA1100_STATICMAP /* * 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 be no more than - * the value specified in the "struct map_desc *_io_desc" mapping - * definition for the corresponding machine. + * Please note: + * 1. We no longer support static flash mappings via the machine io_desc + * structure. + * 2. The flash size given should be the largest flash size that can + * be accommodated. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. * * Please keep these in alphabetical order, and formatted as per existing * entries. Thanks. */ -#ifdef CONFIG_SA1100_ADSAGC -#define ADSAGC_FLASH_SIZE 0x02000000 -static struct mtd_partition adsagc_partitions[] = { - { - name: "bootROM", - size: 0x80000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "zImage", - size: 0x100000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, - } -}; -#endif - #ifdef CONFIG_SA1100_ADSBITSY -#define ADSBITSY_FLASH_SIZE 0x02000000 static struct mtd_partition adsbitsy_partitions[] = { { - name: "bootROM", - size: 0x80000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "zImage", - size: 0x100000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, - } -}; -#endif - -#ifdef CONFIG_SA1100_ADSBITSYPLUS -#define ADSBITSYPLUS_FLASH_SIZE 0x02000000 -static struct mtd_partition adsbitsyplus_partitions[] = { - { - name: "bootROM", - size: 0x80000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "bootROM", + .size = 0x80000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "zImage", - size: 0x100000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "zImage", + .size = 0x100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif #ifdef CONFIG_SA1100_ASSABET /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ -#define ASSABET4_FLASH_SIZE 0x00400000 static struct mtd_partition assabet4_partitions[] = { { - name: "bootloader", - size: 0x00020000, - offset: 0, - mask_flags: MTD_WRITEABLE, + .name = "bootloader", + .size = 0x00020000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, }, { - name: "bootloader params", - size: 0x00020000, - offset: MTDPART_OFS_APPEND, - 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, + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ -#define ASSABET5_FLASH_SIZE 0x02000000 static struct mtd_partition assabet5_partitions[] = { { - name: "bootloader", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, + .name = "bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, }, { - name: "bootloader params", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - 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, + .name = "jffs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; -#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE #define assabet_partitions assabet5_partitions #endif #ifdef CONFIG_SA1100_BADGE4 - /* - * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) + * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) * Eight 4 KiW Parameter Bottom Blocks (64 KiB) * Sixty-three 32 KiW Main Blocks (4032 Ki b) + * + * + * + * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) */ -#define BADGE4_FLASH_SIZE 0x00400000 static struct mtd_partition badge4_partitions[] = { { - name: "BLOB boot loader", - offset: 0, - size: 0x0000A000 - }, { - name: "params", - offset: MTDPART_OFS_APPEND, - size: 0x00006000 + .name = "BLOB boot loader", + .offset = 0, + .size = 0x0000A000 }, { - name: "kernel", - offset: MTDPART_OFS_APPEND, - size: 0x00100000 + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 0x00006000 }, { - name: "root", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + .name = "root", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL } }; - #endif #ifdef CONFIG_SA1100_CERF #ifdef CONFIG_SA1100_CERF_FLASH_32MB -#define CERF_FLASH_SIZE 0x02000000 -static struct mtd_partition cerf_partitions[] = { - { - name: "firmware", - size: 0x00040000, - offset: 0, - }, { - name: "params", - size: 0x00040000, - offset: 0x00040000, - }, { - name: "kernel", - size: 0x00100000, - offset: 0x00080000, - }, { - name: "rootdisk", - size: 0x01E80000, - offset: 0x00180000, - } -}; +# define CERF_FLASH_SIZE 0x02000000 #elif defined CONFIG_SA1100_CERF_FLASH_16MB -#define CERF_FLASH_SIZE 0x01000000 +# define CERF_FLASH_SIZE 0x01000000 +#elif defined CONFIG_SA1100_CERF_FLASH_8MB +# define CERF_FLASH_SIZE 0x00800000 +#else +# error "Undefined flash size for CERF in sa1100-flash.c" +#endif + static struct mtd_partition cerf_partitions[] = { { - name: "firmware", - size: 0x00020000, - offset: 0, + .name = "Bootloader", + .size = 0x00020000, + .offset = 0x00000000, }, { - name: "params", - size: 0x00020000, - offset: 0x00020000, + .name = "Params", + .size = 0x00040000, + .offset = 0x00020000, }, { - name: "kernel", - size: 0x00100000, - offset: 0x00040000, + .name = "Kernel", + .size = 0x00100000, + .offset = 0x00060000, }, { - name: "rootdisk", - size: 0x00EC0000, - offset: 0x00140000, + .name = "Filesystem", + .size = CERF_FLASH_SIZE-0x00160000, + .offset = 0x00160000, } }; -#elif defined CONFIG_SA1100_CERF_FLASH_8MB -# error "Unwritten type definition" -#else -# error "Undefined memory orientation for CERF in sa1100-flash.c" -#endif #endif #ifdef CONFIG_SA1100_CONSUS -#define CONSUS_FLASH_SIZE 0x02000000 static struct mtd_partition consus_partitions[] = { { - name: "Consus boot firmware", - offset: 0, - size: 0x00040000, - mask_flags: MTD_WRITABLE, /* force read-only */ + .name = "Consus boot firmware", + .offset = 0, + .size = 0x00040000, + .mask_flags = MTD_WRITABLE, /* force read-only */ }, { - name: "Consus kernel", - offset: 0x00040000, - size: 0x00100000, - mask_flags: 0, + .name = "Consus kernel", + .offset = 0x00040000, + .size = 0x00100000, + .mask_flags = 0, }, { - name: "Consus disk", - offset: 0x00140000, + .name = "Consus disk", + .offset = 0x00140000, /* The rest (up to 16M) for jffs. We could put 0 and make it find the size automatically, but right now i have 32 megs. jffs will use all 32 megs if given the chance, and this leads to horrible problems when you try to re-flash the image because blob won't erase the whole partition. */ - size: 0x01000000 - 0x00140000, - mask_flags: 0, + .size = 0x01000000 - 0x00140000, + .mask_flags = 0, }, { /* this disk is a secondary disk, which can be used as needed, for simplicity, make it the size of the other consus partition, although realistically it could be the remainder of the disk (depending on the file system used) */ - name: "Consus disk2", - offset: 0x01000000, - size: 0x01000000 - 0x00140000, - mask_flags: 0, + .name = "Consus disk2", + .offset = 0x01000000, + .size = 0x01000000 - 0x00140000, + .mask_flags = 0, } }; #endif @@ -344,96 +226,95 @@ #define FLEXANET_FLASH_SIZE 0x02000000 static struct mtd_partition flexanet_partitions[] = { { - name: "bootloader", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, + .name = "bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, }, { - name: "bootloader params", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - 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 = "kernel", + .size = 0x000C0000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, }, { - name: "altkernel", - 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 = "root", + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, }, { - name: "free1", - size: 0x00300000, - 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 = "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, + .name = "free3", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, } }; #endif #ifdef CONFIG_SA1100_FREEBIRD -#define FREEBIRD_FLASH_SIZE 0x02000000 static struct mtd_partition freebird_partitions[] = { -#if CONFIG_SA1100_FREEBIRD_NEW +#ifdef CONFIG_SA1100_FREEBIRD_NEW { - name: "firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "kernel", - size: 0x00080000, - offset: 0x00040000, + .name = "kernel", + .size = 0x00080000, + .offset = 0x00040000, }, { - name: "params", - size: 0x00040000, - offset: 0x000C0000, + .name = "params", + .size = 0x00040000, + .offset = 0x000C0000, }, { - name: "initrd", - size: 0x00100000, - offset: 0x00100000, + .name = "initrd", + .size = 0x00100000, + .offset = 0x00100000, }, { - name: "root cramfs", - size: 0x00300000, - offset: 0x00200000, + .name = "root cramfs", + .size = 0x00300000, + .offset = 0x00200000, }, { - name: "usr cramfs", - size: 0x00C00000, - offset: 0x00500000, + .name = "usr cramfs", + .size = 0x00C00000, + .offset = 0x00500000, }, { - name: "local", - size: MTDPART_SIZ_FULL, - offset: 0x01100000, + .name = "local", + .size = MTDPART_SIZ_FULL, + .offset = 0x01100000, } #else { - size: 0x00040000, - offset: 0, + .size = 0x00040000, + .offset = 0, }, { - size: 0x000c0000, - offset: MTDPART_OFS_APPEND, + .size = 0x000c0000, + .offset = MTDPART_OFS_APPEND, }, { - size: 0x00400000, - offset: MTDPART_OFS_APPEND, + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, }, { - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } #endif }; @@ -441,206 +322,237 @@ #ifdef CONFIG_SA1100_FRODO /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ -#define FRODO_FLASH_SIZE 0x02000000 static struct mtd_partition frodo_partitions[] = { { - name: "Boot Loader", - size: 0x00040000, - offset: 0x00000000 + .name = "bootloader", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE }, { - name: "Parameter Block", - size: 0x00040000, - offset: MTDPART_OFS_APPEND + .name = "bootloader params", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE }, { - name: "Linux Kernel", - size: 0x00100000, - offset: MTDPART_OFS_APPEND + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE }, { - name: "Ramdisk", - size: 0x00680000, - offset: MTDPART_OFS_APPEND + .name = "ramdisk", + .size = 0x00400000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE }, { - name: "Flash File System", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND + .name = "file system", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND } }; #endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT -#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000 static struct mtd_partition graphicsclient_partitions[] = { { - name: "zImage", - size: 0x100000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "zImage", + .size = 0x100000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER -#define GRAPHICSMASTER_FLASH_SIZE 0x02000000 static struct mtd_partition graphicsmaster_partitions[] = { { - name: "zImage", - size: 0x100000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "zImage", + .size = 0x100000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "ramdisk.gz", - size: 0x300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "ramdisk.gz", + .size = 0x300000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "User FS", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .name = "User FS", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif -#ifdef CONFIG_SA1100_H3600 -#define H3600_FLASH_SIZE 0x02000000 -static struct mtd_partition h3600_partitions[] = { +#ifdef CONFIG_SA1100_H3XXX +static struct mtd_partition h3xxx_partitions[] = { { - name: "H3600 boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "H3XXX boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "H3600 kernel", - size: 0x00080000, - offset: 0x00040000, +#ifdef CONFIG_MTD_2PARTS_IPAQ + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00040000, +#else + .name = "H3XXX kernel", + .size = 0x00080000, + .offset = 0x00040000, }, { - name: "H3600 params", - size: 0x00040000, - offset: 0x000C0000, + .name = "H3XXX params", + .size = 0x00040000, + .offset = 0x000C0000, }, { #ifdef CONFIG_JFFS2_FS - name: "H3600 root jffs2", - size: MTDPART_SIZ_FULL, - offset: 0x00100000, + .name = "H3XXX root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00100000, #else - name: "H3600 initrd", - size: 0x00100000, - offset: 0x00100000, + .name = "H3XXX initrd", + .size = 0x00100000, + .offset = 0x00100000, }, { - name: "H3600 root cramfs", - size: 0x00300000, - offset: 0x00200000, + .name = "H3XXX root cramfs", + .size = 0x00300000, + .offset = 0x00200000, }, { - name: "H3600 usr cramfs", - size: 0x00800000, - offset: 0x00500000, + .name = "H3XXX usr cramfs", + .size = 0x00800000, + .offset = 0x00500000, }, { - name: "H3600 usr local", - size: MTDPART_SIZ_FULL, - offset: 0x00d00000, + .name = "H3XXX usr local", + .size = MTDPART_SIZ_FULL, + .offset = 0x00d00000, +#endif #endif } }; -static void h3600_set_vpp(struct map_info *map, int vpp) +static void h3xxx_set_vpp(struct map_info *map, int vpp) { assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); } +#else +#define h3xxx_set_vpp NULL #endif #ifdef CONFIG_SA1100_HACKKIT -#define HACKKIT_FLASH_SIZE 0x01000000 static struct mtd_partition hackkit_partitions[] = { { - name: "BLOB", - size: 0x00040000, - offset: 0x00000000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "BLOB", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "config", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, + .name = "config", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, }, { - name: "kernel", - size: 0x00100000, - offset: MTDPART_OFS_APPEND, + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, }, { - name: "initrd", - size: 0x00180000, - offset: MTDPART_OFS_APPEND, + .name = "initrd", + .size = 0x00180000, + .offset = MTDPART_OFS_APPEND, }, { - name: "rootfs", - size: 0x700000, - offset: MTDPART_OFS_APPEND, + .name = "rootfs", + .size = 0x700000, + .offset = MTDPART_OFS_APPEND, }, { - name: "data", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .name = "data", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif #ifdef CONFIG_SA1100_HUW_WEBPANEL -#define HUW_WEBPANEL_FLASH_SIZE 0x01000000 static struct mtd_partition huw_webpanel_partitions[] = { { - name: "Loader", - size: 0x00040000, - offset: 0, + .name = "Loader", + .size = 0x00040000, + .offset = 0, }, { - name: "Sector 1", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, + .name = "Sector 1", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, }, { - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif +#ifdef CONFIG_SA1100_JORNADA56X +static struct mtd_partition jornada56x_partitions[] = { + { + .name = "bootldr", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "rootfs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} +#else +#define jornada56x_set_vpp NULL +#endif + #ifdef CONFIG_SA1100_JORNADA720 -#define JORNADA720_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 boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "JORNADA720 kernel", - size: 0x000c0000, - offset: 0x00040000, + .name = "JORNADA720 kernel", + .size = 0x000c0000, + .offset = 0x00040000, }, { - name: "JORNADA720 params", - size: 0x00040000, - offset: 0x00100000, + .name = "JORNADA720 params", + .size = 0x00040000, + .offset = 0x00100000, }, { - name: "JORNADA720 initrd", - size: 0x00100000, - offset: 0x00140000, + .name = "JORNADA720 initrd", + .size = 0x00100000, + .offset = 0x00140000, }, { - name: "JORNADA720 root cramfs", - size: 0x00300000, - offset: 0x00240000, + .name = "JORNADA720 root cramfs", + .size = 0x00300000, + .offset = 0x00240000, }, { - name: "JORNADA720 usr cramfs", - size: 0x00800000, - offset: 0x00540000, + .name = "JORNADA720 usr cramfs", + .size = 0x00800000, + .offset = 0x00540000, }, { - name: "JORNADA720 usr local", - size: 0, /* will expand to the end of the flash */ - offset: 0x00d00000, + .name = "JORNADA720 usr local", + .size = 0, /* will expand to the end of the flash */ + .offset = 0x00d00000, } }; @@ -652,540 +564,820 @@ PPSR &= ~0x80; PPDR |= 0x80; } - -#endif - -#ifdef CONFIG_SA1100_NANOENGINE -/* nanoEngine has one 28F320B3B Flash part in bank 0: */ -#define NANOENGINE_FLASH_SIZE 0x00400000 -static struct mtd_partition nanoengine_partitions[] = { - { - name: "nanoEngine boot firmware and parameter table", - size: 0x00010000, /* 32K */ - offset: 0x00000000, - mask_flags: MTD_WRITEABLE, /* force read-only */ - },{ - name: "kernel/initrd reserved", - size: 0x002f0000, - offset: 0x00010000, - },{ - name: "experimental filesystem allocation", - size: 0x00100000, - offset: 0x00300000, - } -}; +#else +#define jornada720_set_vpp NULL #endif #ifdef CONFIG_SA1100_PANGOLIN -#define PANGOLIN_FLASH_SIZE 0x04000000 static struct mtd_partition pangolin_partitions[] = { { - name: "boot firmware", - size: 0x00080000, - offset: 0x00000000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "boot firmware", + .size = 0x00080000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "kernel", - size: 0x00100000, - offset: 0x00080000, + .name = "kernel", + .size = 0x00100000, + .offset = 0x00080000, }, { - name: "initrd", - size: 0x00280000, - offset: 0x00180000, + .name = "initrd", + .size = 0x00280000, + .offset = 0x00180000, }, { - name: "initrd-test", - size: 0x03C00000, - offset: 0x00400000, + .name = "initrd-test", + .size = 0x03C00000, + .offset = 0x00400000, } }; #endif #ifdef CONFIG_SA1100_PT_SYSTEM3 /* erase size is 0x40000 == 256k partitions have to have this boundary */ -#define SYSTEM3_FLASH_SIZE 0x01000000 static struct mtd_partition system3_partitions[] = { { - name: "BLOB", - size: 0x00040000, - offset: 0x00000000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "BLOB", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "config", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, + .name = "config", + .size = 0x00040000, + .offset = MTDPART_OFS_APPEND, }, { - name: "kernel", - size: 0x00100000, - offset: MTDPART_OFS_APPEND, + .name = "kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, }, { - name: "root", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + .name = "root", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, } }; #endif #ifdef CONFIG_SA1100_SHANNON -#define SHANNON_FLASH_SIZE 0x00400000 static struct mtd_partition shannon_partitions[] = { { - name: "BLOB boot loader", - offset: 0, - size: 0x20000 + .name = "BLOB boot loader", + .offset = 0, + .size = 0x20000 }, { - name: "kernel", - offset: MTDPART_OFS_APPEND, - size: 0xe0000 + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 0xe0000 }, { - name: "initrd", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + .name = "initrd", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL } }; #endif #ifdef CONFIG_SA1100_SHERMAN -#define SHERMAN_FLASH_SIZE 0x02000000 static struct mtd_partition sherman_partitions[] = { { - size: 0x50000, - offset: 0, + .size = 0x50000, + .offset = 0, }, { - size: 0x70000, - offset: MTDPART_OFS_APPEND, + .size = 0x70000, + .offset = MTDPART_OFS_APPEND, }, { - size: 0x600000, - offset: MTDPART_OFS_APPEND, + .size = 0x600000, + .offset = MTDPART_OFS_APPEND, }, { - size: 0xA0000, - offset: MTDPART_OFS_APPEND, + .size = 0xA0000, + .offset = MTDPART_OFS_APPEND, } }; #endif #ifdef CONFIG_SA1100_SIMPAD -#define SIMPAD_FLASH_SIZE 0x02000000 static struct mtd_partition simpad_partitions[] = { { - name: "SIMpad boot firmware", - size: 0x00080000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "SIMpad kernel", - size: 0x00100000, - offset: 0x00080000, - }, { -#ifdef CONFIG_JFFS2_FS - name: "SIMpad root jffs2", - size: MTDPART_SIZ_FULL, - offset: 0x00180000, -#else - name: "SIMpad initrd", - size: 0x00300000, - offset: 0x00180000, + .name = "SIMpad boot firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "SIMpad root cramfs", - size: 0x00300000, - offset: 0x00480000, + .name = "SIMpad kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, }, { - name: "SIMpad usr cramfs", - size: 0x005c0000, - offset: 0x00780000, +#ifdef CONFIG_ROOT_CRAMFS + .name = "SIMpad root cramfs", + .size =0x00D80000, + .offset = MTDPART_OFS_APPEND + }, { - name: "SIMpad usr local", - size: MTDPART_SIZ_FULL, - offset: 0x00d40000, + .name = "SIMpad local jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND +#else + .name = "SIMpad root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND #endif } }; #endif /* CONFIG_SA1100_SIMPAD */ -#ifdef CONFIG_SA1100_SIMPUTER -#define SIMPUTER_FLASH_SIZE 0x02000000 -static struct mtd_partition simputer_partitions[] = { - { - name: "blob+logo", - offset: 0, - size: 0x00040000 - }, - { - name: "kernel", - offset: MTDPART_OFS_APPEND, - size: 0x000C0000 - }, - { - name: "/(cramfs)", - offset: MTDPART_OFS_APPEND, - size: 0x00200000 - }, - { - name: "/usr/local(jffs2)", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL /* expand till the end */ - } -}; -#endif - #ifdef CONFIG_SA1100_STORK -#define STORK_FLASH_SIZE 0x02000000 static struct mtd_partition stork_partitions[] = { { - name: "STORK boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "STORK boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "STORK params", - size: 0x00040000, - offset: 0x00040000, + .name = "STORK params", + .size = 0x00040000, + .offset = 0x00040000, }, { - name: "STORK kernel", - size: 0x00100000, - offset: 0x00080000, + .name = "STORK kernel", + .size = 0x00100000, + .offset = 0x00080000, }, { #ifdef CONFIG_JFFS2_FS - name: "STORK root jffs2", - offset: 0x00180000, - size: MTDPART_SIZ_FULL, + .name = "STORK root jffs2", + .offset = 0x00180000, + .size = MTDPART_SIZ_FULL, #else - name: "STORK initrd", - size: 0x00100000, - offset: 0x00180000, + .name = "STORK initrd", + .size = 0x00100000, + .offset = 0x00180000, }, { - name: "STORK root cramfs", - size: 0x00300000, - offset: 0x00280000, + .name = "STORK root cramfs", + .size = 0x00300000, + .offset = 0x00280000, }, { - name: "STORK usr cramfs", - size: 0x00800000, - offset: 0x00580000, + .name = "STORK usr cramfs", + .size = 0x00800000, + .offset = 0x00580000, }, { - name: "STORK usr local", - offset: 0x00d80000, - size: MTDPART_SIZ_FULL, + .name = "STORK usr local", + .offset = 0x00d80000, + .size = MTDPART_SIZ_FULL, #endif } }; #endif +#ifdef CONFIG_SA1100_TRIZEPS +static struct mtd_partition trizeps_partitions[] = { + { + .name = "Bootloader", + .size = 0x00100000, + .offset = 0, + }, { + .name = "Kernel", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "root", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; +#endif + #ifdef CONFIG_SA1100_YOPY -#define YOPY_FLASH_SIZE 0x08000000 static struct mtd_partition yopy_partitions[] = { { - name: "boot firmware", - size: 0x00040000, - offset: 0x00000000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "boot firmware", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "kernel", - size: 0x00080000, - offset: 0x00080000, + .name = "kernel", + .size = 0x00080000, + .offset = 0x00080000, }, { - name: "initrd", - size: 0x00300000, - offset: 0x00100000, + .name = "initrd", + .size = 0x00300000, + .offset = 0x00100000, }, { - name: "root", - size: 0x01000000, - offset: 0x00400000, + .name = "root", + .size = 0x01000000, + .offset = 0x00400000, } }; #endif -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); -extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); - -static struct mtd_partition *parsed_parts; -static struct mtd_info *mymtd; - -int __init sa1100_mtd_init(void) +static int __init sa1100_static_partitions(struct mtd_partition **parts) { - struct mtd_partition *parts; - int nb_parts = 0, ret; - int parsed_nr_parts = 0; - const char *part_type; - unsigned long base = -1UL; - - /* Default flash buswidth */ - sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; - - /* - * Static partition definition selection - */ - part_type = "static"; + int nb_parts = 0; -#ifdef CONFIG_SA1100_ADSAGC - if (machine_is_adsagc()) { - parts = adsagc_partitions; - nb_parts = ARRAY_SIZE(adsagc_partitions); - sa1100_map.size = ADSAGC_FLASH_SIZE; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; - } -#endif #ifdef CONFIG_SA1100_ADSBITSY if (machine_is_adsbitsy()) { - parts = adsbitsy_partitions; + *parts = adsbitsy_partitions; nb_parts = ARRAY_SIZE(adsbitsy_partitions); - sa1100_map.size = ADSBITSY_FLASH_SIZE; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; - } -#endif -#ifdef CONFIG_SA1100_ADSBITSYPLUS - if (machine_is_adsbitsyplus()) { - parts = adsbitsyplus_partitions; - nb_parts = ARRAY_SIZE(adsbitsyplus_partitions); - sa1100_map.size = ADSBITSYPLUS_FLASH_SIZE; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; } #endif #ifdef CONFIG_SA1100_ASSABET if (machine_is_assabet()) { - parts = assabet_partitions; + *parts = assabet_partitions; nb_parts = ARRAY_SIZE(assabet_partitions); - sa1100_map.size = ASSABET_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_BADGE4 if (machine_is_badge4()) { - parts = badge4_partitions; + *parts = badge4_partitions; nb_parts = ARRAY_SIZE(badge4_partitions); - sa1100_map.size = BADGE4_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_CERF if (machine_is_cerf()) { - parts = cerf_partitions; + *parts = cerf_partitions; nb_parts = ARRAY_SIZE(cerf_partitions); - sa1100_map.size = CERF_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_CONSUS if (machine_is_consus()) { - parts = consus_partitions; + *parts = consus_partitions; nb_parts = ARRAY_SIZE(consus_partitions); - sa1100_map.size = CONSUS_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_FLEXANET if (machine_is_flexanet()) { - parts = flexanet_partitions; + *parts = flexanet_partitions; nb_parts = ARRAY_SIZE(flexanet_partitions); - sa1100_map.size = FLEXANET_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_FREEBIRD if (machine_is_freebird()) { - parts = freebird_partitions; + *parts = freebird_partitions; nb_parts = ARRAY_SIZE(freebird_partitions); - sa1100_map.size = FREEBIRD_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_FRODO if (machine_is_frodo()) { - parts = frodo_partitions; + *parts = frodo_partitions; nb_parts = ARRAY_SIZE(frodo_partitions); - sa1100_map.size = FRODO_FLASH_SIZE; - base = 0x00000000; } #endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT if (machine_is_graphicsclient()) { - parts = graphicsclient_partitions; + *parts = graphicsclient_partitions; nb_parts = ARRAY_SIZE(graphicsclient_partitions); - sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; } #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER if (machine_is_graphicsmaster()) { - parts = graphicsmaster_partitions; + *parts = graphicsmaster_partitions; nb_parts = ARRAY_SIZE(graphicsmaster_partitions); - sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; } #endif -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - parts = h3600_partitions; - nb_parts = ARRAY_SIZE(h3600_partitions); - sa1100_map.size = H3600_FLASH_SIZE; - sa1100_map.set_vpp = h3600_set_vpp; +#ifdef CONFIG_SA1100_H3XXX + if (machine_is_h3xxx()) { + *parts = h3xxx_partitions; + nb_parts = ARRAY_SIZE(h3xxx_partitions); } #endif #ifdef CONFIG_SA1100_HACKKIT if (machine_is_hackkit()) { - parts = hackkit_partitions; + *parts = hackkit_partitions; nb_parts = ARRAY_SIZE(hackkit_partitions); - sa1100_map.size = HACKKIT_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_HUW_WEBPANEL if (machine_is_huw_webpanel()) { - parts = huw_webpanel_partitions; + *parts = huw_webpanel_partitions; nb_parts = ARRAY_SIZE(huw_webpanel_partitions); - sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE; + } +#endif +#ifdef CONFIG_SA1100_JORNADA56X + if (machine_is_jornada56x()) { + *parts = jornada56x_partitions; + nb_parts = ARRAY_SIZE(jornada56x_partitions); } #endif #ifdef CONFIG_SA1100_JORNADA720 if (machine_is_jornada720()) { - parts = jornada720_partitions; + *parts = jornada720_partitions; nb_parts = ARRAY_SIZE(jornada720_partitions); - sa1100_map.size = JORNADA720_FLASH_SIZE; - sa1100_map.set_vpp = jornada720_set_vpp; - } -#endif -#ifdef CONFIG_SA1100_NANOENGINE - if (machine_is_nanoengine()) { - parts = nanoengine_partitions; - nb_parts = ARRAY_SIZE(nanoengine_partitions); - sa1100_map.size = NANOENGINE_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_PANGOLIN if (machine_is_pangolin()) { - parts = pangolin_partitions; + *parts = pangolin_partitions; nb_parts = ARRAY_SIZE(pangolin_partitions); - sa1100_map.size = PANGOLIN_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_PT_SYSTEM3 if (machine_is_pt_system3()) { - parts = system3_partitions; + *parts = system3_partitions; nb_parts = ARRAY_SIZE(system3_partitions); - sa1100_map.size = SYSTEM3_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_SHANNON if (machine_is_shannon()) { - parts = shannon_partitions; + *parts = shannon_partitions; nb_parts = ARRAY_SIZE(shannon_partitions); - sa1100_map.size = SHANNON_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_SHERMAN if (machine_is_sherman()) { - parts = sherman_partitions; + *parts = sherman_partitions; nb_parts = ARRAY_SIZE(sherman_partitions); - sa1100_map.size = SHERMAN_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_SIMPAD if (machine_is_simpad()) { - parts = simpad_partitions; + *parts = simpad_partitions; nb_parts = ARRAY_SIZE(simpad_partitions); - sa1100_map.size = SIMPAD_FLASH_SIZE; - } -#endif -#ifdef CONFIG_SA1100_SIMPUTER - if (machine_is_simputer()) { - parts = simputer_partitions; - nb_parts = ARRAY_SIZE(simputer_partitions); - sa1100_map.size = SIMPUTER_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_STORK if (machine_is_stork()) { - parts = stork_partitions; + *parts = stork_partitions; nb_parts = ARRAY_SIZE(stork_partitions); - sa1100_map.size = STORK_FLASH_SIZE; + } +#endif +#ifdef CONFIG_SA1100_TRIZEPS + if (machine_is_trizeps()) { + *parts = trizeps_partitions; + nb_parts = ARRAY_SIZE(trizeps_partitions); } #endif #ifdef CONFIG_SA1100_YOPY if (machine_is_yopy()) { - parts = yopy_partitions; + *parts = yopy_partitions; nb_parts = ARRAY_SIZE(yopy_partitions); - sa1100_map.size = YOPY_FLASH_SIZE; } #endif + return nb_parts; +} +#endif + +struct sa_info { + unsigned long base; + unsigned long size; + int width; + void (*set_vpp)(struct map_info *, int); + char name[16]; + struct map_info *map; + struct mtd_info *mtd; +}; + +#define NR_SUBMTD 4 + +static struct sa_info info[NR_SUBMTD]; + +static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) +{ + struct mtd_info *subdev[nr]; + struct map_info *maps; + int i, found = 0, ret = 0; + /* - * For simple flash devices, use ioremap to map the flash. + * Allocate the map_info structs in one go. */ - if (base != (unsigned long)-1) { - if (!request_mem_region(base, sa1100_map.size, "flash")) - return -EBUSY; - sa1100_map.map_priv_2 = base; - sa1100_map.map_priv_1 = (unsigned long) - ioremap(base, sa1100_map.size); + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; + + memset(maps, 0, sizeof(struct map_info) * nr); + + /* + * Claim and then map the memory regions. + */ + for (i = 0; i < nr; i++) { + if (sa[i].base == (unsigned long)-1) + break; + + sa[i].map = maps + i; + sa[i].map->name = sa[i].name; + sprintf(sa[i].name, "sa1100-%d", i); + + if (!request_mem_region(sa[i].base, sa[i].size, sa[i].name)) { + i -= 1; + ret = -EBUSY; + break; + } + + sa[i].map->virt = ioremap(sa[i].base, sa[i].size); + if (!sa[i].map->virt) { ret = -ENOMEM; - if (!sa1100_map.map_priv_1) - goto out_err; + break; } + sa[i].map->phys = sa[i].base; + sa[i].map->set_vpp = sa[i].set_vpp; + sa[i].map->bankwidth = sa[i].width; + sa[i].map->size = sa[i].size; + + simple_map_init(sa[i].map); + /* * Now let's probe for the actual flash. Do it here since * specific machine settings might have been set above. */ - printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); - mymtd = do_map_probe("jedec_probe", &sa1100_map); - if (!mymtd) - mymtd = do_map_probe("cfi_probe", &sa1100_map); + sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); + if (sa[i].mtd == NULL) { ret = -ENXIO; - if (!mymtd) - goto out_err; - mymtd->module = THIS_MODULE; + break; + } + sa[i].mtd->owner = THIS_MODULE; + subdev[i] = sa[i].mtd; + + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " + "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, + sa[i].width * 8); + found += 1; + } /* - * Dynamic partition selection stuff (might override the static ones) + * ENXIO is special. It means we didn't find a chip when + * we probed. We need to tear down the mapping, free the + * resource and mark it as such. */ -#ifdef CONFIG_MTD_REDBOOT_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_redboot_partitions(mymtd, &parsed_parts); + if (ret == -ENXIO) { + iounmap(sa[i].map->virt); + sa[i].map->virt = NULL; + release_mem_region(sa[i].base, sa[i].size); + } - if (ret > 0) { - part_type = "RedBoot"; - parsed_nr_parts = ret; + /* + * If we found one device, don't bother with concat support. + * If we found multiple devices, use concat if we have it + * available, otherwise fail. + */ + if (ret == 0 || ret == -ENXIO) { + if (found == 1) { + *rmtd = subdev[0]; + ret = 0; + } else if (found > 1) { + /* + * We detected multiple devices. Concatenate + * them together. + */ +#ifdef CONFIG_MTD_CONCAT + *rmtd = mtd_concat_create(subdev, found, + "sa1100"); + if (*rmtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "SA1100 flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; +#endif + } } + + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].map->virt) + iounmap(sa[i].map->virt); + release_mem_region(sa[i].base, sa[i].size); + } while (i-- > 0); + + kfree(maps); } + + return ret; +} + +static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) +{ + int i; + + del_mtd_partitions(mtd); + +#ifdef CONFIG_MTD_CONCAT + if (mtd != sa[0].mtd) + mtd_concat_destroy(mtd); #endif -#ifdef CONFIG_MTD_CMDLINE_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100"); - if (ret > 0) { - part_type = "Command Line"; - parsed_nr_parts = ret; + + for (i = NR_SUBMTD; i >= 0; i--) { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].map->virt) + iounmap(sa[i].map->virt); + release_mem_region(sa[i].base, sa[i].size); } + kfree(sa[0].map); +} + +/* + * A Thought: can we automatically detect the flash? + * - Check to see if the region is busy (yes -> failure) + * - Is the MSC setup for flash (no -> failure) + * - Probe for flash + */ +static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) +{ + struct map_info map; + struct mtd_info *mtd; + + printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", + phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); + + if (check_mem_region(phys, 0x08000000)) { + printk("busy\n"); + return; } -#endif - if (parsed_nr_parts > 0) { - parts = parsed_parts; - nb_parts = parsed_nr_parts; + if ((msc & 3) == 1) { + printk("wrong type\n"); + return; } - 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); + memset(&map, 0, sizeof(struct map_info)); + + map.name = "Probe"; + map.bankwidth = msc & MSC_RBW ? 2 : 4; + map.size = SZ_1M; + map.phys = phys; + map.virt = ioremap(phys, SZ_1M); + if (map.virt == NULL) + goto fail; + + simple_map_init(&map); + + /* Shame cfi_probe blurts out kernel messages... */ + mtd = do_map_probe("cfi_probe", &map); + if (mtd) + map_destroy(mtd); + iounmap(map.virt); + + if (!mtd) + goto fail; + + printk("pass\n"); + return; + + fail: + printk("failed\n"); +} + +static void __init sa1100_probe_flash(void) +{ + printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); + sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); + sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); + sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); + sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); + sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); + sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); + printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); +} + +static int __init sa1100_locate_flash(void) +{ + int i, nr = -ENODEV; + + sa1100_probe_flash(); + + if (machine_is_adsbitsy()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_assabet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + info[1].base = SA1100_CS1_PHYS; /* neponset */ + info[1].size = SZ_32M; + nr = 2; + } + if (machine_is_badge4()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + nr = 1; + } + if (machine_is_cerf()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_consus()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_flexanet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_freebird()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_frodo()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsclient()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsmaster()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_h3xxx()) { + info[0].set_vpp = h3xxx_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_huw_webpanel()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_itsy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_jornada56x()) { + info[0].set_vpp = jornada56x_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_jornada720()) { + info[0].set_vpp = jornada720_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_nanoengine()) { + info[0].base = SA1100_CS0_PHYS; + info[1].size = SZ_32M; + nr = 1; + } + if (machine_is_pangolin()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + nr = 1; + } + if (machine_is_pfs168()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_pleb()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_4M; + nr = 2; + } + if (machine_is_pt_system3()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_shannon()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + nr = 1; + } + if (machine_is_sherman()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_simpad()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_16M; + nr = 2; + } + if (machine_is_stork()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_trizeps()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_victor()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_2M; + nr = 1; + } + if (machine_is_yopy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_64M; + nr = 2; } - return 0; - out_err: - if (sa1100_map.map_priv_2 != -1) { - iounmap((void *)sa1100_map.map_priv_1); - release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); + if (nr < 0) + return nr; + + /* + * Retrieve the bankwidth from the MSC registers. + * We currently only implement CS0 and CS1 here. + */ + for (i = 0; i < nr; i++) { + switch (info[i].base) { + default: + printk(KERN_WARNING "SA1100 flash: unknown base address " + "0x%08lx, assuming CS0\n", info[i].base); + case SA1100_CS0_PHYS: + info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; + break; + + case SA1100_CS1_PHYS: + info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + break; } - return ret; + } + + return nr; } -static void __exit sa1100_mtd_cleanup(void) +static struct mtd_partition *parsed_parts; +const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; + +static void __init sa1100_locate_partitions(struct mtd_info *mtd) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - if (parsed_parts) - kfree(parsed_parts); + const char *part_type = NULL; + int nr_parts = 0; + + do { + /* + * Partition selection stuff. + */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); + if (nr_parts > 0) { + part_type = "dynamic"; + break; } - if (sa1100_map.map_priv_2 != -1) { - iounmap((void *)sa1100_map.map_priv_1); - release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); +#endif +#ifdef CONFIG_MTD_SA1100_STATICMAP + nr_parts = sa1100_static_partitions(&parsed_parts); + if (nr_parts > 0) { + part_type = "static"; + break; + } +#endif + } while (0); + + if (nr_parts == 0) { + printk(KERN_NOTICE "SA1100 flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(mtd); + } else { + printk(KERN_NOTICE "SA1100 flash: using %s partition " + "definition\n", part_type); + add_mtd_partitions(mtd, parsed_parts, nr_parts); } + + /* Always succeeds. */ +} + +static void __exit sa1100_destroy_partitions(void) +{ + if (parsed_parts) + kfree(parsed_parts); +} + +static struct mtd_info *mymtd; + +static int __init sa1100_mtd_init(void) +{ + int ret; + int nr; + + nr = sa1100_locate_flash(); + if (nr < 0) + return nr; + + ret = sa1100_setup_mtd(info, nr, &mymtd); + if (ret == 0) + sa1100_locate_partitions(mymtd); + + return ret; +} + +static void __exit sa1100_mtd_cleanup(void) +{ + sa1100_destroy_mtd(info, mymtd); + sa1100_destroy_partitions(); } module_init(sa1100_mtd_init); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/sbc8240.c @@ -0,0 +1,247 @@ +/* + * Handle mapping of the flash memory access routines on the SBC8240 board. + * + * Carolyn Smith, Tektronix, Inc. + * + * This code is GPLed + * + * $Id: sbc8240.c,v 1.4 2004/07/12 22:38:29 dwmw2 Exp $ + * + */ + +/* + * The SBC8240 has 2 flash banks. + * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors. + * It contains the U-Boot code (7 sectors) and the environment (1 sector). + * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector, + * 2 x 8 KiB sectors, 1 x 16 KiB sectors. + * Both parts are JEDEC compatible. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#define DEBUG + +#ifdef DEBUG +# define debugk(fmt,args...) printk(fmt ,##args) +#else +# define debugk(fmt,args...) +#endif + + +#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */ +#define WINDOW_SIZE0 0x00080000 +#define BUSWIDTH0 1 + +#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */ +#define WINDOW_SIZE1 0x00400000 +#define BUSWIDTH1 8 + +#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */ +#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */ + + +static struct map_info sbc8240_map[2] = { + { + .name = "sbc8240 Flash Bank #0", + .size = WINDOW_SIZE0, + .bankwidth = BUSWIDTH0, + }, + { + .name = "sbc8240 Flash Bank #1", + .size = WINDOW_SIZE1, + .bankwidth = BUSWIDTH1, + } +}; + +#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info)) + +/* + * The following defines the partition layout of SBC8240 boards. + * + * 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_MTD_PARTITIONS + +static struct mtd_partition sbc8240_uboot_partitions [] = { + /* Bank 0 */ + { + .name = "U-boot", /* U-Boot Firmware */ + .offset = 0, + .size = 0x00070000, /* 7 x 64 KiB sectors */ + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "environment", /* U-Boot environment */ + .offset = 0x00070000, + .size = 0x00010000, /* 1 x 64 KiB sector */ + }, +}; + +static struct mtd_partition sbc8240_fs_partitions [] = { + { + .name = "jffs", /* JFFS filesystem */ + .offset = 0, + .size = 0x003C0000, /* 4 * 15 * 64KiB */ + }, + { + .name = "tmp32", + .offset = 0x003C0000, + .size = 0x00020000, /* 4 * 32KiB */ + }, + { + .name = "tmp8a", + .offset = 0x003E0000, + .size = 0x00008000, /* 4 * 8KiB */ + }, + { + .name = "tmp8b", + .offset = 0x003E8000, + .size = 0x00008000, /* 4 * 8KiB */ + }, + { + .name = "tmp16", + .offset = 0x003F0000, + .size = 0x00010000, /* 4 * 16KiB */ + } +}; + +#define NB_OF(x) (sizeof (x) / sizeof (x[0])) + +/* trivial struct to describe partition information */ +struct mtd_part_def +{ + int nums; + unsigned char *type; + struct mtd_partition* mtd_part; +}; + +static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS]; +static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; + + +#endif /* CONFIG_MTD_PARTITIONS */ + + +int __init init_sbc8240_mtd (void) +{ + static struct _cjs { + u_long addr; + u_long size; + } pt[NUM_FLASH_BANKS] = { + { + .addr = WINDOW_ADDR0, + .size = WINDOW_SIZE0 + }, + { + .addr = WINDOW_ADDR1, + .size = WINDOW_SIZE1 + }, + }; + + int devicesfound = 0; + int i; + + for (i = 0; i < NUM_FLASH_BANKS; i++) { + printk (KERN_NOTICE MSG_PREFIX + "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr); + + sbc8240_map[i].map_priv_1 = + (unsigned long) ioremap (pt[i].addr, pt[i].size); + if (!sbc8240_map[i].map_priv_1) { + printk (MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + simple_map_init(&sbc8240_mtd[i]); + + sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]); + + if (sbc8240_mtd[i]) { + sbc8240_mtd[i]->module = THIS_MODULE; + devicesfound++; + } + } + + if (!devicesfound) { + printk(KERN_NOTICE MSG_PREFIX + "No suppported flash chips found!\n"); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions; + sbc8240_part_banks[0].type = "static image"; + sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions); + sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions; + sbc8240_part_banks[1].type = "static file system"; + sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions); + + for (i = 0; i < NUM_FLASH_BANKS; i++) { + + if (!sbc8240_mtd[i]) continue; + if (sbc8240_part_banks[i].nums == 0) { + printk (KERN_NOTICE MSG_PREFIX + "No partition info available, registering whole device\n"); + add_mtd_device(sbc8240_mtd[i]); + } else { + printk (KERN_NOTICE MSG_PREFIX + "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name); + add_mtd_partitions (sbc8240_mtd[i], + sbc8240_part_banks[i].mtd_part, + sbc8240_part_banks[i].nums); + } + } +#else + printk(KERN_NOTICE MSG_PREFIX + "Registering %d flash banks at once\n", devicesfound); + + for (i = 0; i < devicesfound; i++) { + add_mtd_device(sbc8240_mtd[i]); + } +#endif /* CONFIG_MTD_PARTITIONS */ + + return devicesfound == 0 ? -ENXIO : 0; +} + +static void __exit cleanup_sbc8240_mtd (void) +{ + int i; + + for (i = 0; i < NUM_FLASH_BANKS; i++) { + if (sbc8240_mtd[i]) { + del_mtd_device (sbc8240_mtd[i]); + map_destroy (sbc8240_mtd[i]); + } + if (sbc8240_map[i].map_priv_1) { + iounmap ((void *) sbc8240_map[i].map_priv_1); + sbc8240_map[i].map_priv_1 = 0; + } + } +} + +module_init (init_sbc8240_mtd); +module_exit (cleanup_sbc8240_mtd); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Carolyn Smith "); +MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards"); + --- linux-2.4.21/drivers/mtd/maps/sbc_gxx.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/sbc_gxx.c @@ -17,7 +17,7 @@ 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.21 2003/01/24 13:40:14 dwmw2 Exp $ + $Id: sbc_gxx.c,v 1.34 2005/01/12 22:34:35 gleixner Exp $ The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. @@ -84,21 +84,21 @@ // 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; +static void __iomem *iomapadr; +static DEFINE_SPINLOCK(sbc_gxx_spin); /* 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 } + { .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 @@ -114,32 +114,12 @@ } -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) +static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) { - __u32 ret; + map_word ret; spin_lock(&sbc_gxx_spin); sbc_gxx_page(map, ofs); - ret = readl(iomapadr + (ofs & WINDOW_MASK)); + ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); spin_unlock(&sbc_gxx_spin); return ret; } @@ -155,33 +135,17 @@ sbc_gxx_page(map, from); memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); spin_unlock(&sbc_gxx_spin); - (__u8*)to += thislen; + 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) +static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&sbc_gxx_spin); sbc_gxx_page(map, adr); - writel(d, iomapadr + (adr & WINDOW_MASK)); + writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); spin_unlock(&sbc_gxx_spin); } @@ -203,19 +167,16 @@ } 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 + .name = "SBC-GXx flash", + .phys = NO_XIP, + .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 + .bankwidth = 1, + .read = sbc_gxx_read8, + .copy_from = sbc_gxx_copy_from, + .write = sbc_gxx_write8, + .copy_to = sbc_gxx_copy_to }; /* MTD device for all of the flash. */ @@ -228,26 +189,27 @@ map_destroy( all_mtd ); } - iounmap((void *)iomapadr); + iounmap(iomapadr); release_region(PAGE_IO,PAGE_IO_SIZE); } -int __init init_sbc_gxx(void) +static int __init 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); + iomapadr = 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" ); + if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { + 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 ); + iounmap(iomapadr); + return -EAGAIN; + } + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", sbc_gxx_map.name, @@ -261,7 +223,7 @@ return -ENXIO; } - all_mtd->module=THIS_MODULE; + all_mtd->owner = THIS_MODULE; /* Create MTD devices for each partition. */ add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); --- linux-2.4.21/drivers/mtd/maps/sc520cdp.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/sc520cdp.c @@ -16,7 +16,7 @@ * 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.11 2002/03/08 16:34:35 rkaiser Exp $ + * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $ * * * The SC520CDP is an evaluation board for the Elan SC520 processor available @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -84,88 +85,25 @@ #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 #0", + .size = WINDOW_SIZE_0, + .bankwidth = 4, + .phys = 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 Flash Bank #1", + .size = WINDOW_SIZE_1, + .bankwidth = 4, + .phys = 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 + .name = "SC520CDP DIL Flash", + .size = WINDOW_SIZE_2, + .bankwidth = 1, + .phys = WINDOW_ADDR_2 }, }; @@ -248,16 +186,16 @@ static void sc520cdp_setup_par(void) { - volatile unsigned long *mmcr; + volatile unsigned long __iomem *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); + mmcr = 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: */ + /* force physical address fields to BIOS defaults: */ for(i = 0; i < NUM_FLASH_BANKS; i++) - sc520cdp_map[i].map_priv_2 = par_table[i].default_address; + sc520cdp_map[i].phys = par_table[i].default_address; return; } @@ -282,10 +220,10 @@ 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; + sc520cdp_map[i].phys = par_table[i].default_address; } } - iounmap((void *)mmcr); + iounmap(mmcr); } #endif @@ -300,13 +238,18 @@ #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); + printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", + sc520cdp_map[i].size, sc520cdp_map[i].phys); - if (!sc520cdp_map[i].map_priv_1) { + sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); + + if (!sc520cdp_map[i].virt) { printk("Failed to ioremap_nocache\n"); return -EIO; } + + simple_map_init(&sc520cdp_map[i]); + mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); if(!mymtd[i]) mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); @@ -314,11 +257,11 @@ mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); if (mymtd[i]) { - mymtd[i]->module = THIS_MODULE; + mymtd[i]->owner = THIS_MODULE; ++devices_found; } else { - iounmap((void *)sc520cdp_map[i].map_priv_1); + iounmap(sc520cdp_map[i].virt); } } if(devices_found >= 2) { @@ -346,9 +289,9 @@ for (i = 0; i < NUM_FLASH_BANKS; i++) { if (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; + if (sc520cdp_map[i].virt) { + iounmap(sc520cdp_map[i].virt); + sc520cdp_map[i].virt = NULL; } } } --- linux-2.4.21/drivers/mtd/maps/scb2_flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,6 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.2 2003/01/24 13:09:56 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * @@ -14,7 +14,7 @@ * try to request it here, but if it fails, we carry on anyway. * * This is how the chip is attached, so said the schematic: - * * a 4 MiB (32 Mb) 16 bit chip + * * a 4 MiB (32 Mib) 16 bit chip * * a 1 MiB memory region * * A20 and A21 pulled up * * D8-D15 ignored @@ -31,23 +31,24 @@ * * The actual BIOS layout has been mostly reverse engineered. Intel BIOS * updates for this board include 10 related (*.bio - &.bi9) binary files and - * another seperate (*.bbo) binary file. The 10 files are 64k of data + a + * another separate (*.bbo) binary file. The 10 files are 64k of data + a * small header. If the headers are stripped off, the 10 64k files can be * concatenated into a 640k image. This is your BIOS image, proper. The - * seperate .bbo file also has a small header. It is the 'Boot Block' + * separate .bbo file also has a small header. It is the 'Boot Block' * recovery BIOS. Once the header is stripped, no further prep is needed. * As best I can tell, the BIOS is arranged as such: * offset 0x00000 to 0x4ffff (320k): unknown - SCSI BIOS, etc? * offset 0x50000 to 0xeffff (640k): BIOS proper * offset 0xf0000 ty 0xfffff (64k): Boot Block region * - * Intel's BIOS update program flashes the BIOS and Boot Block in seperate + * Intel's BIOS update program flashes the BIOS and Boot Block in separate * steps. Probably a wise thing to do. */ #include #include #include +#include #include #include #include @@ -60,65 +61,13 @@ #define SCB2_ADDR 0xfff00000 #define SCB2_WINDOW 0x00100000 -static __u8 scb2_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -static __u16 scb2_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -static __u32 scb2_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -static void scb2_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -static void scb2_copy_to(struct map_info *map, unsigned long to, - const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} -static void *scb2_ioaddr; +static void __iomem *scb2_ioaddr; static struct mtd_info *scb2_mtd; -struct map_info scb2_map = { - name: "SCB2 BIOS Flash", - size: 0, - buswidth: 1, - read8: scb2_read8, - read16: scb2_read16, - read32: scb2_read32, - copy_from: scb2_copy_from, - write8: scb2_write8, - write16: scb2_write16, - write32: scb2_write32, - copy_to: scb2_copy_to, +static struct map_info scb2_map = { + .name = "SCB2 BIOS Flash", + .size = 0, + .bankwidth = 1, }; static int region_fail; @@ -137,6 +86,8 @@ return -1; } + /* I wasn't here. I didn't see. dwmw2. */ + /* the chip is sometimes bigger than the map - what a waste */ mtd->size = map->size; @@ -211,9 +162,12 @@ return -ENOMEM; } - scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr; + scb2_map.phys = SCB2_ADDR; + scb2_map.virt = scb2_ioaddr; scb2_map.size = SCB2_WINDOW; + simple_map_init(&scb2_map); + /* try to find a chip */ scb2_mtd = do_map_probe("cfi_probe", &scb2_map); @@ -225,7 +179,7 @@ return -ENODEV; } - scb2_mtd->module = THIS_MODULE; + scb2_mtd->owner = THIS_MODULE; if (scb2_fixup_mtd(scb2_mtd) < 0) { del_mtd_device(scb2_mtd); map_destroy(scb2_mtd); @@ -235,7 +189,7 @@ return -ENODEV; } - printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n", + printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); add_mtd_device(scb2_mtd); @@ -264,21 +218,21 @@ pci_set_drvdata(dev, NULL); } -static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = { +static struct pci_device_id scb2_flash_pci_ids[] = { { - vendor: PCI_VENDOR_ID_SERVERWORKS, - device: PCI_DEVICE_ID_SERVERWORKS_CSB5, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID }, { 0, } }; static struct pci_driver scb2_flash_driver = { - name: "Intel SCB2 BIOS Flash", - id_table: scb2_flash_pci_ids, - probe: scb2_flash_probe, - remove: __devexit_p(scb2_flash_remove), + .name = "Intel SCB2 BIOS Flash", + .id_table = scb2_flash_pci_ids, + .probe = scb2_flash_probe, + .remove = __devexit_p(scb2_flash_remove), }; static int __init --- linux-2.4.21/drivers/mtd/maps/scx200_docflash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/scx200_docflash.c @@ -2,7 +2,7 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.1 2003/01/24 13:20:40 dwmw2 Exp $ + $Id: scx200_docflash.c,v 1.10 2004/11/28 09:40:40 dwmw2 Exp $ National Semiconductor SCx200 flash mapped with DOCCS */ @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -75,49 +76,12 @@ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) #endif -static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} static struct map_info scx200_docflash_map = { .name = "NatSemi SCx200 DOCCS Flash", - .read8 = scx200_docflash_read8, - .read16 = scx200_docflash_read16, - .copy_from = scx200_docflash_copy_from, - .write8 = scx200_docflash_write8, - .write16 = scx200_docflash_write16, - .copy_to = scx200_docflash_copy_to }; -int __init init_scx200_docflash(void) +static int __init init_scx200_docflash(void) { unsigned u; unsigned base; @@ -209,12 +173,15 @@ scx200_docflash_map.size = size; if (width == 8) - scx200_docflash_map.buswidth = 1; + scx200_docflash_map.bankwidth = 1; else - scx200_docflash_map.buswidth = 2; + scx200_docflash_map.bankwidth = 2; - scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); - if (!scx200_docflash_map.map_priv_1) { + simple_map_init(&scx200_docflash_map); + + scx200_docflash_map.phys = docmem.start; + scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size); + if (!scx200_docflash_map.virt) { printk(KERN_ERR NAME ": failed to ioremap the flash\n"); release_resource(&docmem); return -EIO; @@ -223,7 +190,7 @@ mymtd = do_map_probe(flashtype, &scx200_docflash_map); if (!mymtd) { printk(KERN_ERR NAME ": unable to detect flash\n"); - iounmap((void *)scx200_docflash_map.map_priv_1); + iounmap(scx200_docflash_map.virt); release_resource(&docmem); return -ENXIO; } @@ -231,7 +198,7 @@ if (size < mymtd->size) printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; #if PARTITION partition_info[3].offset = mymtd->size-partition_info[3].size; @@ -253,8 +220,8 @@ #endif map_destroy(mymtd); } - if (scx200_docflash_map.map_priv_1) { - iounmap((void *)scx200_docflash_map.map_priv_1); + if (scx200_docflash_map.virt) { + iounmap(scx200_docflash_map.virt); release_resource(&docmem); } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/sharpsl-flash.c @@ -0,0 +1,101 @@ +/* + * sharpsl-flash.c + * + * Copyright (C) 2001 Lineo Japan, Inc. + * Copyright (C) 2002 SHARP + * + * $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $ + * + * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp + * Handle mapping of the flash on the RPX Lite and CLLF boards + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define WINDOW_ADDR 0x00000000 +#define WINDOW_SIZE 0x01000000 +#define BANK_WIDTH 2 + +static struct mtd_info *mymtd; + +struct map_info sharpsl_map = { + .name = "sharpsl-flash", + .size = WINDOW_SIZE, + .bankwidth = BANK_WIDTH, + .phys = WINDOW_ADDR +}; + +static struct mtd_partition sharpsl_partitions[1] = { + { + name: "Filesystem", + size: 0x006d0000, + offset: 0x00120000 + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_sharpsl(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + char *part_type = "static"; + + printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); + if (!sharpsl_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("map_rom", &sharpsl_map); + if (!mymtd) { + iounmap(sharpsl_map.virt); + return -ENXIO; + } + + mymtd->owner = THIS_MODULE; + + parts = sharpsl_partitions; + nb_parts = NB_OF(sharpsl_partitions); + + printk(KERN_NOTICE "Using %s partision definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + + return 0; +} + +static void __exit cleanup_sharpsl(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (sharpsl_map.virt) { + iounmap(sharpsl_map.virt); + sharpsl_map.virt = 0; + } +} + +module_init(init_sharpsl); +module_exit(cleanup_sharpsl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("SHARP (Original: Arnold Christensen )"); +MODULE_DESCRIPTION("MTD map driver for SHARP SL series"); --- linux-2.4.21/drivers/mtd/maps/solutionengine.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/solutionengine.c @@ -1,5 +1,5 @@ /* - * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $ + * $Id: solutionengine.c,v 1.14 2004/09/16 23:27:14 gleixner Exp $ * * Flash and EPROM on Hitachi Solution Engine and similar boards. * @@ -11,31 +11,13 @@ #include #include #include +#include #include #include #include #include #include - - -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); - -__u32 soleng_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void soleng_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - +#include static struct mtd_info *flash_mtd; static struct mtd_info *eprom_mtd; @@ -43,35 +25,33 @@ static struct mtd_partition *parsed_parts; struct map_info soleng_eprom_map = { - name: "Solution Engine EPROM", - size: 0x400000, - buswidth: 4, - copy_from: soleng_copy_from, + .name = "Solution Engine EPROM", + .size = 0x400000, + .bankwidth = 4, }; struct map_info soleng_flash_map = { - name: "Solution Engine FLASH", - size: 0x400000, - buswidth: 4, - read32: soleng_read32, - copy_from: soleng_copy_from, - write32: soleng_write32, + .name = "Solution Engine FLASH", + .size = 0x400000, + .bankwidth = 4, }; +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + #ifdef CONFIG_MTD_SUPERH_RESERVE static struct mtd_partition superh_se_partitions[] = { /* Reserved for boot code, read-only */ { - name: "flash_boot", - offset: 0x00000000, - size: CONFIG_MTD_SUPERH_RESERVE, - mask_flags: MTD_WRITEABLE, + .name = "flash_boot", + .offset = 0x00000000, + .size = CONFIG_MTD_SUPERH_RESERVE, + .mask_flags = MTD_WRITEABLE, }, /* All else is writable (e.g. JFFS) */ { - name: "Flash FS", - offset: MTDPART_OFS_NXTBLK, - size: MTDPART_SIZ_FULL, + .name = "Flash FS", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, } }; #endif /* CONFIG_MTD_SUPERH_RESERVE */ @@ -81,16 +61,22 @@ int nr_parts = 0; /* First probe at offset 0 */ - soleng_flash_map.map_priv_1 = P2SEGADDR(0); - soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000); + soleng_flash_map.phys = 0; + soleng_flash_map.virt = (void __iomem *)P2SEGADDR(0); + soleng_eprom_map.phys = 0x01000000; + soleng_eprom_map.virt = (void __iomem *)P1SEGADDR(0x01000000); + simple_map_init(&soleng_eprom_map); + simple_map_init(&soleng_flash_map); printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); if (!flash_mtd) { /* Not there. Try swapping */ printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n"); - soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000); - soleng_eprom_map.map_priv_1 = P1SEGADDR(0); + soleng_flash_map.phys = 0x01000000; + soleng_flash_map.virt = P2SEGADDR(0x01000000); + soleng_eprom_map.phys = 0; + soleng_eprom_map.virt = P1SEGADDR(0); flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); if (!flash_mtd) { /* Eep. */ @@ -99,25 +85,20 @@ } } printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n", - soleng_flash_map.map_priv_1 & 0x1fffffff, - soleng_eprom_map.map_priv_1 & 0x1fffffff); - flash_mtd->module = THIS_MODULE; + soleng_flash_map.phys & 0x1fffffff, + soleng_eprom_map.phys & 0x1fffffff); + flash_mtd->owner = THIS_MODULE; eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map); if (eprom_mtd) { - eprom_mtd->module = THIS_MODULE; + eprom_mtd->owner = THIS_MODULE; add_mtd_device(eprom_mtd); } -#ifdef CONFIG_MTD_REDBOOT_PARTS - nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); - if (nr_parts > 0) - printk(KERN_NOTICE "Found RedBoot partition table.\n"); - else if (nr_parts < 0) - printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); -#endif /* CONFIG_MTD_REDBOOT_PARTS */ -#if CONFIG_MTD_SUPERH_RESERVE - if (nr_parts == 0) { + nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); + +#ifdef CONFIG_MTD_SUPERH_RESERVE + if (nr_parts <= 0) { printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", CONFIG_MTD_SUPERH_RESERVE); parsed_parts = superh_se_partitions; --- linux-2.4.21/drivers/mtd/maps/sun_uflash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/sun_uflash.c @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ +/* $Id: sun_uflash.c,v 1.11 2004/11/04 13:24:15 gleixner Exp $ * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -48,60 +47,11 @@ 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 + .name = "SUNW,???-????", + .size = UFLASH_WINDOW_SIZE, + .bankwidth = UFLASH_BUSWIDTH, }; int uflash_devinit(struct linux_ebus_device* edev) @@ -145,20 +95,21 @@ 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) { + pdev->map.phys = edev->resource[0].start; + pdev->map.virt = ioremap_nocache(edev->resource[0].start, pdev->map.size); + if(0 == pdev->map.virt) { printk("%s: failed to map device\n", __FUNCTION__); kfree(pdev->name); kfree(pdev); return(-1); } + simple_map_init(&pdev->map); + /* MTD registration */ pdev->mtd = do_map_probe("cfi_probe", &pdev->map); if(0 == pdev->mtd) { - iounmap((void *)pdev->map.map_priv_1); + iounmap((void *)pdev->map.virt); kfree(pdev->name); kfree(pdev); return(-ENXIO); @@ -166,7 +117,7 @@ list_add(&pdev->list, &device_list); - pdev->mtd->module = THIS_MODULE; + pdev->mtd->owner = THIS_MODULE; add_mtd_device(pdev->mtd); return(0); @@ -211,9 +162,9 @@ 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->map.virt) { + iounmap((void*)udev->map.virt); + udev->map.virt = 0; } if(0 != udev->name) { kfree(udev->name); --- linux-2.4.21/drivers/mtd/maps/tqm8xxl.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/tqm8xxl.c @@ -2,7 +2,7 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.4 2002/06/20 13:41:20 mag Exp $ + * $Id: tqm8xxl.c,v 1.13 2004/10/20 22:21:53 dwmw2 Exp $ * * based on rpxlite.c * @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -49,47 +50,7 @@ static struct map_info* map_banks[FLASH_BANK_MAX]; static struct mtd_part_def part_banks[FLASH_BANK_MAX]; static unsigned long num_banks; -static unsigned long start_scan_addr; - -__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs) -{ - return *((__u8 *)(map->map_priv_1 + ofs)); -} - -__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs) -{ - return *((__u16 *)(map->map_priv_1 + ofs)); -} - -__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs) -{ - return *((__u32 *)(map->map_priv_1 + ofs)); -} - -void tqm8xxl_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 tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *((__u8 *)(map->map_priv_1 + adr)) = d; -} - -void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *((__u16 *)( map->map_priv_1 + adr)) = d; -} - -void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *((__u32 *)(map->map_priv_1 + adr)) = d; -} - -void tqm8xxl_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 void __iomem *start_scan_addr; /* * Here are partition information for all known TQM8xxL series devices. @@ -107,50 +68,48 @@ static unsigned long tqm8xxl_max_flash_size = 0x00800000; /* partition definition for first flash bank - * also ref. to "drivers\char\flash_config.c" + * (cf. "drivers/char/flash_config.c") */ static struct mtd_partition tqm8xxl_partitions[] = { { - name: "ppcboot", - offset: 0x00000000, - size: 0x00020000, /* 128KB */ - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "ppcboot", + .offset = 0x00000000, + .size = 0x00020000, /* 128KB */ + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "kernel", /* default kernel image */ - offset: 0x00020000, - size: 0x000e0000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + .name = "kernel", /* default kernel image */ + .offset = 0x00020000, + .size = 0x000e0000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - name: "user", - offset: 0x00100000, - size: 0x00100000, + .name = "user", + .offset = 0x00100000, + .size = 0x00100000, }, { - name: "initrd", - offset: 0x00200000, - size: 0x00200000, + .name = "initrd", + .offset = 0x00200000, + .size = 0x00200000, } }; -/* partition definition for second flahs bank */ +/* partition definition for second flash bank */ static struct mtd_partition tqm8xxl_fs_partitions[] = { { - name: "cramfs", - offset: 0x00000000, - size: 0x00200000, + .name = "cramfs", + .offset = 0x00000000, + .size = 0x00200000, }, { - name: "jffs", - offset: 0x00200000, - size: 0x00200000, - //size: MTDPART_SIZ_FULL, + .name = "jffs", + .offset = 0x00200000, + .size = 0x00200000, + //.size = MTDPART_SIZ_FULL, } }; #endif -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - int __init init_tqm_mtd(void) { int idx = 0, ret = 0; @@ -160,67 +119,73 @@ flash_addr = bd->bi_flashstart; flash_size = bd->bi_flashsize; - //request maximum flash size address spzce - start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); + + //request maximum flash size address space + start_scan_addr = ioremap(flash_addr, flash_size); if (!start_scan_addr) { - //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR); - printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); return -EIO; } - for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) - { + + for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { if(mtd_size >= flash_size) break; - printk("%s: chip probing count %d\n", __FUNCTION__, idx); + printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL); - if(map_banks[idx] == NULL) - { - //return -ENOMEM; + if(map_banks[idx] == NULL) { ret = -ENOMEM; + /* FIXME: What if some MTD devices were probed already? */ goto error_mem; } + memset((void *)map_banks[idx], 0, sizeof(struct map_info)); map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); - if(map_banks[idx]->name == NULL) - { - //return -ENOMEM; + + if (!map_banks[idx]->name) { ret = -ENOMEM; + /* FIXME: What if some MTD devices were probed already? */ goto error_mem; } - memset((void *)map_banks[idx]->name, 0, 16); - sprintf(map_banks[idx]->name, "TQM8xxL%d", idx); + map_banks[idx]->size = flash_size; - map_banks[idx]->buswidth = 4; - map_banks[idx]->read8 = tqm8xxl_read8; - map_banks[idx]->read16 = tqm8xxl_read16; - map_banks[idx]->read32 = tqm8xxl_read32; - map_banks[idx]->copy_from = tqm8xxl_copy_from; - map_banks[idx]->write8 = tqm8xxl_write8; - map_banks[idx]->write16 = tqm8xxl_write16; - map_banks[idx]->write32 = tqm8xxl_write32; - map_banks[idx]->copy_to = tqm8xxl_copy_to; + map_banks[idx]->bankwidth = 4; + + simple_map_init(map_banks[idx]); + + map_banks[idx]->virt = start_scan_addr; + map_banks[idx]->phys = flash_addr; + /* FIXME: This looks utterly bogus, but I'm trying to + preserve the behaviour of the original (shown here)... + map_banks[idx]->map_priv_1 = start_scan_addr + ((idx > 0) ? (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); + */ + + if (idx && mtd_banks[idx-1]) { + map_banks[idx]->virt += mtd_banks[idx-1]->size; + map_banks[idx]->phys += mtd_banks[idx-1]->size; + } + //start to probe flash chips mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); - if(mtd_banks[idx]) - { - mtd_banks[idx]->module = THIS_MODULE; + + if (mtd_banks[idx]) { + mtd_banks[idx]->owner = THIS_MODULE; mtd_size += mtd_banks[idx]->size; num_banks++; - printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + + printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, mtd_banks[idx]->name, mtd_banks[idx]->size); } } /* no supported flash chips found */ - if(!num_banks) - { - printk("TQM8xxL: No support flash chips found!\n"); + if (!num_banks) { + printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n"); ret = -ENXIO; goto error_mem; } @@ -231,12 +196,13 @@ */ part_banks[0].mtd_part = tqm8xxl_partitions; part_banks[0].type = "Static image"; - part_banks[0].nums = NB_OF(tqm8xxl_partitions); + part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions); + part_banks[1].mtd_part = tqm8xxl_fs_partitions; part_banks[1].type = "Static file system"; - part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions); - for(idx = 0; idx < num_banks ; idx++) - { + part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions); + + for(idx = 0; idx < num_banks ; idx++) { if (part_banks[idx].nums == 0) { printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx); add_mtd_device(mtd_banks[idx]); @@ -254,12 +220,9 @@ #endif return 0; error_mem: - for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) - { - if(map_banks[idx] != NULL) - { - if(map_banks[idx]->name != NULL) - { + for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { + if(map_banks[idx] != NULL) { + if(map_banks[idx]->name != NULL) { kfree(map_banks[idx]->name); map_banks[idx]->name = NULL; } @@ -267,18 +230,15 @@ map_banks[idx] = NULL; } } - //return -ENOMEM; error: - iounmap((void *)start_scan_addr); - //return -ENXIO; + iounmap(start_scan_addr); return ret; } static void __exit cleanup_tqm_mtd(void) { unsigned int idx = 0; - for(idx = 0 ; idx < num_banks ; idx++) - { + for(idx = 0 ; idx < num_banks ; idx++) { /* destroy mtd_info previously allocated */ if (mtd_banks[idx]) { del_mtd_partitions(mtd_banks[idx]); @@ -288,8 +248,9 @@ kfree(map_banks[idx]->name); kfree(map_banks[idx]); } + if (start_scan_addr) { - iounmap((void *)start_scan_addr); + iounmap(start_scan_addr); start_scan_addr = 0; } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/ts5500_flash.c @@ -0,0 +1,141 @@ +/* + * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board + * + * Copyright (C) 2004 Sean Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * + * Note: + * - In order for detection to work, jumper 3 must be set. + * - Drive A and B use a proprietary FTL from General Software which isn't + * supported as of yet so standard drives can't be mounted; you can create + * your own (e.g. jffs) file system. + * - If you have created your own jffs file system and the bios overwrites + * it during boot, try disabling Drive A: and B: in the boot order. + * + * $Id: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#define WINDOW_ADDR 0x09400000 +#define WINDOW_SIZE 0x00200000 + +static struct map_info ts5500_map = { + .name = "TS-5500 Flash", + .size = WINDOW_SIZE, + .bankwidth = 1, + .phys = WINDOW_ADDR +}; + +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition ts5500_partitions[] = { + { + .name = "Drive A", + .offset = 0, + .size = 0x0e0000 + }, + { + .name = "BIOS", + .offset = 0x0e0000, + .size = 0x020000, + }, + { + .name = "Drive B", + .offset = 0x100000, + .size = 0x100000 + } +}; + +#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition)) + +#endif + +static struct mtd_info *mymtd; + +static int __init init_ts5500_map(void) +{ + int rc = 0; + + ts5500_map.virt = ioremap_nocache(ts5500_map.phys, ts5500_map.size); + + if(!ts5500_map.virt) { + printk(KERN_ERR "Failed to ioremap_nocache\n"); + rc = -EIO; + goto err_out_ioremap; + } + + simple_map_init(&ts5500_map); + + mymtd = do_map_probe("jedec_probe", &ts5500_map); + if(!mymtd) + mymtd = do_map_probe("map_rom", &ts5500_map); + + if(!mymtd) { + rc = -ENXIO; + goto err_out_map; + } + + mymtd->owner = THIS_MODULE; +#ifdef CONFIG_MTD_PARTITIONS + add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS); +#else + add_mtd_device(mymtd); +#endif + + return 0; + +err_out_map: + map_destroy(mymtd); +err_out_ioremap: + iounmap(ts5500_map.virt); + + return rc; +} + +static void __exit cleanup_ts5500_map(void) +{ + if (mymtd) { +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(mymtd); +#else + del_mtd_device(mymtd); +#endif + map_destroy(mymtd); + } + + if (ts5500_map.virt) { + iounmap(ts5500_map.virt); + ts5500_map.virt = NULL; + } +} + +module_init(init_ts5500_map); +module_exit(cleanup_ts5500_map); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young "); +MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board"); + --- linux-2.4.21/drivers/mtd/maps/tsunami_flash.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/tsunami_flash.c @@ -2,25 +2,29 @@ * tsunami_flash.c * * flash chip on alpha ds10... - * $Id: tsunami_flash.c,v 1.1 2002/01/10 22:59:13 eric Exp $ + * $Id: tsunami_flash.c,v 1.9 2004/07/14 09:52:55 dwmw2 Exp $ */ #include #include +#include #include +#include #define FLASH_ENABLE_PORT 0x00C00001 #define FLASH_ENABLE_BYTE 0x01 #define FLASH_DISABLE_BYTE 0x00 #define MAX_TIG_FLASH_SIZE (12*1024*1024) -static inline __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset) +static inline map_word tsunami_flash_read8(struct map_info *map, unsigned long offset) { - return tsunami_tig_readb(offset); + map_word val; + val.x[0] = tsunami_tig_readb(offset); + return val; } -static void tsunami_flash_write8(struct map_info *map, __u8 value, unsigned long offset) +static void tsunami_flash_write8(struct map_info *map, map_word value, unsigned long offset) { - tsunami_tig_writeb(value, offset); + tsunami_tig_writeb(value.x[0], offset); } static void tsunami_flash_copy_from( @@ -58,18 +62,12 @@ static struct map_info tsunami_flash_map = { .name = "flash chip on the Tsunami TIG bus", .size = MAX_TIG_FLASH_SIZE, - .buswidth = 1, - .read8 = tsunami_flash_read8, - .read16 = 0, - .read32 = 0, + .phys = NO_XIP; + .bankwidth = 1, + .read = tsunami_flash_read8, .copy_from = tsunami_flash_copy_from, - .write8 = tsunami_flash_write8, - .write16 = 0, - .write32 = 0, + .write = tsunami_flash_write8, .copy_to = tsunami_flash_copy_to, - .set_vpp = 0, - .map_priv_1 = 0, - }; static struct mtd_info *tsunami_flash_mtd; @@ -88,7 +86,7 @@ static int __init init_tsunami_flash(void) { - static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; char **type; tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT); @@ -99,7 +97,7 @@ tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map); } if (tsunami_flash_mtd) { - tsunami_flash_mtd->module = THIS_MODULE; + tsunami_flash_mtd->owner = THIS_MODULE; add_mtd_device(tsunami_flash_mtd); return 0; } --- linux-2.4.21/drivers/mtd/maps/uclinux.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/uclinux.c @@ -5,7 +5,7 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: uclinux.c,v 1.2 2002/08/07 00:43:45 gerg Exp $ + * $Id: uclinux.c,v 1.10 2005/01/05 18:05:13 dwmw2 Exp $ */ /****************************************************************************/ @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,58 +25,11 @@ /****************************************************************************/ -__u8 uclinux_read8(struct map_info *map, unsigned long ofs) -{ - return(*((__u8 *) (map->map_priv_1 + ofs))); -} - -__u16 uclinux_read16(struct map_info *map, unsigned long ofs) -{ - return(*((__u16 *) (map->map_priv_1 + ofs))); -} - -__u32 uclinux_read32(struct map_info *map, unsigned long ofs) -{ - return(*((__u32 *) (map->map_priv_1 + ofs))); -} - -void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(map->map_priv_1 + from), len); -} - -void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *((__u8 *) (map->map_priv_1 + adr)) = d; -} - -void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *((__u16 *) (map->map_priv_1 + adr)) = d; -} - -void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *((__u32 *) (map->map_priv_1 + adr)) = d; -} - -void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy((void *) (map->map_priv_1 + to), from, len); -} /****************************************************************************/ struct map_info uclinux_ram_map = { - name: "RAM", - read8: uclinux_read8, - read16: uclinux_read16, - read32: uclinux_read32, - copy_from: uclinux_copy_from, - write8: uclinux_write8, - write16: uclinux_write16, - write32: uclinux_write32, - copy_to: uclinux_copy_to, + .name = "RAM", }; struct mtd_info *uclinux_ram_mtdinfo; @@ -83,7 +37,7 @@ /****************************************************************************/ struct mtd_partition uclinux_romfs[] = { - { name: "ROMfs", offset: 0 } + { .name = "ROMfs" } }; #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) @@ -93,8 +47,8 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct map_info *map = (struct map_info *) mtd->priv; - *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from)); + struct map_info *map = mtd->priv; + *mtdbuf = (u_char *) (map->virt + ((int) from)); *retlen = len; return(0); } @@ -108,29 +62,30 @@ extern char _ebss; mapp = &uclinux_ram_map; - mapp->map_priv_2 = (unsigned long) &_ebss; + mapp->phys = (unsigned long) &_ebss; mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); - mapp->buswidth = 4; + mapp->bankwidth = 4; printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", (int) mapp->map_priv_2, (int) mapp->size); - mapp->map_priv_1 = (unsigned long) - ioremap_nocache(mapp->map_priv_2, mapp->size); + mapp->virt = ioremap_nocache(mapp->phys, mapp->size); - if (mapp->map_priv_1 == 0) { + if (mapp->virt == 0) { printk("uclinux[mtd]: ioremap_nocache() failed\n"); return(-EIO); } + simple_map_init(mapp); + mtd = do_map_probe("map_ram", mapp); if (!mtd) { printk("uclinux[mtd]: failed to find a mapping?\n"); - iounmap((void *) mapp->map_priv_1); + iounmap(mapp->virt); return(-ENXIO); } - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->point = uclinux_point; mtd->priv = mapp; @@ -155,8 +110,8 @@ uclinux_ram_mtdinfo = NULL; } if (uclinux_ram_map.map_priv_1) { - iounmap((void *) uclinux_ram_map.map_priv_1); - uclinux_ram_map.map_priv_1 = 0; + iounmap((void *) uclinux_ram_map.virt); + uclinux_ram_map.virt = 0; } } --- linux-2.4.21/drivers/mtd/maps/vmax301.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/maps/vmax301.c @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $ +// $Id: vmax301.c,v 1.31 2005/01/12 22:34:35 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -24,6 +24,7 @@ #include #include +#include #define WINDOW_START 0xd8000 @@ -37,7 +38,7 @@ 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 DEFINE_SPINLOCK(vmax301_spin); static void __vmax301_page(struct map_info *map, unsigned long page) { @@ -53,32 +54,12 @@ __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) +static map_word vmax301_read8(struct map_info *map, unsigned long ofs) { - __u32 ret; + map_word ret; spin_lock(&vmax301_spin); vmax301_page(map, ofs); - ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK)); + ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); spin_unlock(&vmax301_spin); return ret; } @@ -99,27 +80,11 @@ } } -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) +static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&vmax301_spin); vmax301_page(map, adr); - writel(d, map->map_priv_2 + (adr & WINDOW_MASK)); + writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK)); spin_unlock(&vmax301_spin); } @@ -142,34 +107,28 @@ 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 Internal Flash", + .phys = NO_XIP, + .size = 3*2*1024*1024, + .bankwidth = 1, + .read = vmax301_read8, + .copy_from = vmax301_copy_from, + .write = vmax301_write8, + .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 + .name = "VMAX301 Socket", + .phys = NO_XIP, + .size = 0, + .bankwidth = 1, + .read = vmax301_read8, + .copy_from = vmax301_copy_from, + .write = vmax301_write8, + .copy_to = vmax301_copy_to, + .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), + .map_priv_2 = 0xFFFFFFFF } }; @@ -206,8 +165,8 @@ 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); + vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; + vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); for (i=0; i<2; i++) { vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); @@ -218,7 +177,7 @@ if (!vmax_mtd[i]) vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); if (vmax_mtd[i]) { - vmax_mtd[i]->module = THIS_MODULE; + vmax_mtd[i]->owner = THIS_MODULE; add_mtd_device(vmax_mtd[i]); } } --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/walnut.c @@ -0,0 +1,122 @@ +/* + * $Id: walnut.c,v 1.2 2004/12/10 12:07:42 holindho Exp $ + * + * Mapping for Walnut flash + * (used ebony.c as a "framework") + * + * Heikki Lindholm + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* these should be in platforms/4xx/walnut.h ? */ +#define WALNUT_FLASH_ONBD_N(x) (x & 0x02) +#define WALNUT_FLASH_SRAM_SEL(x) (x & 0x01) +#define WALNUT_FLASH_LOW 0xFFF00000 +#define WALNUT_FLASH_HIGH 0xFFF80000 +#define WALNUT_FLASH_SIZE 0x80000 + +static struct mtd_info *flash; + +static struct map_info walnut_map = { + .name = "Walnut flash", + .size = WALNUT_FLASH_SIZE, + .bankwidth = 1, +}; + +/* Actually, OpenBIOS is the last 128 KiB of the flash - better + * partitioning could be made */ +static struct mtd_partition walnut_partitions[] = { + { + .name = "OpenBIOS", + .offset = 0x0, + .size = WALNUT_FLASH_SIZE, + /*.mask_flags = MTD_WRITEABLE, */ /* force read-only */ + } +}; + +int __init init_walnut(void) +{ + u8 fpga_brds1; + void *fpga_brds1_adr; + void *fpga_status_adr; + unsigned long flash_base; + + /* this should already be mapped (platform/4xx/walnut.c) */ + fpga_status_adr = ioremap(WALNUT_FPGA_BASE, 8); + if (!fpga_status_adr) + return -ENOMEM; + + fpga_brds1_adr = fpga_status_adr+5; + fpga_brds1 = readb(fpga_brds1_adr); + /* iounmap(fpga_status_adr); */ + + if (WALNUT_FLASH_ONBD_N(fpga_brds1)) { + printk("The on-board flash is disabled (U79 sw 5)!"); + return -EIO; + } + if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) + flash_base = WALNUT_FLASH_LOW; + else + flash_base = WALNUT_FLASH_HIGH; + + walnut_map.phys = flash_base; + walnut_map.virt = + (void __iomem *)ioremap(flash_base, walnut_map.size); + + if (!walnut_map.virt) { + printk("Failed to ioremap flash.\n"); + return -EIO; + } + + simple_map_init(&walnut_map); + + flash = do_map_probe("jedec_probe", &walnut_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, walnut_partitions, + ARRAY_SIZE(walnut_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_walnut(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (walnut_map.virt) { + iounmap((void *)walnut_map.virt); + walnut_map.virt = 0; + } +} + +module_init(init_walnut); +module_exit(cleanup_walnut); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Heikki Lindholm "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405GP Walnut boards"); --- /dev/null +++ linux-2.4.21/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -0,0 +1,181 @@ +/* + * $Id: wr_sbc82xx_flash.c,v 1.7 2004/11/04 13:24:15 gleixner Exp $ + * + * Map for flash chips on Wind River PowerQUICC II SBC82xx board. + * + * Copyright (C) 2004 Red Hat, Inc. + * + * Author: David Woodhouse + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct mtd_info *sbcmtd[3]; +static struct mtd_partition *sbcmtd_parts[3]; + +struct map_info sbc82xx_flash_map[3] = { + {.name = "Boot flash"}, + {.name = "Alternate boot flash"}, + {.name = "User flash"} +}; + +static struct mtd_partition smallflash_parts[] = { + { + .name = "space", + .size = 0x100000, + .offset = 0, + }, { + .name = "bootloader", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct mtd_partition bigflash_parts[] = { + { + .name = "bootloader", + .size = 0x00100000, + .offset = 0, + }, { + .name = "file system", + .size = 0x01f00000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "boot config", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "space", + .size = 0x01f00000, + .offset = MTDPART_OFS_APPEND, + } +}; + +static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + +#define init_sbc82xx_one_flash(map, br, or) \ +do { \ + (map).phys = (br & 1) ? (br & 0xffff8000) : 0; \ + (map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0; \ + switch (br & 0x00001800) { \ + case 0x00000000: \ + case 0x00000800: (map).bankwidth = 1; break; \ + case 0x00001000: (map).bankwidth = 2; break; \ + case 0x00001800: (map).bankwidth = 4; break; \ + } \ +} while (0); + +int __init init_sbc82xx_flash(void) +{ + volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; + int bigflash; + int i; + +#ifdef CONFIG_SBC8560 + mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t)); +#else + mc = &cpm2_immr->im_memctl; +#endif + + bigflash = 1; + if ((mc->memc_br0 & 0x00001800) == 0x00001800) + bigflash = 0; + + init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0); + init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6); + init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1); + +#ifdef CONFIG_SBC8560 + iounmap((void *) mc); +#endif + + for (i=0; i<3; i++) { + int8_t flashcs[3] = { 0, 6, 1 }; + int nr_parts; + + printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d", + sbc82xx_flash_map[i].name, + (sbc82xx_flash_map[i].size >> 20), + flashcs[i]); + if (!sbc82xx_flash_map[i].phys) { + /* We know it can't be at zero. */ + printk("): disabled by bootloader.\n"); + continue; + } + printk(" at %08lx)\n", sbc82xx_flash_map[i].phys); + + sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); + + if (!sbc82xx_flash_map[i].virt) { + printk("Failed to ioremap\n"); + continue; + } + + simple_map_init(&sbc82xx_flash_map[i]); + + sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]); + + if (!sbcmtd[i]) + continue; + + sbcmtd[i]->owner = THIS_MODULE; + + nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes, + &sbcmtd_parts[i], 0); + if (nr_parts > 0) { + add_mtd_partitions (sbcmtd[i], sbcmtd_parts[i], nr_parts); + continue; + } + + /* No partitioning detected. Use default */ + if (i == 2) { + add_mtd_device(sbcmtd[i]); + } else if (i == bigflash) { + add_mtd_partitions (sbcmtd[i], bigflash_parts, ARRAY_SIZE(bigflash_parts)); + } else { + add_mtd_partitions (sbcmtd[i], smallflash_parts, ARRAY_SIZE(smallflash_parts)); + } + } + return 0; +} + +static void __exit cleanup_sbc82xx_flash(void) +{ + int i; + + for (i=0; i<3; i++) { + if (!sbcmtd[i]) + continue; + + if (i<2 || sbcmtd_parts[i]) + del_mtd_partitions(sbcmtd[i]); + else + del_mtd_device(sbcmtd[i]); + + kfree(sbcmtd_parts[i]); + map_destroy(sbcmtd[i]); + + iounmap((void *)sbc82xx_flash_map[i].virt); + sbc82xx_flash_map[i].virt = 0; + } +} + +module_init(init_sbc82xx_flash); +module_exit(cleanup_sbc82xx_flash); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II"); --- /dev/null +++ linux-2.4.21/drivers/mtd/mtd_blkdevs-24.c @@ -0,0 +1,692 @@ +/* + * $Id: mtd_blkdevs-24.c,v 1.17 2005/01/05 17:35:22 dwmw2 Exp $ + * + * (C) 2003 David Woodhouse + * + * Interface to Linux 2.4 block layer for MTD 'translation layers'. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(blktrans_majors); + +extern struct semaphore mtd_table_mutex; +extern struct mtd_info *mtd_table[]; + +struct mtd_blkcore_priv { + devfs_handle_t devfs_dir_handle; + int blksizes[256]; + int sizes[256]; + struct hd_struct part_table[256]; + struct gendisk gd; + spinlock_t devs_lock; /* See comment in _request function */ + struct completion thread_dead; + int exiting; + wait_queue_head_t thread_wq; +}; + +static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr, + int devnum) +{ + struct list_head *this; + struct mtd_blktrans_dev *d; + + list_for_each(this, &tr->devs) { + d = list_entry(this, struct mtd_blktrans_dev, list); + + if (d->devnum == devnum) + return d; + } + return NULL; +} + +static inline struct mtd_blktrans_ops *get_tr(int major) +{ + struct list_head *this; + struct mtd_blktrans_ops *t; + + list_for_each(this, &blktrans_majors) { + t = list_entry(this, struct mtd_blktrans_ops, list); + + if (t->major == major) + return t; + } + return NULL; +} + +static int do_blktrans_request(struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev, + struct request *req) +{ + unsigned long block, nsect; + char *buf; + int minor; + + minor = MINOR(req->rq_dev); + block = req->sector; + nsect = req->current_nr_sectors; + buf = req->buffer; + + if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) { + printk(KERN_WARNING "Access beyond end of device.\n"); + return 0; + } + block += tr->blkcore_priv->part_table[minor].start_sect; + + switch(req->cmd) { + case READ: + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->readsect(dev, block, buf)) + return 0; + return 1; + + case WRITE: + if (!tr->writesect) + return 0; + + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->writesect(dev, block, buf)) + return 0; + return 1; + + default: + printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd); + return 0; + } +} + +static int mtd_blktrans_thread(void *arg) +{ + struct mtd_blktrans_ops *tr = arg; + struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major); + + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + current->flags |= PF_MEMALLOC; + + snprintf(current->comm, sizeof(current->comm), "%sd", tr->name); + + /* daemonize() doesn't do this for us since some kernel threads + actually want to deal with signals. We can't just call + exit_sighand() since that'll cause an oops when we finally + do exit. */ + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + daemonize("%sd", tr->name); + + while (!tr->blkcore_priv->exiting) { + struct request *req; + struct mtd_blktrans_dev *dev; + int devnum; + int res = 0; + DECLARE_WAITQUEUE(wait, current); + + spin_lock_irq(&io_request_lock); + + if (list_empty(&rq->queue_head)) { + + add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_unlock_irq(&io_request_lock); + + schedule(); + remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + + continue; + } + + req = blkdev_entry_next_request(&rq->queue_head); + + devnum = MINOR(req->rq_dev) >> tr->part_bits; + + /* The ll_rw_blk code knows not to touch the request + at the head of the queue */ + spin_unlock_irq(&io_request_lock); + + /* FIXME: Where can we store the dev, on which + we already have a refcount anyway? We need to + lock against concurrent addition/removal of devices, + but if we use the mtd_table_mutex we deadlock when + grok_partitions is called from the registration + callbacks. */ + spin_lock(&tr->blkcore_priv->devs_lock); + dev = tr_get_dev(tr, devnum); + spin_unlock(&tr->blkcore_priv->devs_lock); + + BUG_ON(!dev); + + /* Ensure serialisation of requests */ + down(&dev->sem); + + res = do_blktrans_request(tr, dev, req); + up(&dev->sem); + + if (!end_that_request_first(req, res, tr->name)) { + spin_lock_irq(&io_request_lock); + blkdev_dequeue_request(req); + end_that_request_last(req); + spin_unlock_irq(&io_request_lock); + } + } + complete_and_exit(&tr->blkcore_priv->thread_dead, 0); +} + +static void mtd_blktrans_request(struct request_queue *rq) +{ + struct mtd_blktrans_ops *tr = rq->queuedata; + wake_up(&tr->blkcore_priv->thread_wq); +} + +int blktrans_open(struct inode *i, struct file *f) +{ + struct mtd_blktrans_ops *tr = NULL; + struct mtd_blktrans_dev *dev = NULL; + int major_nr = MAJOR(i->i_rdev); + int minor_nr = MINOR(i->i_rdev); + int devnum; + int ret = -ENODEV; + + if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE)) + return -EROFS; + + down(&mtd_table_mutex); + + tr = get_tr(major_nr); + + if (!tr) + goto out; + + devnum = minor_nr >> tr->part_bits; + + dev = tr_get_dev(tr, devnum); + + if (!dev) + goto out; + + if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) { + ret = -ENODEV; + goto out; + } + + if (!try_inc_mod_count(dev->mtd->owner)) + goto out; + + if (!try_inc_mod_count(tr->owner)) + goto out_tr; + + dev->mtd->usecount++; + + ret = 0; + if (tr->open && (ret = tr->open(dev))) { + dev->mtd->usecount--; + if (dev->mtd->owner) + __MOD_DEC_USE_COUNT(dev->mtd->owner); + out_tr: + if (tr->owner) + __MOD_DEC_USE_COUNT(tr->owner); + } + out: + up(&mtd_table_mutex); + + return ret; +} + +int blktrans_release(struct inode *i, struct file *f) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int ret = 0; + int devnum; + + down(&mtd_table_mutex); + + tr = get_tr(MAJOR(i->i_rdev)); + if (!tr) { + up(&mtd_table_mutex); + return -ENODEV; + } + + devnum = MINOR(i->i_rdev) >> tr->part_bits; + dev = tr_get_dev(tr, devnum); + + if (!dev) { + up(&mtd_table_mutex); + return -ENODEV; + } + + if (tr->release) + ret = tr->release(dev); + + if (!ret) { + dev->mtd->usecount--; + if (dev->mtd->owner) + __MOD_DEC_USE_COUNT(dev->mtd->owner); + if (tr->owner) + __MOD_DEC_USE_COUNT(tr->owner); + } + + up(&mtd_table_mutex); + + return ret; +} + +static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev) +{ + struct gendisk *gd = &(tr->blkcore_priv->gd); + int i; + int minor = MINOR(rdev); + + if (minor & ((1<part_bits)-1) || !tr->part_bits) { + /* BLKRRPART on a partition. Go away. */ + return -ENOTTY; + } + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* We are required to prevent simultaneous open() ourselves. + The core doesn't do that for us. Did I ever mention how + much the Linux block layer sucks? Sledgehammer approach... */ + down(&mtd_table_mutex); + + for (i=0; i < (1<part_bits); i++) { + invalidate_device(MKDEV(tr->major, minor+i), 1); + gd->part[minor + i].start_sect = 0; + gd->part[minor + i].nr_sects = 0; + } + + grok_partitions(gd, minor, 1 << tr->part_bits, + dev->size); + up(&mtd_table_mutex); + + return 0; +} + +static int blktrans_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int devnum; + + switch(cmd) { + case BLKGETSIZE: + case BLKGETSIZE64: + case BLKBSZSET: + case BLKBSZGET: + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKPG: + case BLKELVGET: + case BLKELVSET: + return blk_ioctl(inode->i_rdev, cmd, arg); + } + + down(&mtd_table_mutex); + + tr = get_tr(MAJOR(inode->i_rdev)); + if (!tr) { + up(&mtd_table_mutex); + return -ENODEV; + } + + devnum = MINOR(inode->i_rdev) >> tr->part_bits; + dev = tr_get_dev(tr, devnum); + + up(&mtd_table_mutex); + + if (!dev) + return -ENODEV; + + switch(cmd) { + case BLKRRPART: + return mtd_blktrans_rrpart(inode->i_rdev, tr, dev); + + case BLKFLSBUF: + blk_ioctl(inode->i_rdev, cmd, arg); + if (tr->flush) + return tr->flush(dev); + /* The core code did the work, we had nothing to do. */ + return 0; + + case HDIO_GETGEO: + if (tr->getgeo) { + struct hd_geometry g; + struct gendisk *gd = &(tr->blkcore_priv->gd); + int ret; + + memset(&g, 0, sizeof(g)); + ret = tr->getgeo(dev, &g); + if (ret) + return ret; + + g.start = gd->part[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *)arg, &g, sizeof(g))) + return -EFAULT; + return 0; + } /* else */ + default: + return -ENOTTY; + } +} + +struct block_device_operations mtd_blktrans_ops = { + .owner = THIS_MODULE, + .open = blktrans_open, + .release = blktrans_release, + .ioctl = blktrans_ioctl, +}; + +int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) +{ + struct mtd_blktrans_ops *tr = new->tr; + struct list_head *this; + int last_devnum = -1; + int i; + + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + + spin_lock(&tr->blkcore_priv->devs_lock); + + list_for_each(this, &tr->devs) { + struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); + if (new->devnum == -1) { + /* Use first free number */ + if (d->devnum != last_devnum+1) { + /* Found a free devnum. Plug it in here */ + new->devnum = last_devnum+1; + list_add_tail(&new->list, &d->list); + goto added; + } + } else if (d->devnum == new->devnum) { + /* Required number taken */ + spin_unlock(&tr->blkcore_priv->devs_lock); + return -EBUSY; + } else if (d->devnum > new->devnum) { + /* Required number was free */ + list_add_tail(&new->list, &d->list); + goto added; + } + last_devnum = d->devnum; + } + if (new->devnum == -1) + new->devnum = last_devnum+1; + + if ((new->devnum << tr->part_bits) > 256) { + spin_unlock(&tr->blkcore_priv->devs_lock); + return -EBUSY; + } + + init_MUTEX(&new->sem); + list_add_tail(&new->list, &tr->devs); + added: + spin_unlock(&tr->blkcore_priv->devs_lock); + + if (!tr->writesect) + new->readonly = 1; + + for (i = new->devnum << tr->part_bits; + i < (new->devnum+1) << tr->part_bits; + i++) { + set_device_ro(MKDEV(tr->major, i), new->readonly); + tr->blkcore_priv->blksizes[i] = new->blksize; + tr->blkcore_priv->sizes[i] = 0; + tr->blkcore_priv->part_table[i].nr_sects = 0; + tr->blkcore_priv->part_table[i].start_sect = 0; + } + + /* + dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices + dwmw2: any code which sets blk_size[][] should be + size >> 10 /+ 2.4 and its dumb units */ + + tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = + (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */ + + /* But this is still in device's sectors? $DEITY knows */ + tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size; + + if (tr->part_bits) { + grok_partitions(&tr->blkcore_priv->gd, new->devnum, + 1 << tr->part_bits, new->size); + } +#ifdef CONFIG_DEVFS_FS + if (!tr->part_bits) { + char name[2]; + + name[0] = '0' + new->devnum; + name[1] = 0; + + new->blkcore_priv = + devfs_register(tr->blkcore_priv->devfs_dir_handle, + name, DEVFS_FL_DEFAULT, tr->major, + new->devnum, S_IFBLK|S_IRUGO|S_IWUGO, + &mtd_blktrans_ops, NULL); + } +#endif + return 0; +} + +int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) +{ + struct mtd_blktrans_ops *tr = old->tr; + int i; + + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + +#ifdef CONFIG_DEVFS_FS + if (!tr->part_bits) { + devfs_unregister(old->blkcore_priv); + old->blkcore_priv = NULL; + } else { + devfs_register_partitions(&tr->blkcore_priv->gd, + old->devnum << tr->part_bits, 1); + } +#endif + spin_lock(&tr->blkcore_priv->devs_lock); + list_del(&old->list); + spin_unlock(&tr->blkcore_priv->devs_lock); + + for (i = (old->devnum << tr->part_bits); + i < ((old->devnum+1) << tr->part_bits); i++) { + tr->blkcore_priv->sizes[i] = 0; + tr->blkcore_priv->part_table[i].nr_sects = 0; + tr->blkcore_priv->part_table[i].start_sect = 0; + } + + return 0; +} + +void blktrans_notify_remove(struct mtd_info *mtd) +{ + struct list_head *this, *this2, *next; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + list_for_each_safe(this2, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); + + if (dev->mtd == mtd) + tr->remove_dev(dev); + } + } +} + +void blktrans_notify_add(struct mtd_info *mtd) +{ + struct list_head *this; + + if (mtd->type == MTD_ABSENT) + return; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + tr->add_mtd(tr, mtd); + } + +} + +static struct mtd_notifier blktrans_notifier = { + .add = blktrans_notify_add, + .remove = blktrans_notify_remove, +}; + +int register_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + int ret, i; + + /* Register the notifier if/when the first device type is + registered, to prevent the link/init ordering from fucking + us over. */ + if (!blktrans_notifier.list.next) + register_mtd_user(&blktrans_notifier); + + tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); + if (!tr->blkcore_priv) + return -ENOMEM; + + memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); + + down(&mtd_table_mutex); + + ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops); + if (ret) { + printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", + tr->name, tr->major, ret); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + + blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request); + (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr; + + init_completion(&tr->blkcore_priv->thread_dead); + init_waitqueue_head(&tr->blkcore_priv->thread_wq); + + ret = kernel_thread(mtd_blktrans_thread, tr, + CLONE_FS|CLONE_FILES|CLONE_SIGHAND); + if (ret < 0) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); + devfs_unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + + tr->blkcore_priv->devfs_dir_handle = + devfs_mk_dir(NULL, tr->name, NULL); + + blksize_size[tr->major] = tr->blkcore_priv->blksizes; + blk_size[tr->major] = tr->blkcore_priv->sizes; + + tr->blkcore_priv->gd.major = tr->major; + tr->blkcore_priv->gd.major_name = tr->name; + tr->blkcore_priv->gd.minor_shift = tr->part_bits; + tr->blkcore_priv->gd.max_p = (1<part_bits) - 1; + tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table; + tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes; + tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits; + + spin_lock_init(&tr->blkcore_priv->devs_lock); + + add_gendisk(&tr->blkcore_priv->gd); + + INIT_LIST_HEAD(&tr->devs); + list_add(&tr->list, &blktrans_majors); + + for (i=0; itype != MTD_ABSENT) + tr->add_mtd(tr, mtd_table[i]); + } + up(&mtd_table_mutex); + + return 0; +} + +int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + struct list_head *this, *next; + + down(&mtd_table_mutex); + + /* Clean up the kernel thread */ + tr->blkcore_priv->exiting = 1; + wake_up(&tr->blkcore_priv->thread_wq); + wait_for_completion(&tr->blkcore_priv->thread_dead); + + /* Remove it from the list of active majors */ + list_del(&tr->list); + + /* Remove each of its devices */ + list_for_each_safe(this, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); + tr->remove_dev(dev); + } + + blksize_size[tr->major] = NULL; + blk_size[tr->major] = NULL; + + del_gendisk(&tr->blkcore_priv->gd); + + blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); + devfs_unregister_blkdev(tr->major, tr->name); + + devfs_unregister(tr->blkcore_priv->devfs_dir_handle); + + up(&mtd_table_mutex); + + kfree(tr->blkcore_priv); + + if (!list_empty(&tr->devs)) + BUG(); + return 0; +} + +static void __exit mtd_blktrans_exit(void) +{ + /* No race here -- if someone's currently in register_mtd_blktrans + we're screwed anyway. */ + if (blktrans_notifier.list.next) + unregister_mtd_user(&blktrans_notifier); +} + +module_exit(mtd_blktrans_exit); + +EXPORT_SYMBOL_GPL(register_mtd_blktrans); +EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); +EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); +EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); + +MODULE_AUTHOR("David Woodhouse "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); --- /dev/null +++ linux-2.4.21/drivers/mtd/mtd_blkdevs.c @@ -0,0 +1,478 @@ +/* + * $Id: mtd_blkdevs.c,v 1.24 2004/11/16 18:28:59 dwmw2 Exp $ + * + * (C) 2003 David Woodhouse + * + * Interface to Linux 2.5 block layer for MTD 'translation layers'. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(blktrans_majors); + +extern struct semaphore mtd_table_mutex; +extern struct mtd_info *mtd_table[]; + +struct mtd_blkcore_priv { + struct completion thread_dead; + int exiting; + wait_queue_head_t thread_wq; + struct request_queue *rq; + spinlock_t queue_lock; +}; + +static int do_blktrans_request(struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev, + struct request *req) +{ + unsigned long block, nsect; + char *buf; + + block = req->sector; + nsect = req->current_nr_sectors; + buf = req->buffer; + + if (!(req->flags & REQ_CMD)) + return 0; + + if (block + nsect > get_capacity(req->rq_disk)) + return 0; + + switch(rq_data_dir(req)) { + case READ: + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->readsect(dev, block, buf)) + return 0; + return 1; + + case WRITE: + if (!tr->writesect) + return 0; + + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->writesect(dev, block, buf)) + return 0; + return 1; + + default: + printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); + return 0; + } +} + +static int mtd_blktrans_thread(void *arg) +{ + struct mtd_blktrans_ops *tr = arg; + struct request_queue *rq = tr->blkcore_priv->rq; + + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + current->flags |= PF_MEMALLOC | PF_NOFREEZE; + + daemonize("%sd", tr->name); + + /* daemonize() doesn't do this for us since some kernel threads + actually want to deal with signals. We can't just call + exit_sighand() since that'll cause an oops when we finally + do exit. */ + spin_lock_irq(¤t->sighand->siglock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + spin_lock_irq(rq->queue_lock); + + while (!tr->blkcore_priv->exiting) { + struct request *req; + struct mtd_blktrans_dev *dev; + int res = 0; + DECLARE_WAITQUEUE(wait, current); + + req = elv_next_request(rq); + + if (!req) { + add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_unlock_irq(rq->queue_lock); + + schedule(); + remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + + spin_lock_irq(rq->queue_lock); + + continue; + } + + dev = req->rq_disk->private_data; + tr = dev->tr; + + spin_unlock_irq(rq->queue_lock); + + down(&dev->sem); + res = do_blktrans_request(tr, dev, req); + up(&dev->sem); + + spin_lock_irq(rq->queue_lock); + + end_request(req, res); + } + spin_unlock_irq(rq->queue_lock); + + complete_and_exit(&tr->blkcore_priv->thread_dead, 0); +} + +static void mtd_blktrans_request(struct request_queue *rq) +{ + struct mtd_blktrans_ops *tr = rq->queuedata; + wake_up(&tr->blkcore_priv->thread_wq); +} + + +static int blktrans_open(struct inode *i, struct file *f) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int ret = -ENODEV; + + dev = i->i_bdev->bd_disk->private_data; + tr = dev->tr; + + if (!try_module_get(dev->mtd->owner)) + goto out; + + if (!try_module_get(tr->owner)) + goto out_tr; + + /* FIXME: Locking. A hot pluggable device can go away + (del_mtd_device can be called for it) without its module + being unloaded. */ + dev->mtd->usecount++; + + ret = 0; + if (tr->open && (ret = tr->open(dev))) { + dev->mtd->usecount--; + module_put(dev->mtd->owner); + out_tr: + module_put(tr->owner); + } + out: + return ret; +} + +static int blktrans_release(struct inode *i, struct file *f) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int ret = 0; + + dev = i->i_bdev->bd_disk->private_data; + tr = dev->tr; + + if (tr->release) + ret = tr->release(dev); + + if (!ret) { + dev->mtd->usecount--; + module_put(dev->mtd->owner); + module_put(tr->owner); + } + + return ret; +} + + +static int blktrans_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; + struct mtd_blktrans_ops *tr = dev->tr; + + switch (cmd) { + case BLKFLSBUF: + if (tr->flush) + return tr->flush(dev); + /* The core code did the work, we had nothing to do. */ + return 0; + + case HDIO_GETGEO: + if (tr->getgeo) { + struct hd_geometry g; + int ret; + + memset(&g, 0, sizeof(g)); + ret = tr->getgeo(dev, &g); + if (ret) + return ret; + + g.start = get_start_sect(inode->i_bdev); + if (copy_to_user((void __user *)arg, &g, sizeof(g))) + return -EFAULT; + return 0; + } /* else */ + default: + return -ENOTTY; + } +} + +struct block_device_operations mtd_blktrans_ops = { + .owner = THIS_MODULE, + .open = blktrans_open, + .release = blktrans_release, + .ioctl = blktrans_ioctl, +}; + +int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) +{ + struct mtd_blktrans_ops *tr = new->tr; + struct list_head *this; + int last_devnum = -1; + struct gendisk *gd; + + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + + list_for_each(this, &tr->devs) { + struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); + if (new->devnum == -1) { + /* Use first free number */ + if (d->devnum != last_devnum+1) { + /* Found a free devnum. Plug it in here */ + new->devnum = last_devnum+1; + list_add_tail(&new->list, &d->list); + goto added; + } + } else if (d->devnum == new->devnum) { + /* Required number taken */ + return -EBUSY; + } else if (d->devnum > new->devnum) { + /* Required number was free */ + list_add_tail(&new->list, &d->list); + goto added; + } + last_devnum = d->devnum; + } + if (new->devnum == -1) + new->devnum = last_devnum+1; + + if ((new->devnum << tr->part_bits) > 256) { + return -EBUSY; + } + + init_MUTEX(&new->sem); + list_add_tail(&new->list, &tr->devs); + added: + if (!tr->writesect) + new->readonly = 1; + + gd = alloc_disk(1 << tr->part_bits); + if (!gd) { + list_del(&new->list); + return -ENOMEM; + } + gd->major = tr->major; + gd->first_minor = (new->devnum) << tr->part_bits; + gd->fops = &mtd_blktrans_ops; + + snprintf(gd->disk_name, sizeof(gd->disk_name), + "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); + snprintf(gd->devfs_name, sizeof(gd->devfs_name), + "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); + + /* 2.5 has capacity in units of 512 bytes while still + having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ + set_capacity(gd, (new->size * new->blksize) >> 9); + + gd->private_data = new; + new->blkcore_priv = gd; + gd->queue = tr->blkcore_priv->rq; + + if (new->readonly) + set_disk_ro(gd, 1); + + add_disk(gd); + + return 0; +} + +int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) +{ + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + + list_del(&old->list); + + del_gendisk(old->blkcore_priv); + put_disk(old->blkcore_priv); + + return 0; +} + +static void blktrans_notify_remove(struct mtd_info *mtd) +{ + struct list_head *this, *this2, *next; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + list_for_each_safe(this2, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); + + if (dev->mtd == mtd) + tr->remove_dev(dev); + } + } +} + +static void blktrans_notify_add(struct mtd_info *mtd) +{ + struct list_head *this; + + if (mtd->type == MTD_ABSENT) + return; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + tr->add_mtd(tr, mtd); + } + +} + +static struct mtd_notifier blktrans_notifier = { + .add = blktrans_notify_add, + .remove = blktrans_notify_remove, +}; + +int register_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + int ret, i; + + /* Register the notifier if/when the first device type is + registered, to prevent the link/init ordering from fucking + us over. */ + if (!blktrans_notifier.list.next) + register_mtd_user(&blktrans_notifier); + + tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); + if (!tr->blkcore_priv) + return -ENOMEM; + + memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); + + down(&mtd_table_mutex); + + ret = register_blkdev(tr->major, tr->name); + if (ret) { + printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", + tr->name, tr->major, ret); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + spin_lock_init(&tr->blkcore_priv->queue_lock); + init_completion(&tr->blkcore_priv->thread_dead); + init_waitqueue_head(&tr->blkcore_priv->thread_wq); + + tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); + if (!tr->blkcore_priv->rq) { + unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return -ENOMEM; + } + + tr->blkcore_priv->rq->queuedata = tr; + + ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); + if (ret < 0) { + blk_cleanup_queue(tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + + devfs_mk_dir(tr->name); + + INIT_LIST_HEAD(&tr->devs); + list_add(&tr->list, &blktrans_majors); + + for (i=0; itype != MTD_ABSENT) + tr->add_mtd(tr, mtd_table[i]); + } + + up(&mtd_table_mutex); + + return 0; +} + +int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + struct list_head *this, *next; + + down(&mtd_table_mutex); + + /* Clean up the kernel thread */ + tr->blkcore_priv->exiting = 1; + wake_up(&tr->blkcore_priv->thread_wq); + wait_for_completion(&tr->blkcore_priv->thread_dead); + + /* Remove it from the list of active majors */ + list_del(&tr->list); + + list_for_each_safe(this, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); + tr->remove_dev(dev); + } + + devfs_remove(tr->name); + blk_cleanup_queue(tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + + up(&mtd_table_mutex); + + kfree(tr->blkcore_priv); + + if (!list_empty(&tr->devs)) + BUG(); + return 0; +} + +static void __exit mtd_blktrans_exit(void) +{ + /* No race here -- if someone's currently in register_mtd_blktrans + we're screwed anyway. */ + if (blktrans_notifier.list.next) + unregister_mtd_user(&blktrans_notifier); +} + +module_exit(mtd_blktrans_exit); + +EXPORT_SYMBOL_GPL(register_mtd_blktrans); +EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); +EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); +EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); + +MODULE_AUTHOR("David Woodhouse "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); --- linux-2.4.21/drivers/mtd/mtdblock.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdblock.c @@ -1,52 +1,25 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.51 2001/11/20 11:42:33 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $ * - * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache + * (C) 2000-2003 Nicolas Pitre + * (C) 1999-2003 David Woodhouse */ #include #include #include #include +#include +#include #include +#include #include -#include - -#define MAJOR_NR MTD_BLOCK_MAJOR -#define DEVICE_NAME "mtdblock" -#define DEVICE_REQUEST mtdblock_request -#define DEVICE_NR(device) (device) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) -#define DEVICE_NO_RANDOM -#include -/* for old kernels... */ -#ifndef QUEUE_EMPTY -#define QUEUE_EMPTY (!CURRENT) -#endif -#if LINUX_VERSION_CODE < 0x20300 -#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) -#else -#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) -#endif - -#ifdef CONFIG_DEVFS_FS -#include -static void mtd_notify_add(struct mtd_info* mtd); -static void mtd_notify_remove(struct mtd_info* mtd); -static struct mtd_notifier notifier = { - mtd_notify_add, - mtd_notify_remove, - NULL -}; -static devfs_handle_t devfs_dir_handle = NULL; -static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; -#endif +#include static struct mtdblk_dev { - struct mtd_info *mtd; /* Locked */ + struct mtd_info *mtd; int count; struct semaphore cache_sem; unsigned char *cache_data; @@ -55,19 +28,6 @@ enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; } *mtdblks[MAX_MTD_DEVICES]; -static spinlock_t mtdblks_lock; - -static int mtd_sizes[MAX_MTD_DEVICES]; -static int mtd_blksizes[MAX_MTD_DEVICES]; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) -#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT -#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT -#else -#define BLK_INC_USE_COUNT do {} while(0) -#define BLK_DEC_USE_COUNT do {} while(0) -#endif - /* * Cache stuff... * @@ -151,7 +111,7 @@ return ret; /* - * Here we could argably set the cache state to STATE_CLEAN. + * Here we could argubly set the cache state to STATE_CLEAN. * However this could lead to inconsistency since we will not * be notified if this content is altered on the flash by other * means. Let's declare it empty and leave buffering tasks to @@ -277,57 +237,47 @@ return 0; } +static int mtdblock_readsect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) +{ + struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + return do_cached_read(mtdblk, block<<9, 512, buf); +} +static int mtdblock_writesect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) +{ + struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { + mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); + if (!mtdblk->cache_data) + return -EINTR; + /* -EINTR is not really correct, but it is the best match + * documented in man 2 write for all cases. We could also + * return -EAGAIN sometimes, but why bother? + */ + } + return do_cached_write(mtdblk, block<<9, 512, buf); +} -static int mtdblock_open(struct inode *inode, struct file *file) +static int mtdblock_open(struct mtd_blktrans_dev *mbd) { struct mtdblk_dev *mtdblk; - struct mtd_info *mtd; - int dev; + struct mtd_info *mtd = mbd->mtd; + int dev = mbd->devnum; DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); - if (!inode) - return -EINVAL; - - dev = MINOR(inode->i_rdev); - if (dev >= MAX_MTD_DEVICES) - return -EINVAL; - - BLK_INC_USE_COUNT; - - mtd = get_mtd_device(NULL, dev); - if (!mtd) - return -ENODEV; - if (MTD_ABSENT == mtd->type) { - put_mtd_device(mtd); - BLK_DEC_USE_COUNT; - return -ENODEV; - } - - spin_lock(&mtdblks_lock); - - /* If it's already open, no need to piss about. */ if (mtdblks[dev]) { mtdblks[dev]->count++; - spin_unlock(&mtdblks_lock); - put_mtd_device(mtd); return 0; } - /* OK, it's not open. Try to find it */ - - /* First we have to drop the lock, because we have to - to things which might sleep. - */ - spin_unlock(&mtdblks_lock); - + /* OK, it's not open. Create cache info for it */ mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - if (!mtdblk) { - put_mtd_device(mtd); - BLK_DEC_USE_COUNT; + if (!mtdblk) return -ENOMEM; - } + memset(mtdblk, 0, sizeof(*mtdblk)); mtdblk->count = 1; mtdblk->mtd = mtd; @@ -337,336 +287,102 @@ if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && mtdblk->mtd->erasesize) { mtdblk->cache_size = mtdblk->mtd->erasesize; - mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); - if (!mtdblk->cache_data) { - put_mtd_device(mtdblk->mtd); - kfree(mtdblk); - BLK_DEC_USE_COUNT; - return -ENOMEM; - } - } - - /* OK, we've created a new one. Add it to the list. */ - - spin_lock(&mtdblks_lock); - - if (mtdblks[dev]) { - /* Another CPU made one at the same time as us. */ - mtdblks[dev]->count++; - spin_unlock(&mtdblks_lock); - put_mtd_device(mtdblk->mtd); - vfree(mtdblk->cache_data); - kfree(mtdblk); - return 0; + mtdblk->cache_data = NULL; } mtdblks[dev] = mtdblk; - mtd_sizes[dev] = mtdblk->mtd->size/1024; - if (mtdblk->mtd->erasesize) - mtd_blksizes[dev] = mtdblk->mtd->erasesize; - if (mtd_blksizes[dev] > PAGE_SIZE) - mtd_blksizes[dev] = PAGE_SIZE; - set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); - - spin_unlock(&mtdblks_lock); DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); return 0; } -static release_t mtdblock_release(struct inode *inode, struct file *file) +static int mtdblock_release(struct mtd_blktrans_dev *mbd) { - int dev; - struct mtdblk_dev *mtdblk; - DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); - - if (inode == NULL) - release_return(-ENODEV); + int dev = mbd->devnum; + struct mtdblk_dev *mtdblk = mtdblks[dev]; - dev = MINOR(inode->i_rdev); - mtdblk = mtdblks[dev]; + DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); down(&mtdblk->cache_sem); write_cached_data(mtdblk); up(&mtdblk->cache_sem); - spin_lock(&mtdblks_lock); if (!--mtdblk->count) { /* It was the last usage. Free the device */ mtdblks[dev] = NULL; - spin_unlock(&mtdblks_lock); if (mtdblk->mtd->sync) mtdblk->mtd->sync(mtdblk->mtd); - put_mtd_device(mtdblk->mtd); vfree(mtdblk->cache_data); kfree(mtdblk); - } else { - spin_unlock(&mtdblks_lock); } - DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); - BLK_DEC_USE_COUNT; - release_return(0); -} - - -/* - * This is a special request_fn because it is executed in a process context - * to be able to sleep independently of the caller. The io_request_lock - * is held upon entry and exit. - * The head of our request queue is considered active so there is no need - * to dequeue requests before we are done. - */ -static void handle_mtdblock_request(void) -{ - struct request *req; - struct mtdblk_dev *mtdblk; - unsigned int res; - - for (;;) { - INIT_REQUEST; - req = CURRENT; - spin_unlock_irq(&io_request_lock); - mtdblk = mtdblks[MINOR(req->rq_dev)]; - res = 0; - - if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) - panic("%s: minor out of bounds", __FUNCTION__); - - if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) - goto end_req; - - // Handle the request - switch (req->cmd) - { - int err; - - case READ: - down(&mtdblk->cache_sem); - err = do_cached_read (mtdblk, req->sector << 9, - req->current_nr_sectors << 9, - req->buffer); - up(&mtdblk->cache_sem); - if (!err) - res = 1; - break; - - case WRITE: - // Read only device - if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) - break; - - // Do the write - down(&mtdblk->cache_sem); - err = do_cached_write (mtdblk, req->sector << 9, - req->current_nr_sectors << 9, - req->buffer); - up(&mtdblk->cache_sem); - if (!err) - res = 1; - break; - } - -end_req: - spin_lock_irq(&io_request_lock); - end_request(res); - } -} - -static volatile int leaving = 0; -static DECLARE_MUTEX_LOCKED(thread_sem); -static DECLARE_WAIT_QUEUE_HEAD(thr_wq); - -int mtdblock_thread(void *dummy) -{ - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); - - /* we might get involved when memory gets low, so use PF_MEMALLOC */ - tsk->flags |= PF_MEMALLOC; - strcpy(tsk->comm, "mtdblockd"); - spin_lock_irq(&tsk->sigmask_lock); - sigfillset(&tsk->blocked); - recalc_sigpending(tsk); - spin_unlock_irq(&tsk->sigmask_lock); - daemonize(); - - while (!leaving) { - add_wait_queue(&thr_wq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(&io_request_lock); - if (QUEUE_EMPTY || QUEUE_PLUGGED) { - spin_unlock_irq(&io_request_lock); - schedule(); - remove_wait_queue(&thr_wq, &wait); - } else { - remove_wait_queue(&thr_wq, &wait); - set_current_state(TASK_RUNNING); - handle_mtdblock_request(); - spin_unlock_irq(&io_request_lock); - } - } - - up(&thread_sem); return 0; } -#if LINUX_VERSION_CODE < 0x20300 -#define RQFUNC_ARG void -#else -#define RQFUNC_ARG request_queue_t *q -#endif - -static void mtdblock_request(RQFUNC_ARG) -{ - /* Don't do anything, except wake the thread if necessary */ - wake_up(&thr_wq); -} - - -static int mtdblock_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static int mtdblock_flush(struct mtd_blktrans_dev *dev) { - struct mtdblk_dev *mtdblk; - - mtdblk = mtdblks[MINOR(inode->i_rdev)]; - -#ifdef PARANOIA - if (!mtdblk) - BUG(); -#endif - - switch (cmd) { - case BLKGETSIZE: /* Return device size */ - return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg); - -#ifdef BLKGETSIZE64 - case BLKGETSIZE64: - return put_user((u64)mtdblk->mtd->size, (u64 *)arg); -#endif + struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; - 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); down(&mtdblk->cache_sem); write_cached_data(mtdblk); up(&mtdblk->cache_sem); + if (mtdblk->mtd->sync) mtdblk->mtd->sync(mtdblk->mtd); return 0; - - default: - return -EINVAL; - } } -#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 = -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) - owner: THIS_MODULE, -#endif - open: mtdblock_open, - release: mtdblock_release, - ioctl: mtdblock_ioctl -}; -#endif - -#ifdef CONFIG_DEVFS_FS -/* Notification that a new device has been added. Create the devfs entry for - * it. */ - -static void mtd_notify_add(struct mtd_info* mtd) +static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - char name[8]; + struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!mtd || mtd->type == MTD_ABSENT) + if (!dev) return; - sprintf(name, "%d", mtd->index); - devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, - DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, - S_IFBLK | S_IRUGO | S_IWUGO, - &mtd_fops, NULL); -} + memset(dev, 0, sizeof(*dev)); -static void mtd_notify_remove(struct mtd_info* mtd) -{ - if (!mtd || mtd->type == MTD_ABSENT) - return; + dev->mtd = mtd; + dev->devnum = mtd->index; + dev->blksize = 512; + dev->size = mtd->size >> 9; + dev->tr = tr; - devfs_unregister(devfs_rw_handle[mtd->index]); + if (!(mtd->flags & MTD_WRITEABLE)) + dev->readonly = 1; + + add_mtd_blktrans_dev(dev); } -#endif -int __init init_mtdblock(void) +static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { - int i; - - spin_lock_init(&mtdblks_lock); -#ifdef CONFIG_DEVFS_FS - if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) - { - printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", - MTD_BLOCK_MAJOR); - return -EAGAIN; - } - - devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); - register_mtd_user(¬ifier); -#else - 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; - } -#endif + del_mtd_blktrans_dev(dev); + kfree(dev); +} - /* We fill it in at open() time. */ - for (i=0; i< MAX_MTD_DEVICES; i++) { - mtd_sizes[i] = 0; - mtd_blksizes[i] = BLOCK_SIZE; - } - init_waitqueue_head(&thr_wq); - /* Allow the block size to default to BLOCK_SIZE. */ - blksize_size[MAJOR_NR] = mtd_blksizes; - blk_size[MAJOR_NR] = mtd_sizes; +static struct mtd_blktrans_ops mtdblock_tr = { + .name = "mtdblock", + .major = 31, + .part_bits = 0, + .open = mtdblock_open, + .flush = mtdblock_flush, + .release = mtdblock_release, + .readsect = mtdblock_readsect, + .writesect = mtdblock_writesect, + .add_mtd = mtdblock_add_mtd, + .remove_dev = mtdblock_remove_dev, + .owner = THIS_MODULE, +}; - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); - kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); - return 0; +static int __init init_mtdblock(void) +{ + return register_mtd_blktrans(&mtdblock_tr); } static void __exit cleanup_mtdblock(void) { - leaving = 1; - wake_up(&thr_wq); - down(&thread_sem); -#ifdef CONFIG_DEVFS_FS - unregister_mtd_user(¬ifier); - devfs_unregister(devfs_dir_handle); - devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); -#else - unregister_blkdev(MAJOR_NR,DEVICE_NAME); -#endif - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - blksize_size[MAJOR_NR] = NULL; - blk_size[MAJOR_NR] = NULL; + deregister_mtd_blktrans(&mtdblock_tr); } module_init(init_mtdblock); --- linux-2.4.21/drivers/mtd/mtdblock_ro.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdblock_ro.c @@ -1,301 +1,87 @@ /* - * $Id: mtdblock_ro.c,v 1.12 2001/11/20 11:42:33 dwmw2 Exp $ + * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $ * - * Read-only version of the mtdblock device, without the - * read/erase/modify/writeback stuff + * (C) 2003 David Woodhouse + * + * Simple read-only (writable only for RAM) mtdblock driver */ -#ifdef MTDBLOCK_DEBUG -#define DEBUGLVL debug -#endif - - -#include -#include - +#include +#include #include -#include - -#define MAJOR_NR MTD_BLOCK_MAJOR -#define DEVICE_NAME "mtdblock" -#define DEVICE_REQUEST mtdblock_request -#define DEVICE_NR(device) (device) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) -#define DEVICE_NO_RANDOM -#include - -#if LINUX_VERSION_CODE < 0x20300 -#define RQFUNC_ARG void -#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) -#else -#define RQFUNC_ARG request_queue_t *q -#endif - -#ifdef MTDBLOCK_DEBUG -static int debug = MTDBLOCK_DEBUG; -MODULE_PARM(debug, "i"); -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) -#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT -#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT -#else -#define BLK_INC_USE_COUNT do {} while(0) -#define BLK_DEC_USE_COUNT do {} while(0) -#endif - -static int mtd_sizes[MAX_MTD_DEVICES]; - +#include -static int mtdblock_open(struct inode *inode, struct file *file) +static int mtdblock_readsect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) { - struct mtd_info *mtd = NULL; - - int dev; - - DEBUG(1,"mtdblock_open\n"); - - if (inode == 0) - return -EINVAL; - - dev = MINOR(inode->i_rdev); - - mtd = get_mtd_device(NULL, dev); - if (!mtd) - return -EINVAL; - if (MTD_ABSENT == mtd->type) { - put_mtd_device(mtd); - return -EINVAL; - } - - BLK_INC_USE_COUNT; - - mtd_sizes[dev] = mtd->size>>9; - - DEBUG(1, "ok\n"); + size_t retlen; + if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf)) + return 1; return 0; } -static release_t mtdblock_release(struct inode *inode, struct file *file) +static int mtdblock_writesect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) { - int dev; - struct mtd_info *mtd; - - DEBUG(1, "mtdblock_release\n"); - - if (inode == NULL) - release_return(-ENODEV); - - dev = MINOR(inode->i_rdev); - mtd = __get_mtd_device(NULL, dev); - - if (!mtd) { - printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); - BLK_DEC_USE_COUNT; - release_return(-ENODEV); - } - - if (mtd->sync) - mtd->sync(mtd); - - put_mtd_device(mtd); - - DEBUG(1, "ok\n"); + size_t retlen; - BLK_DEC_USE_COUNT; - release_return(0); + if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) + return 1; + return 0; } - -static void mtdblock_request(RQFUNC_ARG) +static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - 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->current_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->current_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->current_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->current_nr_sectors); - */ + struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); - // Read only device - if ((mtd->flags & MTD_CAP_RAM) == 0) - { - res = 0; - break; - } + if (!dev) + return; - // Do the write - if (MTD_WRITE(mtd,current_request->sector<<9, - current_request->current_nr_sectors << 9, - &retlen, current_request->buffer) == 0) - res = 1; - else - res = 0; - break; + memset(dev, 0, sizeof(*dev)); - // Shouldn't happen - default: - printk("mtd: unknown request\n"); - break; - } + dev->mtd = mtd; + dev->devnum = mtd->index; + dev->blksize = 512; + dev->size = mtd->size >> 9; + dev->tr = tr; + if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) != + (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) + dev->readonly = 1; - // 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); - } + add_mtd_blktrans_dev(dev); } - - -static int mtdblock_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { - struct mtd_info *mtd; - - mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); - - if (!mtd) return -EINVAL; - - switch (cmd) { - case BLKGETSIZE: /* Return device size */ - return put_user((mtd->size >> 9), (unsigned long *) arg); - -#ifdef BLKGETSIZE64 - case BLKGETSIZE64: - return put_user((u64)mtd->size, (u64 *)arg); -#endif - - 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; - } + del_mtd_blktrans_dev(dev); + kfree(dev); } -#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 = -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) - owner: THIS_MODULE, -#endif - open: mtdblock_open, - release: mtdblock_release, - ioctl: mtdblock_ioctl +static struct mtd_blktrans_ops mtdblock_tr = { + .name = "mtdblock", + .major = 31, + .part_bits = 0, + .readsect = mtdblock_readsect, + .writesect = mtdblock_writesect, + .add_mtd = mtdblock_add_mtd, + .remove_dev = mtdblock_remove_dev, + .owner = THIS_MODULE, }; -#endif -int __init init_mtdblock(void) +static int __init mtdblock_init(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; - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); - return 0; + return register_mtd_blktrans(&mtdblock_tr); } -static void __exit cleanup_mtdblock(void) +static void __exit mtdblock_exit(void) { - unregister_blkdev(MAJOR_NR,DEVICE_NAME); - blk_size[MAJOR_NR] = NULL; - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + deregister_mtd_blktrans(&mtdblock_tr); } -module_init(init_mtdblock); -module_exit(cleanup_mtdblock); - +module_init(mtdblock_init); +module_exit(mtdblock_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Erwin Authried et al."); +MODULE_AUTHOR("David Woodhouse "); MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices"); --- linux-2.4.21/drivers/mtd/mtdchar.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdchar.c @@ -1,8 +1,7 @@ /* - * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.68 2005/02/08 19:12:50 nico Exp $ * * Character-device access to raw MTD devices. - * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c * */ @@ -10,10 +9,15 @@ #include #include #include +#include #include +#include +#include +#include #ifdef CONFIG_DEVFS_FS #include +#ifndef NEW static void mtd_notify_add(struct mtd_info* mtd); static void mtd_notify_remove(struct mtd_info* mtd); @@ -27,9 +31,98 @@ static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES]; #endif +static void mtd_notify_add(struct mtd_info* mtd) +{ +#ifndef NEW + char name[8]; +#endif + if (!mtd) + return; + +#ifdef NEW + devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); + + devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); +#else + struct file_operations mtd_fops; + + sprintf(name, "%d", mtd->index); + devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, + DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, + S_IFCHR | S_IRUGO | S_IWUGO, + &mtd_fops, NULL); + + sprintf(name, "%dro", mtd->index); + devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, + DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, + S_IFCHR | S_IRUGO, + &mtd_fops, NULL); +#endif +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; +#ifdef NEW + devfs_remove("mtd/%d", mtd->index); + devfs_remove("mtd/%dro", mtd->index); +#else + devfs_unregister(devfs_rw_handle[mtd->index]); + devfs_unregister(devfs_ro_handle[mtd->index]); +#endif +} + +#ifdef NEW +static struct mtd_notifier notifier = { + .add = mtd_notify_add, + .remove = mtd_notify_remove, +}; +#endif + +static inline void mtdchar_devfs_init(void) +{ +#ifdef NEW + devfs_mk_dir("mtd"); + register_mtd_user(¬ifier); +#else + devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); + + register_mtd_user(¬ifier); +#endif +} + +static inline void mtdchar_devfs_exit(void) +{ + unregister_mtd_user(¬ifier); + devfs_remove("mtd"); +} +#else /* !DEVFS */ +#define mtdchar_devfs_init() do { } while(0) +#define mtdchar_devfs_exit() do { } while(0) +#endif + +/* + * We use file->private_data to store a pointer to the MTDdevice. + * Since alighment is at least 32 bits, we have 2 bits free for OTP + * modes as well. + */ + +#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) + +#define MTD_MODE_OTP_FACT 1 +#define MTD_MODE_OTP_USER 2 +#define MTD_MODE(file) ((long)((file)->private_data) & 3) + +#define SET_MTD_MODE(file, mode) \ + do { long __p = (long)((file)->private_data); \ + (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) + static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd=(struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); switch (orig) { case 0: @@ -60,7 +153,7 @@ static int mtd_open(struct inode *inode, struct file *file) { - int minor = minor(inode->i_rdev); + int minor = iminor(inode); int devnum = minor >> 1; struct mtd_info *mtd; @@ -102,7 +195,7 @@ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = (struct mtd_info *)file->private_data; + mtd = TO_MTD(file); if (mtd->sync) mtd->sync(mtd); @@ -117,9 +210,9 @@ */ #define MAX_KMALLOC_SIZE 0x20000 -static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) +static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -146,8 +239,23 @@ if (!kbuf) return -ENOMEM; + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_OTP_USER: + ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); - if (!ret) { + } + /* Nand returns -EBADMSG on ecc errors, but it returns + * the data. For our userspace tools it is important + * to dump areas with ecc errors ! + * Userspace software which accesses NAND this way + * must be aware of the fact that it deals with NAND + */ + if (!ret || (ret == -EBADMSG)) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); @@ -158,6 +266,8 @@ count -= retlen; buf += retlen; + if (retlen == 0) + count = 0; } else { kfree(kbuf); @@ -170,9 +280,9 @@ return total_retlen; } /* mtd_read */ -static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos) +static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); char *kbuf; size_t retlen; size_t total_retlen=0; @@ -207,7 +317,20 @@ return -EFAULT; } + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = -EROFS; + break; + case MTD_MODE_OTP_USER: + if (!mtd->write_user_prot_reg) { + ret = -EOPNOTSUPP; + break; + } + ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + } if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -230,7 +353,7 @@ IOCTL calls for getting device parameters. ======================================================================*/ -static void mtd_erase_callback (struct erase_info *instr) +static void mtdchar_erase_callback (struct erase_info *instr) { wake_up((wait_queue_head_t *)instr->priv); } @@ -238,7 +361,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = TO_MTD(file); + void __user *argp = (void __user *)arg; int ret = 0; u_long size; @@ -246,17 +370,17 @@ size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (cmd & IOC_IN) { - ret = verify_area(VERIFY_READ, (char *)arg, size); + ret = verify_area(VERIFY_READ, argp, size); if (ret) return ret; } if (cmd & IOC_OUT) { - ret = verify_area(VERIFY_WRITE, (char *)arg, size); + ret = verify_area(VERIFY_WRITE, argp, size); if (ret) return ret; } switch (cmd) { case MEMGETREGIONCOUNT: - if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int))) + if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) return -EFAULT; break; @@ -264,24 +388,19 @@ { struct region_info_user ur; - if (copy_from_user( &ur, - (struct region_info_user *)arg, - sizeof(struct region_info_user))) { + if (copy_from_user(&ur, argp, 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]), + if (copy_to_user(argp, &(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))) + if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user))) return -EFAULT; break; @@ -302,13 +421,13 @@ init_waitqueue_head(&waitq); memset (erase,0,sizeof(struct erase_info)); - if (copy_from_user(&erase->addr, (u_long *)arg, - 2 * sizeof(u_long))) { + if (copy_from_user(&erase->addr, argp, + sizeof(struct erase_info_user))) { kfree(erase); return -EFAULT; } erase->mtd = mtd; - erase->callback = mtd_erase_callback; + erase->callback = mtdchar_erase_callback; erase->priv = (unsigned long)&waitq; /* @@ -346,7 +465,7 @@ if(!(file->f_mode & 2)) return -EPERM; - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 0x4096) @@ -355,7 +474,7 @@ if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); + ret = verify_area(VERIFY_READ, buf.ptr, buf.length); if (ret) return ret; @@ -371,7 +490,7 @@ ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) ret = -EFAULT; kfree(databuf); @@ -385,7 +504,7 @@ void *databuf; ssize_t retlen; - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 0x4096) @@ -394,7 +513,7 @@ if (!mtd->read_oob) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); + ret = verify_area(VERIFY_WRITE, buf.ptr, buf.length); if (ret) return ret; @@ -405,7 +524,7 @@ ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (put_user(retlen, (uint32_t __user *)argp)) ret = -EFAULT; else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) ret = -EFAULT; @@ -416,109 +535,146 @@ case MEMLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else - ret = mtd->lock(mtd, adrs[0], adrs[1]); + ret = mtd->lock(mtd, info.start, info.length); break; } case MEMUNLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->unlock) ret = -EOPNOTSUPP; else - ret = mtd->unlock(mtd, adrs[0], adrs[1]); + ret = mtd->unlock(mtd, info.start, info.length); break; } - case MEMWRITEDATA: + case MEMSETOOBSEL: { - struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen; + if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) + return -EFAULT; + break; + } - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + case MEMGETOOBSEL: + { + if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) return -EFAULT; + break; + } - if (buf.length > 0x4096) - return -EINVAL; + case MEMGETBADBLOCK: + { + loff_t offs; - if (!mtd->write_ecc) + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_isbad) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); - - if (ret) - return ret; - - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) - return -ENOMEM; - - if (copy_from_user(databuf, buf.ptr, buf.length)) { - kfree(databuf); - return -EFAULT; + return mtd->block_isbad(mtd, offs); + break; } - ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); - - if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) - ret = -EFAULT; + case MEMSETBADBLOCK: + { + loff_t offs; - kfree(databuf); + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_markbad) + ret = -EOPNOTSUPP; + else + return mtd->block_markbad(mtd, offs); break; - } - case MEMREADDATA: +#ifdef CONFIG_MTD_OTP + case OTPSELECT: { - struct mtd_oob_buf buf; - void *databuf; - ssize_t retlen = 0; - - if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) + int mode; + if (copy_from_user(&mode, argp, sizeof(int))) return -EFAULT; - - if (buf.length > 0x4096) - return -EINVAL; - - if (!mtd->read_ecc) + SET_MTD_MODE(file, 0); + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) ret = -EOPNOTSUPP; else - ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); - - if (ret) - return ret; + SET_MTD_MODE(file, MTD_MODE_OTP_FACT); + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_USER); + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + break; + } - databuf = kmalloc(buf.length, GFP_KERNEL); - if (!databuf) + case OTPGETREGIONCOUNT: + case OTPGETREGIONINFO: + { + struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + if (!buf) return -ENOMEM; - - ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); - - 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 = -EOPNOTSUPP; + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + if (mtd->get_fact_prot_info) + ret = mtd->get_fact_prot_info(mtd, buf, 4096); + break; + case MTD_MODE_OTP_USER: + if (mtd->get_user_prot_info) + ret = mtd->get_user_prot_info(mtd, buf, 4096); + break; + } + if (ret >= 0) { + if (cmd == OTPGETREGIONCOUNT) { + int nbr = ret / sizeof(struct otp_info); + ret = copy_to_user(argp, &nbr, sizeof(int)); + } else + ret = copy_to_user(argp, buf, ret); + if (ret) ret = -EFAULT; - - kfree(databuf); + } + kfree(buf); break; } + case OTPLOCK: + { + struct otp_info info; + + if (MTD_MODE(file) != MTD_MODE_OTP_USER) + return -EINVAL; + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + if (!mtd->lock_user_prot_reg) + return -EOPNOTSUPP; + ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); + break; + } +#endif default: - DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); ret = -ENOTTY; } @@ -526,84 +682,31 @@ } /* memory_ioctl */ static struct file_operations mtd_fops = { - owner: THIS_MODULE, - llseek: mtd_lseek, /* lseek */ - read: mtd_read, /* read */ - write: mtd_write, /* write */ - ioctl: mtd_ioctl, /* ioctl */ - open: mtd_open, /* open */ - release: mtd_close, /* release */ + .owner = THIS_MODULE, + .llseek = mtd_lseek, + .read = mtd_read, + .write = mtd_write, + .ioctl = mtd_ioctl, + .open = mtd_open, + .release = mtd_close, }; - -#ifdef CONFIG_DEVFS_FS -/* Notification that a new device has been added. Create the devfs entry for - * it. */ - -static void mtd_notify_add(struct mtd_info* mtd) -{ - char name[8]; - - if (!mtd) - return; - - sprintf(name, "%d", mtd->index); - devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, - DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2, - S_IFCHR | S_IRUGO | S_IWUGO, - &mtd_fops, NULL); - - sprintf(name, "%dro", mtd->index); - devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name, - DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1, - S_IFCHR | S_IRUGO, - &mtd_fops, NULL); -} - -static void mtd_notify_remove(struct mtd_info* mtd) -{ - if (!mtd) - return; - - devfs_unregister(devfs_rw_handle[mtd->index]); - devfs_unregister(devfs_ro_handle[mtd->index]); -} -#endif - static int __init init_mtdchar(void) { -#ifdef CONFIG_DEVFS_FS - if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) - { - printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", - MTD_CHAR_MAJOR); - return -EAGAIN; - } - - devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); - - register_mtd_user(¬ifier); -#else - if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) - { + if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", MTD_CHAR_MAJOR); return -EAGAIN; } -#endif + mtdchar_devfs_init(); return 0; } static void __exit cleanup_mtdchar(void) { -#ifdef CONFIG_DEVFS_FS - unregister_mtd_user(¬ifier); - devfs_unregister(devfs_dir_handle); - devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); -#else + mtdchar_devfs_exit(); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); -#endif } module_init(init_mtdchar); --- linux-2.4.21/drivers/mtd/mtdconcat.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdconcat.c @@ -3,9 +3,11 @@ * * (C) 2002 Robert Kaiser * + * NAND support by Christian Gan + * * This code is GPL * - * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ + * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $ */ #include @@ -35,21 +37,20 @@ #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) - /* * Given a pointer to the MTD object in the mtd_concat structure, * we can retrieve the pointer to that structure with this macro. */ #define CONCAT(x) ((struct mtd_concat *)(x)) - /* * MTD methods which look up the relevant subdevice, translate the * effective address and pass through to the subdevice. */ -static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int +concat_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -57,43 +58,43 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - if (from >= subdev->size) - { + if (from >= subdev->size) { + /* Not destined for this subdev */ size = 0; from -= subdev->size; + continue; } - else - { if (from + len > subdev->size) + /* First part goes into this subdev */ size = subdev->size - from; else + /* Entire transaction goes into this subdev */ size = len; err = subdev->read(subdev, from, size, &retsize, buf); - if(err) + if (err) break; *retlen += retsize; len -= size; - if(len == 0) + if (len == 0) break; err = -EINVAL; buf += size; from = 0; } - } return err; } -static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int +concat_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); int err = -EINVAL; @@ -104,18 +105,15 @@ *retlen = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size, retsize; - if (to >= subdev->size) - { + if (to >= subdev->size) { size = 0; to -= subdev->size; + continue; } - else - { if (to + len > subdev->size) size = subdev->size - to; else @@ -126,25 +124,232 @@ else err = subdev->write(subdev, to, size, &retsize, buf); - if(err) + if (err) break; *retlen += retsize; len -= size; - if(len == 0) + if (len == 0) break; err = -EINVAL; buf += size; to = 0; } + return err; +} + +static int +concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf, + struct nand_oobinfo *oobsel) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; + from -= subdev->size; + continue; + } + + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; + else + /* Entire transaction goes into this subdev */ + size = len; + + if (subdev->read_ecc) + err = subdev->read_ecc(subdev, from, size, + &retsize, buf, eccbuf, oobsel); + else + err = -EINVAL; + + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + if (eccbuf) { + eccbuf += subdev->oobsize; + /* in nand.c at least, eccbufs are + tagged with 2 (int)eccstatus'; we + must account for these */ + eccbuf += 2 * (sizeof (int)); + } + from = 0; } return err; } -static void concat_erase_callback (struct erase_info *instr) +static int +concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, + struct nand_oobinfo *oobsel) { - wake_up((wait_queue_head_t *)instr->priv); + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + size = 0; + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else if (subdev->write_ecc) + err = subdev->write_ecc(subdev, to, size, + &retsize, buf, eccbuf, oobsel); + else + err = -EINVAL; + + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + if (eccbuf) + eccbuf += subdev->oobsize; + to = 0; + } + return err; +} + +static int +concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) { + /* Not destined for this subdev */ + size = 0; + from -= subdev->size; + continue; + } + if (from + len > subdev->size) + /* First part goes into this subdev */ + size = subdev->size - from; + else + /* Entire transaction goes into this subdev */ + size = len; + + if (subdev->read_oob) + err = subdev->read_oob(subdev, from, size, + &retsize, buf); + else + err = -EINVAL; + + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } + return err; +} + +static int +concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + *retlen = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + size = 0; + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else if (subdev->write_oob) + err = subdev->write_oob(subdev, to, size, &retsize, + buf); + else + err = -EINVAL; + + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + +static void concat_erase_callback(struct erase_info *instr) +{ + wake_up((wait_queue_head_t *) instr->priv); } static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) @@ -160,18 +365,18 @@ erase->mtd = mtd; erase->callback = concat_erase_callback; - erase->priv = (unsigned long)&waitq; + erase->priv = (unsigned long) &waitq; /* * FIXME: Allow INTERRUPTIBLE. Which means * not having the wait_queue head on the stack. */ err = mtd->erase(mtd, erase); - if (!err) - { + if (!err) { set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&waitq, &wait); - if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) + if (erase->state != MTD_ERASE_DONE + && erase->state != MTD_ERASE_FAILED) schedule(); remove_wait_queue(&waitq, &wait); set_current_state(TASK_RUNNING); @@ -181,21 +386,21 @@ return err; } -static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) +static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mtd_concat *concat = CONCAT(mtd); struct mtd_info *subdev; int i, err; - u_int32_t length; + u_int32_t length, offset = 0; struct erase_info *erase; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if(instr->addr > concat->mtd.size) + if (instr->addr > concat->mtd.size) return -EINVAL; - if(instr->len + instr->addr > concat->mtd.size) + if (instr->len + instr->addr > concat->mtd.size) return -EINVAL; /* @@ -204,23 +409,22 @@ * region info rather than looking at each particular sub-device * in turn. */ - if (!concat->mtd.numeraseregions) - { /* the easy case: device has uniform erase block size */ - if(instr->addr & (concat->mtd.erasesize - 1)) + if (!concat->mtd.numeraseregions) { + /* the easy case: device has uniform erase block size */ + if (instr->addr & (concat->mtd.erasesize - 1)) return -EINVAL; - if(instr->len & (concat->mtd.erasesize - 1)) + if (instr->len & (concat->mtd.erasesize - 1)) return -EINVAL; - } - else - { /* device has variable erase size */ - struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; + } else { + /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = + concat->mtd.eraseregions; /* * Find the erase region where the to-be-erased area begins: */ - for(i = 0; i < concat->mtd.numeraseregions && - instr->addr >= erase_regions[i].offset; i++) - ; + for (i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) ; --i; /* @@ -228,25 +432,28 @@ * to-be-erased area begins. Verify that the starting * offset is aligned to this region's erase size: */ - if (instr->addr & (erase_regions[i].erasesize-1)) + if (instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL; /* * now find the erase region where the to-be-erased area ends: */ - for(; i < concat->mtd.numeraseregions && - (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) - ; + for (; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset; + ++i) ; --i; /* * check if the ending offset is aligned to this region's erase size */ - if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) + if ((instr->addr + instr->len) & (erase_regions[i].erasesize - + 1)) return -EINVAL; } + instr->fail_addr = 0xffffffff; + /* make a local copy of instr to avoid modifying the caller's struct */ - erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); + erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); if (!erase) return -ENOMEM; @@ -258,39 +465,44 @@ * find the subdevice where the to-be-erased area begins, adjust * starting offset to be relative to the subdevice start */ - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { subdev = concat->subdev[i]; - if(subdev->size <= erase->addr) + if (subdev->size <= erase->addr) { erase->addr -= subdev->size; - else + offset += subdev->size; + } else { break; } - if(i >= concat->num_subdev) /* must never happen since size */ - BUG(); /* limit has been verified above */ + } + + /* must never happen since size limit has been verified above */ + if (i >= concat->num_subdev) + BUG(); /* now do the erase: */ err = 0; - for(;length > 0; i++) /* loop for all subevices affected by this request */ - { + for (; length > 0; i++) { + /* loop for all subdevices affected by this request */ subdev = concat->subdev[i]; /* get current subdevice */ /* limit length to subdevice's size: */ - if(erase->addr + length > subdev->size) + if (erase->addr + length > subdev->size) erase->len = subdev->size - erase->addr; else erase->len = length; - if (!(subdev->flags & MTD_WRITEABLE)) - { + if (!(subdev->flags & MTD_WRITEABLE)) { err = -EROFS; break; } length -= erase->len; - if ((err = concat_dev_erase(subdev, erase))) - { - if(err == -EINVAL) /* sanity check: must never happen since */ - BUG(); /* block alignment has been checked above */ + if ((err = concat_dev_erase(subdev, erase))) { + /* sanity check: should never happen since + * block alignment has been checked above */ + if (err == -EINVAL) + BUG(); + if (erase->fail_addr != 0xffffffff) + instr->fail_addr = erase->fail_addr + offset; break; } /* @@ -302,18 +514,19 @@ * current subdevice, i.e. at offset zero. */ erase->addr = 0; + offset += subdev->size; } + instr->state = erase->state; kfree(erase); if (err) return err; - instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); return 0; } -static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; @@ -321,18 +534,15 @@ if ((len + ofs) > mtd->size) return -EINVAL; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size; - if (ofs >= subdev->size) - { + if (ofs >= subdev->size) { size = 0; ofs -= subdev->size; + continue; } - else - { if (ofs + len > subdev->size) size = subdev->size - ofs; else @@ -340,21 +550,21 @@ err = subdev->lock(subdev, ofs, size); - if(err) + if (err) break; len -= size; - if(len == 0) + if (len == 0) break; err = -EINVAL; ofs = 0; } - } + return err; } -static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = 0; @@ -362,18 +572,15 @@ if ((len + ofs) > mtd->size) return -EINVAL; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; size_t size; - if (ofs >= subdev->size) - { + if (ofs >= subdev->size) { size = 0; ofs -= subdev->size; + continue; } - else - { if (ofs + len > subdev->size) size = subdev->size - ofs; else @@ -381,17 +588,17 @@ err = subdev->unlock(subdev, ofs, size); - if(err) + if (err) break; len -= size; - if(len == 0) + if (len == 0) break; err = -EINVAL; ofs = 0; } - } + return err; } @@ -400,8 +607,7 @@ struct mtd_concat *concat = CONCAT(mtd); int i; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; subdev->sync(subdev); } @@ -412,10 +618,9 @@ struct mtd_concat *concat = CONCAT(mtd); int i, rc = 0; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - if((rc = subdev->suspend(subdev)) < 0) + if ((rc = subdev->suspend(subdev)) < 0) return rc; } return rc; @@ -426,8 +631,7 @@ struct mtd_concat *concat = CONCAT(mtd); int i; - for(i = 0; i < concat->num_subdev; i++) - { + for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; subdev->resume(subdev); } @@ -439,11 +643,10 @@ * stored to *new_dev upon success. This function does _not_ * register any devices: this is the caller's responsibility. */ -struct mtd_info *mtd_concat_create( - struct mtd_info *subdev[], /* subdevices to concatenate */ +struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ - char *name) /* name for the new device */ -{ + char *name) +{ /* name for the new device */ int i; size_t size; struct mtd_concat *concat; @@ -451,21 +654,21 @@ int num_erase_region; printk(KERN_NOTICE "Concatenating MTD devices:\n"); - for(i = 0; i < num_devs; i++) + for (i = 0; i < num_devs; i++) printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); printk(KERN_NOTICE "into device \"%s\"\n", name); /* allocate the device structure */ size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); - concat = kmalloc (size, GFP_KERNEL); - if(!concat) - { - printk ("memory allocation error while creating concatenated device \"%s\"\n", + concat = kmalloc(size, GFP_KERNEL); + if (!concat) { + printk + ("memory allocation error while creating concatenated device \"%s\"\n", name); return NULL; } memset(concat, 0, size); - concat->subdev = (struct mtd_info **)(concat + 1); + concat->subdev = (struct mtd_info **) (concat + 1); /* * Set up the new "super" device's MTD object structure, check for @@ -479,39 +682,53 @@ concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.ecctype = subdev[0]->ecctype; concat->mtd.eccsize = subdev[0]->eccsize; + if (subdev[0]->read_ecc) + concat->mtd.read_ecc = concat_read_ecc; + if (subdev[0]->write_ecc) + concat->mtd.write_ecc = concat_write_ecc; + if (subdev[0]->read_oob) + concat->mtd.read_oob = concat_read_oob; + if (subdev[0]->write_oob) + concat->mtd.write_oob = concat_write_oob; concat->subdev[0] = subdev[0]; - for(i = 1; i < num_devs; i++) - { - if(concat->mtd.type != subdev[i]->type) - { + for (i = 1; i < num_devs; i++) { + if (concat->mtd.type != subdev[i]->type) { kfree(concat); - printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); + printk("Incompatible device type on \"%s\"\n", + subdev[i]->name); return NULL; } - if(concat->mtd.flags != subdev[i]->flags) - { /* - * Expect all flags except MTD_WRITEABLE to be equal on - * all subdevices. + if (concat->mtd.flags != subdev[i]->flags) { + /* + * Expect all flags except MTD_WRITEABLE to be + * equal on all subdevices. */ - if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) - { + if ((concat->mtd.flags ^ subdev[i]-> + flags) & ~MTD_WRITEABLE) { kfree(concat); - printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); + printk("Incompatible device flags on \"%s\"\n", + subdev[i]->name); return NULL; - } - else /* if writeable attribute differs, make super device writeable */ - concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; + } else + /* if writeable attribute differs, + make super device writeable */ + concat->mtd.flags |= + subdev[i]->flags & MTD_WRITEABLE; } concat->mtd.size += subdev[i]->size; - if(concat->mtd.oobblock != subdev[i]->oobblock || + if (concat->mtd.oobblock != subdev[i]->oobblock || concat->mtd.oobsize != subdev[i]->oobsize || concat->mtd.ecctype != subdev[i]->ecctype || - concat->mtd.eccsize != subdev[i]->eccsize) - { + concat->mtd.eccsize != subdev[i]->eccsize || + !concat->mtd.read_ecc != !subdev[i]->read_ecc || + !concat->mtd.write_ecc != !subdev[i]->write_ecc || + !concat->mtd.read_oob != !subdev[i]->read_oob || + !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); - printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); + printk("Incompatible OOB or ECC data on \"%s\"\n", + subdev[i]->name); return NULL; } concat->subdev[i] = subdev[i]; @@ -535,7 +752,6 @@ concat->mtd.suspend = concat_suspend; concat->mtd.resume = concat_resume; - /* * Combine the erase block size info of the subdevices: * @@ -544,44 +760,44 @@ */ max_erasesize = curr_erasesize = subdev[0]->erasesize; num_erase_region = 1; - for(i = 0; i < num_devs; i++) - { - if(subdev[i]->numeraseregions == 0) - { /* current subdevice has uniform erase size */ - if(subdev[i]->erasesize != curr_erasesize) - { /* if it differs from the last subdevice's erase size, count it */ + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* if it differs from the last subdevice's erase size, count it */ ++num_erase_region; curr_erasesize = subdev[i]->erasesize; - if(curr_erasesize > max_erasesize) + if (curr_erasesize > max_erasesize) max_erasesize = curr_erasesize; } - } - else - { /* current subdevice has variable erase size */ + } else { + /* current subdevice has variable erase size */ int j; - for(j = 0; j < subdev[i]->numeraseregions; j++) - { /* walk the list of erase regions, count any changes */ - if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) - { + for (j = 0; j < subdev[i]->numeraseregions; j++) { + + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j].erasesize != + curr_erasesize) { ++num_erase_region; - curr_erasesize = subdev[i]->eraseregions[j].erasesize; - if(curr_erasesize > max_erasesize) + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; + if (curr_erasesize > max_erasesize) max_erasesize = curr_erasesize; } } } } - if(num_erase_region == 1) - { /* + if (num_erase_region == 1) { + /* * All subdevices have the same uniform erase size. * This is easy: */ concat->mtd.erasesize = curr_erasesize; concat->mtd.numeraseregions = 0; - } - else - { /* + } else { + /* * erase block size varies across the subdevices: allocate * space to store the data describing the variable erase regions */ @@ -590,12 +806,13 @@ concat->mtd.erasesize = max_erasesize; concat->mtd.numeraseregions = num_erase_region; - concat->mtd.eraseregions = erase_region_p = kmalloc ( - num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); - if(!erase_region_p) - { + concat->mtd.eraseregions = erase_region_p = + kmalloc(num_erase_region * + sizeof (struct mtd_erase_region_info), GFP_KERNEL); + if (!erase_region_p) { kfree(concat); - printk ("memory allocation error while creating erase region list" + printk + ("memory allocation error while creating erase region list" " for device \"%s\"\n", name); return NULL; } @@ -606,41 +823,48 @@ */ curr_erasesize = subdev[0]->erasesize; begin = position = 0; - for(i = 0; i < num_devs; i++) - { - if(subdev[i]->numeraseregions == 0) - { /* current subdevice has uniform erase size */ - if(subdev[i]->erasesize != curr_erasesize) - { /* + for (i = 0; i < num_devs; i++) { + if (subdev[i]->numeraseregions == 0) { + /* current subdevice has uniform erase size */ + if (subdev[i]->erasesize != curr_erasesize) { + /* * fill in an mtd_erase_region_info structure for the area * we have walked so far: */ erase_region_p->offset = begin; - erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = (position - begin) / curr_erasesize; + erase_region_p->erasesize = + curr_erasesize; + erase_region_p->numblocks = + (position - begin) / curr_erasesize; begin = position; curr_erasesize = subdev[i]->erasesize; ++erase_region_p; } position += subdev[i]->size; - } - else - { /* current subdevice has variable erase size */ + } else { + /* current subdevice has variable erase size */ int j; - for(j = 0; j < subdev[i]->numeraseregions; j++) - { /* walk the list of erase regions, count any changes */ - if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) - { + for (j = 0; j < subdev[i]->numeraseregions; j++) { + /* walk the list of erase regions, count any changes */ + if (subdev[i]->eraseregions[j]. + erasesize != curr_erasesize) { erase_region_p->offset = begin; - erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = (position - begin) / curr_erasesize; + erase_region_p->erasesize = + curr_erasesize; + erase_region_p->numblocks = + (position - + begin) / curr_erasesize; begin = position; - curr_erasesize = subdev[i]->eraseregions[j].erasesize; + curr_erasesize = + subdev[i]->eraseregions[j]. + erasesize; ++erase_region_p; } - position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; + position += + subdev[i]->eraseregions[j]. + numblocks * curr_erasesize; } } } @@ -660,16 +884,14 @@ void mtd_concat_destroy(struct mtd_info *mtd) { struct mtd_concat *concat = CONCAT(mtd); - if(concat->mtd.numeraseregions) + if (concat->mtd.numeraseregions) kfree(concat->mtd.eraseregions); kfree(concat); } - EXPORT_SYMBOL(mtd_concat_create); EXPORT_SYMBOL(mtd_concat_destroy); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Robert Kaiser "); MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); --- linux-2.4.21/drivers/mtd/mtdcore.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdcore.c @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.45 2005/02/18 14:34:50 dedekind Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -17,6 +17,7 @@ #include #include #include +#include #include #ifdef CONFIG_PROC_FS #include @@ -24,9 +25,15 @@ #include -static DECLARE_MUTEX(mtd_table_mutex); -static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; -static struct mtd_notifier *mtd_notifiers = NULL; +/* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ +DECLARE_MUTEX(mtd_table_mutex); +struct mtd_info *mtd_table[MAX_MTD_DEVICES]; + +EXPORT_SYMBOL_GPL(mtd_table_mutex); +EXPORT_SYMBOL_GPL(mtd_table); + +static LIST_HEAD(mtd_notifiers); /** * add_mtd_device - register an MTD device @@ -44,21 +51,28 @@ down(&mtd_table_mutex); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) - { - struct mtd_notifier *not=mtd_notifiers; + for (i=0; i < MAX_MTD_DEVICES; i++) + if (!mtd_table[i]) { + struct list_head *this; mtd_table[i] = mtd; mtd->index = i; + mtd->usecount = 0; + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); - while (not) - { - (*(not->add))(mtd); - not = not->next; + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each(this, &mtd_notifiers) { + struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); + not->add(mtd); } + up(&mtd_table_mutex); - MOD_INC_USE_COUNT; + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); return 0; } @@ -78,29 +92,34 @@ int del_mtd_device (struct mtd_info *mtd) { - struct mtd_notifier *not=mtd_notifiers; - int i; + int ret; down(&mtd_table_mutex); - for (i=0; i < MAX_MTD_DEVICES; i++) - { - if (mtd_table[i] == mtd) - { - while (not) - { - (*(not->remove))(mtd); - not = not->next; - } - mtd_table[i] = NULL; - up (&mtd_table_mutex); - MOD_DEC_USE_COUNT; - return 0; + if (mtd_table[mtd->index] != mtd) { + ret = -ENODEV; + } else if (mtd->usecount) { + printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", + mtd->index, mtd->name, mtd->usecount); + ret = -EBUSY; + } else { + struct list_head *this; + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each(this, &mtd_notifiers) { + struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); + not->remove(mtd); } + + mtd_table[mtd->index] = NULL; + + module_put(THIS_MODULE); + ret = 0; } up(&mtd_table_mutex); - return 1; + return ret; } /** @@ -118,10 +137,9 @@ down(&mtd_table_mutex); - new->next = mtd_notifiers; - mtd_notifiers = new; + list_add(&new->list, &mtd_notifiers); - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); for (i=0; i< MAX_MTD_DEVICES; i++) if (mtd_table[i]) @@ -131,8 +149,8 @@ } /** - * register_mtd_user - unregister a 'user' of MTD devices. - * @new: pointer to notifier info structure + * unregister_mtd_user - unregister a 'user' of MTD devices. + * @old: pointer to notifier info structure * * Removes a callback function pair from the list of 'users' to be * notified upon addition or removal of MTD devices. Causes the @@ -142,34 +160,24 @@ int unregister_mtd_user (struct mtd_notifier *old) { - struct mtd_notifier **prev = &mtd_notifiers; - struct mtd_notifier *cur; int i; down(&mtd_table_mutex); - while ((cur = *prev)) { - if (cur == old) { - *prev = cur->next; - - MOD_DEC_USE_COUNT; + module_put(THIS_MODULE); for (i=0; i< MAX_MTD_DEVICES; i++) if (mtd_table[i]) old->remove(mtd_table[i]); + list_del(&old->list); up(&mtd_table_mutex); return 0; - } - prev = &cur->next; - } - up(&mtd_table_mutex); - return 1; } /** - * __get_mtd_device - obtain a validated handle for an MTD device + * get_mtd_device - obtain a validated handle for an MTD device * @mtd: last known address of the required MTD device * @num: internal device number of the required MTD device * @@ -177,11 +185,10 @@ * table, if any. Given an address and num == -1, search the device table * for a device with that address and return if it's still present. Given * both, return the num'th driver only if its address matches. Return NULL - * if not. get_mtd_device() increases the use count, but - * __get_mtd_device() doesn't - you should generally use get_mtd_device(). + * if not. */ -struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) +struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { struct mtd_info *ret = NULL; int i; @@ -198,16 +205,33 @@ ret = NULL; } + if (ret && !try_module_get(ret->owner)) + ret = NULL; + + if (ret) + ret->usecount++; + up(&mtd_table_mutex); return ret; } +void put_mtd_device(struct mtd_info *mtd) +{ + int c; + + down(&mtd_table_mutex); + c = --mtd->usecount; + up(&mtd_table_mutex); + BUG_ON(c < 0); + + module_put(mtd->owner); +} /* default_mtd_writev - default mtd writev method for MTD devices that * dont implement their own */ -int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, +int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { unsigned long i; @@ -237,7 +261,7 @@ * implement their own */ -int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs, +int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen) { unsigned long i; @@ -265,7 +289,8 @@ EXPORT_SYMBOL(add_mtd_device); EXPORT_SYMBOL(del_mtd_device); -EXPORT_SYMBOL(__get_mtd_device); +EXPORT_SYMBOL(get_mtd_device); +EXPORT_SYMBOL(put_mtd_device); EXPORT_SYMBOL(register_mtd_user); EXPORT_SYMBOL(unregister_mtd_user); EXPORT_SYMBOL(default_mtd_writev); @@ -308,10 +333,7 @@ /* Support for /proc/mtd */ #ifdef CONFIG_PROC_FS - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static struct proc_dir_entry *proc_mtd; -#endif static inline int mtd_proc_info (char *buf, int i) { @@ -324,13 +346,8 @@ this->erasesize, this->name); } -static int mtd_read_proc ( char *page, char **start, off_t off,int count -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - ,int *eof, void *data_unused -#else - ,int unused -#endif - ) +static int mtd_read_proc (char *page, char **start, off_t off, int count, + int *eof, void *data_unused) { int len, l, i; off_t begin = 0; @@ -350,9 +367,7 @@ } } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) *eof = 1; -#endif done: up(&mtd_table_mutex); @@ -362,36 +377,16 @@ return ((count < begin+len-off) ? count : begin+len-off); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) -struct proc_dir_entry mtd_proc_entry = { - 0, /* low_ino: the inode -- dynamic */ - 3, "mtd", /* len of name and name */ - S_IFREG | S_IRUGO, /* mode */ - 1, 0, 0, /* nlinks, owner, group */ - 0, NULL, /* size - unused; operations -- use default */ - &mtd_read_proc, /* function used to read data */ - /* nothing more */ - }; -#endif - #endif /* CONFIG_PROC_FS */ /*====================================================================*/ /* Init code */ -int __init init_mtd(void) +static int __init init_mtd(void) { #ifdef CONFIG_PROC_FS -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - if ((proc_mtd = create_proc_entry( "mtd", 0, 0 ))) + if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) proc_mtd->read_proc = mtd_read_proc; -#else - proc_register_dynamic(&proc_root,&mtd_proc_entry); -#endif -#endif - -#if LINUX_VERSION_CODE < 0x20212 - init_mtd_devices(); #endif #ifdef CONFIG_PM @@ -410,12 +405,8 @@ #endif #ifdef CONFIG_PROC_FS -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if (proc_mtd) - remove_proc_entry( "mtd", 0); -#else - proc_unregister(&proc_root,mtd_proc_entry.low_ino); -#endif + remove_proc_entry( "mtd", NULL); #endif } --- linux-2.4.21/drivers/mtd/mtdpart.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/mtdpart.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.32 2002/10/21 13:40:05 jocke Exp $ + * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $ * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob @@ -16,10 +16,11 @@ #include #include #include - +#include +#include #include #include - +#include /* Our partition linked list */ static LIST_HEAD(mtd_partitions); @@ -54,8 +55,12 @@ len = 0; else if (from + len > mtd->size) len = mtd->size - from; + if (part->master->read_ecc == NULL) return part->master->read (part->master, from + part->offset, len, retlen, buf); + else + return part->master->read_ecc (part->master, from + part->offset, + len, retlen, buf, NULL, &mtd->oobinfo); } static int part_point (struct mtd_info *mtd, loff_t from, size_t len, @@ -78,9 +83,11 @@ static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) + size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) { struct mtd_part *part = PART(mtd); + if (oobsel == NULL) + oobsel = &mtd->oobinfo; if (from >= mtd->size) len = 0; else if (from + len > mtd->size) @@ -109,14 +116,28 @@ len, retlen, buf); } +static int part_get_user_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_user_prot_info (part->master, buf, len); +} + static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return part->master->read_user_prot_reg (part->master, from, + return part->master->read_fact_prot_reg (part->master, from, len, retlen, buf); } +static int part_get_fact_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_fact_prot_info (part->master, buf, len); +} + static int part_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -127,17 +148,24 @@ len = 0; else if (to + len > mtd->size) len = mtd->size - to; + if (part->master->write_ecc == NULL) return part->master->write (part->master, to + part->offset, len, retlen, buf); + else + return part->master->write_ecc (part->master, to + part->offset, + len, retlen, buf, NULL, &mtd->oobinfo); + } static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, - u_char *eccbuf, int oobsel) + u_char *eccbuf, struct nand_oobinfo *oobsel) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + if (oobsel == NULL) + oobsel = &mtd->oobinfo; if (to >= mtd->size) len = 0; else if (to + len > mtd->size) @@ -168,41 +196,61 @@ len, retlen, buf); } -static int part_writev (struct mtd_info *mtd, const struct iovec *vecs, +static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->lock_user_prot_reg (part->master, from, len); +} + +static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + if (part->master->writev_ecc == NULL) return part->master->writev (part->master, vecs, count, to + part->offset, retlen); + else + return part->master->writev_ecc (part->master, vecs, count, + to + part->offset, retlen, + NULL, &mtd->oobinfo); } -static int part_readv (struct mtd_info *mtd, struct iovec *vecs, +static int part_readv (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen) { struct mtd_part *part = PART(mtd); + if (part->master->readv_ecc == NULL) return part->master->readv (part->master, vecs, count, from + part->offset, retlen); + else + return part->master->readv_ecc (part->master, vecs, count, + from + part->offset, retlen, + NULL, &mtd->oobinfo); } -static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, +static int part_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, - u_char *eccbuf, int oobsel) + u_char *eccbuf, struct nand_oobinfo *oobsel) { struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + if (oobsel == NULL) + oobsel = &mtd->oobinfo; return part->master->writev_ecc (part->master, vecs, count, to + part->offset, retlen, eccbuf, oobsel); } -static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs, +static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen, - u_char *eccbuf, int oobsel) + u_char *eccbuf, struct nand_oobinfo *oobsel) { struct mtd_part *part = PART(mtd); + if (oobsel == NULL) + oobsel = &mtd->oobinfo; return part->master->readv_ecc (part->master, vecs, count, from + part->offset, retlen, eccbuf, oobsel); @@ -211,13 +259,29 @@ static int part_erase (struct mtd_info *mtd, struct erase_info *instr) { struct mtd_part *part = PART(mtd); + int ret; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (instr->addr >= mtd->size) return -EINVAL; instr->addr += part->offset; - return part->master->erase(part->master, instr); + ret = part->master->erase(part->master, instr); + return ret; +} + +void mtd_erase_callback(struct erase_info *instr) +{ + if (instr->mtd->erase == part_erase) { + struct mtd_part *part = PART(instr->mtd); + + if (instr->fail_addr != 0xffffffff) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + if (instr->callback) + instr->callback(instr); } +EXPORT_SYMBOL_GPL(mtd_erase_callback); static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) { @@ -253,6 +317,26 @@ part->master->resume(part->master); } +static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + return part->master->block_isbad(part->master, ofs); +} + +static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + return part->master->block_markbad(part->master, ofs); +} + /* * This function unregisters and destroy all slave MTD objects which are * attached to the given master MTD object. @@ -288,7 +372,7 @@ */ int add_mtd_partitions(struct mtd_info *master, - struct mtd_partition *parts, + const struct mtd_partition *parts, int nbparts) { struct mtd_part *slave; @@ -321,7 +405,7 @@ slave->mtd.name = parts[i].name; slave->mtd.bank_size = master->bank_size; - slave->mtd.module = master->module; + slave->mtd.owner = master->owner; slave->mtd.read = part_read; slave->mtd.write = part_write; @@ -345,6 +429,12 @@ slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if(master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if(master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if(master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if(master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { @@ -363,6 +453,10 @@ slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; + if (master->block_isbad) + slave->mtd.block_isbad = part_block_isbad; + if (master->block_markbad) + slave->mtd.block_markbad = part_block_markbad; slave->mtd.erase = part_erase; slave->master = master; slave->offset = parts[i].offset; @@ -433,6 +527,9 @@ parts[i].name); } + /* copy oobinfo from master */ + memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo)); + if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ *parts[i].mtdp = &slave->mtd; @@ -452,6 +549,75 @@ EXPORT_SYMBOL(add_mtd_partitions); EXPORT_SYMBOL(del_mtd_partitions); +static DEFINE_SPINLOCK(part_parser_lock); +static LIST_HEAD(part_parsers); + +static struct mtd_part_parser *get_partition_parser(const char *name) +{ + struct list_head *this; + void *ret = NULL; + spin_lock(&part_parser_lock); + + list_for_each(this, &part_parsers) { + struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); + + if (!strcmp(p->name, name) && try_module_get(p->owner)) { + ret = p; + break; + } + } + spin_unlock(&part_parser_lock); + + return ret; +} + +int register_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_add(&p->list, &part_parsers); + spin_unlock(&part_parser_lock); + + return 0; +} + +int deregister_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_del(&p->list); + spin_unlock(&part_parser_lock); + return 0; +} + +int parse_mtd_partitions(struct mtd_info *master, const char **types, + struct mtd_partition **pparts, unsigned long origin) +{ + struct mtd_part_parser *parser; + int ret = 0; + + for ( ; ret <= 0 && *types; types++) { + parser = get_partition_parser(*types); +#ifdef CONFIG_KMOD + if (!parser && !request_module("%s", *types)) + parser = get_partition_parser(*types); +#endif + if (!parser) { + printk(KERN_NOTICE "%s partition parsing not available\n", + *types); + continue; + } + ret = (*parser->parse_fn)(master, pparts, origin); + if (ret > 0) { + printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", + ret, parser->name, master->name); + } + put_partition_parser(parser); + } + return ret; +} + +EXPORT_SYMBOL_GPL(parse_mtd_partitions); +EXPORT_SYMBOL_GPL(register_mtd_parser); +EXPORT_SYMBOL_GPL(deregister_mtd_parser); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nicolas Pitre "); --- linux-2.4.21/drivers/mtd/nand/Config.in~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/Config.in @@ -1,6 +1,6 @@ # drivers/mtd/nand/Config.in -# $Id: Config.in,v 1.11 2002/12/01 13:23:05 gleixner Exp $ +# $Id: Config.in,v 1.24 2005/01/17 18:25:19 dmarlin Exp $ mainmenu_option next_comment @@ -11,26 +11,56 @@ bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE fi -if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then - dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND +if [ "$CONFIG_ARM" = "y" ]; then + dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T + dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP + dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12 + dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312 fi -if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then - dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND +if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then + define_bool CONFIG_MTD_NAND_IDS y +else + if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then + define_bool CONFIG_MTD_NAND_IDS m + fi fi -if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then - dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND +if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then + dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND fi -if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then - define_bool CONFIG_MTD_NAND_IDS y +if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then + dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND fi -if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then -if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then - define_bool CONFIG_MTD_NAND_IDS m +if [ "$CONFIG_PPCHAMELEONEVB" = "y" ]; then + dep_tristate ' NAND Flash device on PPChameleonEVB board' CONFIG_MTD_NAND_PPCHAMELEONEVB $CONFIG_MTD_NAND +fi + +if [ "$CONFIG_SOC_AU1550" = "y" ]; then + dep_tristate ' NAND Flash Driver for Au1550 controller' CONFIG_MTD_NAND_AU1550 $CONFIG_MTD_NAND fi + +if [ "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then + dep_tristate ' Renesas Flash ROM 4-slot interface board (FROM_BOARD4)' CONFIG_MTD_NAND_RTC_FROM4 $CONFIG_MTD_NAND + if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "y" ]; then + define_bool CONFIG_REED_SOLOMON y + define_bool CONFIG_REED_SOLOMON_DEC8 y + else + if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "m" ]; then + define_bool CONFIG_REED_SOLOMON m + define_bool CONFIG_REED_SOLOMON_DEC8 m + fi + fi +fi + +dep_tristate ' DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)' CONFIG_MTD_NAND_DISKONCHIP $CONFIG_MTD_NAND $CONFIG_EXPERIMENTAL +if [ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then + bool ' Advanced detection options for DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED + hex ' Physical address of DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS + bool ' Probe high addresses' CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH $CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED + bool ' Allow BBT write on DiskOnChip Millennium and 2000TSOP' CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE fi endmenu --- linux-2.4.21/drivers/mtd/nand/Makefile~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/Makefile @@ -1,16 +1,16 @@ # -# linux/drivers/nand/Makefile +# linux/drivers/nand/Makefile.24 +# Makefile for obsolete kernels. # -# $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $ +# $Id: Makefile.24,v 1.1 2004/07/12 16:08:17 dwmw2 Exp $ O_TARGET := nandlink.o +export-objs := nand_base.o nand_bbt.o nand_ecc.o nand_ids.o +list-multi := nand.o -export-objs := nand.o nand_ecc.o nand_ids.o - -obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o -obj-$(CONFIG_MTD_NAND_SPIA) += spia.o -obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o -obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o -obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +include Makefile.common include $(TOPDIR)/Rules.make + +nand.o: $(nand-objs) + $(LD) -r -o $@ $(nand-objs) --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/Makefile.common @@ -0,0 +1,24 @@ +# +# linux/drivers/nand/Makefile +# +# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ + +obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o + +obj-$(CONFIG_MTD_NAND_SPIA) += spia.o +obj-$(CONFIG_MTD_NAND_TOTO) += toto.o +obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o +obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o +obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o +obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o +obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o +obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o +obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_H1900) += h1910.o +obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o +obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o + +nand-objs = nand_base.o nand_bbt.o --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/au1550nd.c @@ -0,0 +1,477 @@ +/* + * drivers/mtd/nand/au1550nd.c + * + * Copyright (C) 2004 Embedded Edge, LLC + * + * $Id: au1550nd.c,v 1.11 2004/11/04 12:53:10 gleixner 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* fixme: this is ugly */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0) +#include +#ifdef CONFIG_MIPS_PB1550 +#include +#endif +#ifdef CONFIG_MIPS_DB1550 +#include +#endif +#else +#include +#ifdef CONFIG_MIPS_PB1550 +#include +#endif +#ifdef CONFIG_MIPS_DB1550 +#include +#endif +#endif + +/* + * MTD structure for NAND controller + */ +static struct mtd_info *au1550_mtd = NULL; +static void __iomem *p_nand; +static int nand_width = 1; /* default x8*/ + +#define NAND_CS 1 + +/* + * Define partitions for flash device + */ +const static struct mtd_partition partition_info[] = { +#ifdef CONFIG_MIPS_PB1550 +#define NUM_PARTITIONS 2 + { + .name = "Pb1550 NAND FS 0", + .offset = 0, + .size = 8*1024*1024 + }, + { + .name = "Pb1550 NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +#endif +#ifdef CONFIG_MIPS_DB1550 +#define NUM_PARTITIONS 2 + { + .name = "Db1550 NAND FS 0", + .offset = 0, + .size = 8*1024*1024 + }, + { + .name = "Db1550 NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +#endif +}; + + +/** + * au_read_byte - read one byte from the chip + * @mtd: MTD device structure + * + * read function for 8bit buswith + */ +static u_char au_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u_char ret = readb(this->IO_ADDR_R); + au_sync(); + return ret; +} + +/** + * au_write_byte - write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 8it buswith + */ +static void au_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); + au_sync(); +} + +/** + * au_read_byte16 - read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith with + * endianess conversion + */ +static u_char au_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); + au_sync(); + return ret; +} + +/** + * au_write_byte16 - write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 16bit buswith with + * endianess conversion + */ +static void au_write_byte16(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); + au_sync(); +} + +/** + * au_read_word - read one word from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith without + * endianess conversion + */ +static u16 au_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u16 ret = readw(this->IO_ADDR_R); + au_sync(); + return ret; +} + +/** + * au_write_word - write one word to the chip + * @mtd: MTD device structure + * @word: data word to write + * + * write function for 16bit buswith without + * endianess conversion + */ +static void au_write_word(struct mtd_info *mtd, u16 word) +{ + struct nand_chip *this = mtd->priv; + writew(word, this->IO_ADDR_W); + au_sync(); +} + +/** + * au_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 8bit buswith + */ +static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); + au_sync(); + } +} + +/** + * au_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 8bit buswith + */ +static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); + au_sync(); + } +} + +/** + * au_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 8bit buswith + */ +static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + au_sync(); + } + + return 0; +} + +/** + * au_write_buf16 - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 16bit buswith + */ +static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_W); + au_sync(); + } + +} + +/** + * au_read_buf16 - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 16bit buswith + */ +static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R); + au_sync(); + } +} + +/** + * au_verify_buf16 - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 16bit buswith + */ +static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + au_sync(); + } + return 0; +} + + +static void au1550_hwcontrol(struct mtd_info *mtd, int cmd) +{ + register struct nand_chip *this = mtd->priv; + + switch(cmd){ + + case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break; + case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break; + + case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break; + case NAND_CTL_CLRALE: + this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; + /* FIXME: Nobody knows why this is neccecary, + * but it works only that way */ + udelay(1); + break; + + case NAND_CTL_SETNCE: + /* assert (force assert) chip enable */ + au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break; + break; + + case NAND_CTL_CLRNCE: + /* deassert chip enable */ + au_writel(0, MEM_STNDCTL); break; + break; + } + + this->IO_ADDR_R = this->IO_ADDR_W; + + /* Drain the writebuffer */ + au_sync(); +} + +int au1550_device_ready(struct mtd_info *mtd) +{ + int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; + au_sync(); + return ret; +} + +/* + * Main initialization routine + */ +int __init au1550_init (void) +{ + struct nand_chip *this; + u16 boot_swapboot = 0; /* default value */ + int retval; + + /* Allocate memory for MTD device structure and private data */ + au1550_mtd = kmalloc (sizeof(struct mtd_info) + + sizeof (struct nand_chip), GFP_KERNEL); + if (!au1550_mtd) { + printk ("Unable to allocate NAND MTD dev structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&au1550_mtd[1]); + + /* Initialize structures */ + memset((char *) au1550_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + au1550_mtd->priv = this; + + + /* MEM_STNDCTL: disable ints, disable nand boot */ + au_writel(0, MEM_STNDCTL); + +#ifdef CONFIG_MIPS_PB1550 + /* set gpio206 high */ + au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR); + + boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | + ((bcsr->status >> 6) & 0x1); + switch (boot_swapboot) { + case 0: + case 2: + case 8: + case 0xC: + case 0xD: + /* x16 NAND Flash */ + nand_width = 0; + break; + case 1: + case 9: + case 3: + case 0xE: + case 0xF: + /* x8 NAND Flash */ + nand_width = 1; + break; + default: + printk("Pb1550 NAND: bad boot:swap\n"); + retval = -EINVAL; + goto outmem; + } +#endif + + /* Configure RCE1 - should be done by YAMON */ + au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */ + au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */ + au_sync(); + + /* setup and enable chip select, MEM_STADDR1 */ + /* we really need to decode offsets only up till 0x20 */ + au_writel((1<<28) | (NAND_PHYS_ADDR>>4) | + (((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18), + MEM_STADDR1); + au_sync(); + + p_nand = ioremap(NAND_PHYS_ADDR, 0x1000); + + /* Set address of hardware control function */ + this->hwcontrol = au1550_hwcontrol; + this->dev_ready = au1550_device_ready; + /* 30 us command delay time */ + this->chip_delay = 30; + this->eccmode = NAND_ECC_SOFT; + + this->options = NAND_NO_AUTOINCR; + + if (!nand_width) + this->options |= NAND_BUSWIDTH_16; + + this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; + this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; + this->write_word = au_write_word; + this->read_word = au_read_word; + this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; + this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; + this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; + + /* Scan to find existence of the device */ + if (nand_scan (au1550_mtd, 1)) { + retval = -ENXIO; + goto outio; + } + + /* Register the partitions */ + add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS); + + return 0; + + outio: + iounmap ((void *)p_nand); + + outmem: + kfree (au1550_mtd); + return retval; +} + +module_init(au1550_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit au1550_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1]; + + /* Release resources, unregister device */ + nand_release (au1550_mtd); + + /* Free the MTD device structure */ + kfree (au1550_mtd); + + /* Unmap */ + iounmap ((void *)p_nand); +} +module_exit(au1550_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Embedded Edge, LLC"); +MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board"); --- linux-2.4.21/drivers/mtd/nand/autcpu12.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/autcpu12.c @@ -4,9 +4,9 @@ * Copyright (c) 2002 Thomas Gleixner * * Derived from drivers/mtd/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.6 2002/11/11 15:47:56 gleixner Exp $ + * $Id: autcpu12.c,v 1.22 2004/11/04 12:53:10 gleixner 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 @@ -15,7 +15,7 @@ * Overview: * This is a device driver for the NAND flash device found on the * autronix autcpu12 board, which is a SmartMediaCard. It supports - * 16MB, 32MB and 64MB cards. + * 16MiB, 32MiB and 64MiB cards. * * * 02-12-2002 TG Cleanup of module params @@ -25,10 +25,11 @@ * added page_cache * * 10-06-2002 TG 128K card support added - * */ +#include #include +#include #include #include #include @@ -43,68 +44,49 @@ */ static struct mtd_info *autcpu12_mtd = NULL; -/* - * Module stuff - */ -#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) -#define autcpu12_init init_module -#define autcpu12_cleanup cleanup_module -#endif - static int autcpu12_io_base = CS89712_VIRT_BASE; static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC; static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET; static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET; -static int autcpu12_fio_base; - -#ifdef MODULE -MODULE_PARM(autcpu12_fio_pbase, "i"); -MODULE_PARM(autcpu12_fio_ctrl, "i"); -MODULE_PARM(autcpu12_pedr, "i"); - -__setup("autcpu12_fio_pbase=",autcpu12_fio_pbase); -__setup("autcpu12_fio_ctrl=",autcpu12_fio_ctrl); -__setup("autcpu12_pedr=",autcpu12_pedr); -#endif +static void __iomem * autcpu12_fio_base; /* * Define partitions for flash devices */ - static struct mtd_partition partition_info16k[] = { - { name: "AUTCPU12 flash partition 1", - offset: 0, - size: 8 * SZ_1M }, - { name: "AUTCPU12 flash partition 2", - offset: 8 * SZ_1M, - size: 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 8 * SZ_1M, + .size = 8 * SZ_1M }, }; static struct mtd_partition partition_info32k[] = { - { name: "AUTCPU12 flash partition 1", - offset: 0, - size: 8 * SZ_1M }, - { name: "AUTCPU12 flash partition 2", - offset: 8 * SZ_1M, - size: 24 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 8 * SZ_1M, + .size = 24 * SZ_1M }, }; static struct mtd_partition partition_info64k[] = { - { name: "AUTCPU12 flash partition 1", - offset: 0, - size: 16 * SZ_1M }, - { name: "AUTCPU12 flash partition 2", - offset: 16 * SZ_1M, - size: 48 * SZ_1M}, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 16 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 16 * SZ_1M, + .size = 48 * SZ_1M }, }; static struct mtd_partition partition_info128k[] = { - { name: "AUTCPU12 flash partition 1", - offset: 0, - size: 16 * SZ_1M }, - { name: "AUTCPU12 flash partition 2", - offset: 16 * SZ_1M, - size: 112 * SZ_1M}, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 16 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 16 * SZ_1M, + .size = 112 * SZ_1M }, }; #define NUM_PARTITIONS16K 2 @@ -114,7 +96,7 @@ /* * hardware specific access to control-lines */ -void autcpu12_hwcontrol(int cmd) +static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) { switch(cmd){ @@ -133,12 +115,13 @@ /* * read device ready pin */ -int autcpu12_device_ready(void) +int autcpu12_device_ready(struct mtd_info *mtd) { return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; } + /* * Main initialization routine */ @@ -157,7 +140,7 @@ } /* map physical adress */ - autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K); + autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K); if(!autcpu12_fio_base){ printk("Ioremap autcpu12 SmartMedia Card failed\n"); err = -EIO; @@ -183,29 +166,18 @@ this->chip_delay = 20; this->eccmode = NAND_ECC_SOFT; + /* Enable the following for a flash based bad block table */ + /* + this->options = NAND_USE_FLASH_BBT; + */ + this->options = NAND_USE_FLASH_BBT; + /* Scan to find existance of the device */ - if (nand_scan (autcpu12_mtd)) { + if (nand_scan (autcpu12_mtd, 1)) { err = -ENXIO; goto out_ior; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk ("Unable to allocate NAND data buffer for AUTCPU12.\n"); - err = -ENOMEM; - goto out_ior; - } - - /* Allocate memory for internal data buffer */ - this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); - if (!this->data_cache) { - printk ("Unable to allocate NAND data cache for AUTCPU12.\n"); - err = -ENOMEM; - goto out_buf; - } - this->cache_page = -1; - /* Register the partitions */ switch(autcpu12_mtd->size){ case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; @@ -215,15 +187,11 @@ default: { printk ("Unsupported SmartMedia device\n"); err = -ENXIO; - goto out_cac; + goto out_ior; } } goto out; -out_cac: - kfree (this->data_cache); -out_buf: - kfree (this->data_buf); out_ior: iounmap((void *)autcpu12_fio_base); out_mtd: @@ -240,17 +208,8 @@ #ifdef MODULE static void __exit autcpu12_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1]; - - /* Unregister partitions */ - del_mtd_partitions(autcpu12_mtd); - - /* Unregister the device */ - del_mtd_device (autcpu12_mtd); - - /* Free internal data buffers */ - kfree (this->data_buf); - kfree (this->data_cache); + /* Release resources, unregister device */ + nand_release (autcpu12_mtd); /* unmap physical adress */ iounmap((void *)autcpu12_fio_base); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/diskonchip.c @@ -0,0 +1,1780 @@ +/* + * drivers/mtd/nand/diskonchip.c + * + * (C) 2003 Red Hat, Inc. + * (C) 2004 Dan Brown + * (C) 2004 Kalev Lember + * + * Author: David Woodhouse + * Additional Diskonchip 2000 and Millennium support by Dan Brown + * Diskonchip Millennium Plus support by Kalev Lember + * + * Error correction code lifted from the old docecc code + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * converted to the generic Reed-Solomon library by Thomas Gleixner + * + * Interface to generic NAND code for M-Systems DiskOnChip devices + * + * $Id: diskonchip.c,v 1.49 2005/02/22 21:48:21 gleixner Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Where to look for the devices? */ +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 +#endif + +static unsigned long __initdata doc_locations[] = { +#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_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, + 0xff000000, +#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) + 0xff000000, +##else +#warning Unknown architecture for DiskOnChip. No default probe locations defined +#endif + 0xffffffff }; + +static struct mtd_info *doclist = NULL; + +struct doc_priv { + void __iomem *virtadr; + unsigned long physadr; + u_char ChipID; + u_char CDSNControl; + int chips_per_floor; /* The number of chips detected on each floor */ + int curfloor; + int curchip; + int mh0_page; + int mh1_page; + struct mtd_info *nextdoc; +}; + +/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL + MediaHeader. The spec says to just keep going, I think, but that's just + silly. */ +#define MAX_MEDIAHEADER_SCAN 8 + +/* This is the syndrome computed by the HW ecc generator upon reading an empty + page, one with all 0xff for data and stored ecc code. */ +static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; +/* This is the ecc value computed by the HW ecc generator upon writing an empty + page, one with all 0xff for data. */ +static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; + +#define INFTL_BBT_RESERVED_BLOCKS 4 + +#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) +#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) +#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); +static void doc200x_select_chip(struct mtd_info *mtd, int chip); + +static int debug=0; +module_param(debug, int, 0); + +static int try_dword=1; +module_param(try_dword, int, 0); + +static int no_ecc_failures=0; +module_param(no_ecc_failures, int, 0); + +#ifdef CONFIG_MTD_PARTITIONS +static int no_autopart=0; +module_param(no_autopart, int, 0); +#endif + +#ifdef MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write=1; +#else +static int inftl_bbt_write=0; +#endif +module_param(inftl_bbt_write, int, 0); + +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; +module_param(doc_config_location, ulong, 0); +MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); + + +/* Sector size for HW ECC */ +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA 10 bit words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) +/* Number of roots */ +#define NROOTS 4 +/* First consective root */ +#define FCR 510 +/* Number of symbols */ +#define NN 1023 + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * The HW decoder in the DoC ASIC's provides us a error syndrome, + * which we must convert to a standard syndrom usable by the generic + * Reed-Solomon library code. + * + * Fabrice Bellard figured this out in the old docecc code. I added + * some comments, improved a minor bit and converted it to make use + * of the generic Reed-Solomon libary. tglx + */ +static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) +{ + int i, j, nerr, errpos[8]; + uint8_t parity; + uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + + /* Convert the ecc bytes into words */ + ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); + ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); + ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); + ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); + parity = ecc[1]; + + /* Initialize the syndrom buffer */ + for (i = 0; i < NROOTS; i++) + s[i] = ds[0]; + /* + * Evaluate + * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] + * where x = alpha^(FCR + i) + */ + for(j = 1; j < NROOTS; j++) { + if(ds[j] == 0) + continue; + tmp = rs->index_of[ds[j]]; + for(i = 0; i < NROOTS; i++) + s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; + } + + /* Calc s[i] = s[i] / alpha^(v + i) */ + for (i = 0; i < NROOTS; i++) { + if (syn[i]) + syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); + } + /* Call the decoder library */ + nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); + + /* Incorrectable errors ? */ + if (nerr < 0) + return nerr; + + /* + * Correct the errors. The bitpositions are a bit of magic, + * but they are given by the design of the de/encoder circuit + * in the DoC ASIC's. + */ + for(i = 0;i < nerr; i++) { + int index, bitpos, pos = 1015 - errpos[i]; + uint8_t val; + if (pos >= NB_DATA && pos < 1019) + continue; + 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 = (uint8_t) (errval[i] >> (2 + bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[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 = (uint8_t)(errval[i] << (8 - bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + } + } + /* If the parity is wrong, no rescue possible */ + return parity ? -1 : nerr; +} + +static void DoC_Delay(struct doc_priv *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 if (DoC_is_MillenniumPlus(doc)) + dummy = ReadDOC(doc->virtadr, Mplus_NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + unsigned long timeo = jiffies + (HZ * 10); + + if(debug) printk("_DoC_WaitReady...\n"); + /* Out-of-line routine to wait for chip response */ + if (DoC_is_MillenniumPlus(doc)) { + while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } else { + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } + + return 0; +} + +static inline int DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + int ret = 0; + + if (DoC_is_MillenniumPlus(doc)) { + DoC_Delay(doc, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + } else { + DoC_Delay(doc, 4); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + DoC_Delay(doc, 2); + } + + if(debug) printk("DoC_WaitReady OK\n"); + return ret; +} + +static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if(debug)printk("write_byte %02x\n", datum); + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, 2k_CDSN_IO); +} + +static u_char doc2000_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(doc, 2); + ret = ReadDOC(docptr, 2k_CDSN_IO); + if (debug) printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2000_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + if (debug)printk("writebuf of %d bytes: ", len); + for (i=0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) printk("\n"); +} + +static void doc2000_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("readbuf of %d bytes: ", len); + + for (i=0; i < len; i++) { + buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); + } +} + +static void doc2000_readbuf_dword(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) printk("readbuf_dword of %d bytes: ", len); + + if (unlikely((((unsigned long)buf)|len) & 3)) { + for (i=0; i < len; i++) { + *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); + } + } else { + for (i=0; i < len; i+=4) { + *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); + } + } +} + +static int doc2000_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i=0; i < len; i++) + if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) + return -EFAULT; + return 0; +} + +static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t ret; + + doc200x_select_chip(mtd, nr); + doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_READID); + doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); + doc200x_hwcontrol(mtd, NAND_CTL_SETALE); + this->write_byte(mtd, 0); + doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + + /* We cant' use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); + + ret = this->read_byte(mtd) << 8; + ret |= this->read_byte(mtd); + + if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { + /* First chip probe. See if we get same results by 32-bit access */ + union { + uint32_t dword; + uint8_t byte[4]; + } ident; + void __iomem *docptr = doc->virtadr; + + doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); + doc2000_write_byte(mtd, NAND_CMD_READID); + doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); + doc200x_hwcontrol(mtd, NAND_CTL_SETALE); + doc2000_write_byte(mtd, 0); + doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + + udelay(50); + + ident.dword = readl(docptr + DoC_2k_CDSN_IO); + if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { + printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); + this->read_buf = &doc2000_readbuf_dword; + } + } + + return ret; +} + +static void __init doc2000_count_chips(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t mfrid; + int i; + + /* Max 4 chips per floor on DiskOnChip 2000 */ + doc->chips_per_floor = 4; + + /* Find out what the first chip is */ + mfrid = doc200x_ident_chip(mtd, 0); + + /* Find how many chips in each floor. */ + for (i = 1; i < 4; i++) { + if (doc200x_ident_chip(mtd, i) != mfrid) + break; + } + doc->chips_per_floor = i; + printk(KERN_DEBUG "Detected %d chips per floor.\n", i); +} + +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + struct doc_priv *doc = this->priv; + + int status; + + DoC_WaitReady(doc); + this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + DoC_WaitReady(doc); + status = (int)this->read_byte(mtd); + + return status; +} + +static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, Mil_CDSN_IO); + WriteDOC(datum, docptr, WritePipeTerm); +} + +static u_char doc2001_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + //ReadDOC(docptr, CDSNSlowIO); + /* 11.4.5 -- delay twice to allow extended length cycle */ + DoC_Delay(doc, 2); + ReadDOC(docptr, ReadPipeInit); + //return ReadDOC(docptr, Mil_CDSN_IO); + return ReadDOC(docptr, LastDataRead); +} + +static void doc2001_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i=0; i < len; i++) + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + /* Terminate write pipeline */ + WriteDOC(0x00, docptr, WritePipeTerm); +} + +static void doc2001_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i=0; i < len-1; i++) + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); + + /* Terminate read pipeline */ + buf[i] = ReadDOC(docptr, LastDataRead); +} + +static int doc2001_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i=0; i < len-1; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, LastDataRead); + return i; + } + if (buf[i] != ReadDOC(docptr, LastDataRead)) + return i; + return 0; +} + +static u_char doc2001plus_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + ret = ReadDOC(docptr, Mplus_LastDataRead); + if (debug) printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2001plus_writebuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("writebuf of %d bytes: ", len); + for (i=0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) printk("\n"); +} + +static void doc2001plus_readbuf(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("readbuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i=0; i < len-2; i++) { + buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + + /* Terminate read pipeline */ + buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len-2]); + buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len-1]); + if (debug) printk("\n"); +} + +static int doc2001plus_verifybuf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug)printk("verifybuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i=0; i < len-2; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, Mplus_LastDataRead); + ReadDOC(docptr, Mplus_LastDataRead); + return i; + } + if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead)) + return len-2; + if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead)) + return len-1; + return 0; +} + +static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if(debug)printk("select chip (%d)\n", chip); + + if (chip == -1) { + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + return; + } + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* Assert ChipEnable and deassert WriteProtect */ + WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if(debug)printk("select chip (%d)\n", chip); + + if (chip == -1) + return; + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* 11.4.4 -- deassert CE before changing chip */ + doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); + + WriteDOC(floor, docptr, FloorSelect); + WriteDOC(chip, docptr, CDSNDeviceSelect); + + doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + switch(cmd) { + case NAND_CTL_SETNCE: + doc->CDSNControl |= CDSN_CTRL_CE; + break; + case NAND_CTL_CLRNCE: + doc->CDSNControl &= ~CDSN_CTRL_CE; + break; + case NAND_CTL_SETCLE: + doc->CDSNControl |= CDSN_CTRL_CLE; + break; + case NAND_CTL_CLRCLE: + doc->CDSNControl &= ~CDSN_CTRL_CLE; + break; + case NAND_CTL_SETALE: + doc->CDSNControl |= CDSN_CTRL_ALE; + break; + case NAND_CTL_CLRALE: + doc->CDSNControl &= ~CDSN_CTRL_ALE; + break; + case NAND_CTL_SETWP: + doc->CDSNControl |= CDSN_CTRL_WP; + break; + case NAND_CTL_CLRWP: + doc->CDSNControl &= ~CDSN_CTRL_WP; + break; + } + if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); +} + +static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* + * Must terminate write pipeline before sending any commands + * to the device. + */ + if (command == NAND_CMD_PAGEPROG) { + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + } + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + WriteDOC(readcmd, docptr, Mplus_FlashCmd); + } + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + + if (column != -1 || page_addr != -1) { + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + WriteDOC(column, docptr, Mplus_FlashAddress); + } + if (page_addr != -1) { + WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); + /* One more address cycle for higher density devices */ + if (this->chipsize & 0x0c000000) { + WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); + printk("high density\n"); + } + } + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + /* deassert ALE */ + if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) + WriteDOC(0, docptr, Mplus_FlashControl); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +static int doc200x_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if (DoC_is_MillenniumPlus(doc)) { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if(debug) + printk("not ready\n"); + return 0; + } + if (debug)printk("was ready\n"); + return 1; + } else { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if(debug) + printk("not ready\n"); + return 0; + } + /* 11.4.2 -- Must NOP twice if it's ready */ + DoC_Delay(doc, 2); + if (debug)printk("was ready\n"); + return 1; + } +} + +static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* This is our last resort if we couldn't find or create a BBT. Just + pretend all blocks are good. */ + return 0; +} + +static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch(mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + break; + } +} + +static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch(mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); + break; + } +} + +/* This code is only called on write */ +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + unsigned char *ecc_code) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + } else if (DoC_is_MillenniumPlus(doc)) { + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + } else { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } + + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (ecc_code[i] != empty_write_ecc[i]) + emptymatch = 0; + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); +#if 0 + /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the writebuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, we do have an all-0xff data buffer. + Return all-0xff ecc value instead of the computed one, so + it'll look just like a freshly-erased page. */ + if (emptymatch) memset(ecc_code, 0xff, 6); +#endif + return 0; +} + +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + int i, ret = 0; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + volatile u_char dummy; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + } else if (DoC_is_MillenniumPlus(doc)) { + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + } else { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + } + + /* Error occured ? */ + if (dummy & 0x80) { + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (calc_ecc[i] != empty_read_syndrome[i]) + emptymatch = 0; + } + /* If emptymatch=1, the read syndrome is consistent with an + all-0xff data and stored ecc block. Check the stored ecc. */ + if (emptymatch) { + for (i = 0; i < 6; i++) { + if (read_ecc[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, check the data block. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the readbuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, this is almost certainly a freshly- + erased block, in which case the ECC will not come out right. + We'll suppress the error and tell the caller everything's + OK. Because it is. */ + if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); + if (ret > 0) + printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + if (no_ecc_failures && (ret == -1)) { + printk(KERN_ERR "suppressing ECC failure\n"); + ret = 0; + } + return ret; +} + +//u_char mydatabuf[528]; + +static struct nand_oobinfo doc200x_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 4, 5}, + .oobfree = { {8, 8} } +}; + +/* Find the (I)NFTL Media Header, and optionally also the mirror media header. + On sucessful return, buf will contain a copy of the media header for + further processing. id is the string to scan for, and will presumably be + either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media + header. The page #s of the found media headers are placed in mh0_page and + mh1_page in the DOC private structure. */ +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, + const char *id, int findmirror) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); + int ret; + size_t retlen; + + end = min(end, mtd->size); // paranoia + for (offs = 0; offs < end; offs += mtd->erasesize) { + ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); + if (retlen != mtd->oobblock) continue; + if (ret) { + printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", + offs); + } + if (memcmp(buf, id, 6)) continue; + printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); + if (doc->mh0_page == -1) { + doc->mh0_page = offs >> this->page_shift; + if (!findmirror) return 1; + continue; + } + doc->mh1_page = offs >> this->page_shift; + return 2; + } + if (doc->mh0_page == -1) { + printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); + return 0; + } + /* Only one mediaheader was found. We want buf to contain a + mediaheader on return, so we'll have to re-read the one we found. */ + offs = doc->mh0_page << this->page_shift; + ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); + if (retlen != mtd->oobblock) { + /* Insanity. Give up. */ + printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); + return 0; + } + return 1; +} + +static inline int __init nftl_partscan(struct mtd_info *mtd, + struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct NFTLMediaHeader *mh; + const unsigned psize = 1 << this->page_shift; + unsigned blocks, maxblocks; + int offs, numheaders; + + buf = kmalloc(mtd->oobblock, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; + mh = (struct NFTLMediaHeader *) buf; + + mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); + mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); + mh->FormattedSize = le32_to_cpu(mh->FormattedSize); + + printk(KERN_INFO " DataOrgID = %s\n" + " NumEraseUnits = %d\n" + " FirstPhysicalEUN = %d\n" + " FormattedSize = %d\n" + " UnitSizeFactor = %d\n", + mh->DataOrgID, mh->NumEraseUnits, + mh->FirstPhysicalEUN, mh->FormattedSize, + mh->UnitSizeFactor); + + blocks = mtd->size >> this->phys_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + + if (mh->UnitSizeFactor == 0x00) { + /* Auto-determine UnitSizeFactor. The constraints are: + - There can be at most 32768 virtual blocks. + - There can be at most (virtual block size - page size) + virtual blocks (because MediaHeader+BBT must fit in 1). + */ + mh->UnitSizeFactor = 0xff; + while (blocks > maxblocks) { + blocks >>= 1; + maxblocks = min(32768U, (maxblocks << 1) + psize); + mh->UnitSizeFactor--; + } + printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); + } + + /* NOTE: The lines below modify internal variables of the NAND and MTD + layers; variables with have already been configured by nand_scan. + Unfortunately, we didn't know before this point what these values + should be. Thus, this code is somewhat dependant on the exact + implementation of the NAND layer. */ + if (mh->UnitSizeFactor != 0xff) { + this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); + mtd->erasesize <<= (0xff - mh->UnitSizeFactor); + printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); + blocks = mtd->size >> this->bbt_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + } + + if (blocks > maxblocks) { + printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); + goto out; + } + + /* Skip past the media headers. */ + offs = max(doc->mh0_page, doc->mh1_page); + offs <<= this->page_shift; + offs += mtd->erasesize; + + parts[0].name = " DiskOnChip BDTL partition"; + parts[0].offset = offs; + parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + + offs += parts[0].size; + if (offs < mtd->size) { + parts[1].name = " DiskOnChip Remainder partition"; + parts[1].offset = offs; + parts[1].size = mtd->size - offs; + ret = 2; + goto out; + } + ret = 1; +out: + kfree(buf); + return ret; +} + +/* This is a stripped-down copy of the code in inftlmount.c */ +static inline int __init inftl_partscan(struct mtd_info *mtd, + struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct INFTLMediaHeader *mh; + struct INFTLPartition *ip; + int numparts = 0; + int blocks; + int vshift, lastvunit = 0; + int i; + int end = mtd->size; + + if (inftl_bbt_write) + end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); + + buf = kmalloc(mtd->oobblock, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + + if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; + doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); + mh = (struct INFTLMediaHeader *) buf; + + mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); + mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); + mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); + mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); + mh->FormatFlags = le32_to_cpu(mh->FormatFlags); + mh->PercentUsed = le32_to_cpu(mh->PercentUsed); + + printk(KERN_INFO " bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", + mh->bootRecordID, mh->NoOfBootImageBlocks, + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->BlockMultiplierBits, mh->FormatFlags, + ((unsigned char *) &mh->OsakVersion)[0] & 0xf, + ((unsigned char *) &mh->OsakVersion)[1] & 0xf, + ((unsigned char *) &mh->OsakVersion)[2] & 0xf, + ((unsigned char *) &mh->OsakVersion)[3] & 0xf, + mh->PercentUsed); + + vshift = this->phys_erase_shift + mh->BlockMultiplierBits; + + blocks = mtd->size >> vshift; + if (blocks > 32768) { + printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); + goto out; + } + + blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); + if (inftl_bbt_write && (blocks > mtd->erasesize)) { + printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); + goto out; + } + + /* Scan the partitions */ + for (i = 0; (i < 4); i++) { + ip = &(mh->Partitions[i]); + ip->virtualUnits = le32_to_cpu(ip->virtualUnits); + ip->firstUnit = le32_to_cpu(ip->firstUnit); + ip->lastUnit = le32_to_cpu(ip->lastUnit); + ip->flags = le32_to_cpu(ip->flags); + ip->spareUnits = le32_to_cpu(ip->spareUnits); + ip->Reserved0 = le32_to_cpu(ip->Reserved0); + + printk(KERN_INFO " PARTITION[%d] ->\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + i, ip->virtualUnits, ip->firstUnit, + ip->lastUnit, ip->flags, + ip->spareUnits); + +#if 0 + if ((i == 0) && (ip->firstUnit > 0)) { + parts[0].name = " DiskOnChip IPL / Media Header partition"; + parts[0].offset = 0; + parts[0].size = mtd->erasesize * ip->firstUnit; + numparts = 1; + } +#endif + + if (ip->flags & INFTL_BINARY) + parts[numparts].name = " DiskOnChip BDK partition"; + else + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = ip->firstUnit << vshift; + parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; + numparts++; + if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; + if (ip->flags & INFTL_LAST) break; + } + lastvunit++; + if ((lastvunit << vshift) < end) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = lastvunit << vshift; + parts[numparts].size = end - parts[numparts].offset; + numparts++; + } + ret = numparts; +out: + kfree(buf); + return ret; +} + +static int __init nftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[2]; + + memset((char *) parts, 0, sizeof(parts)); + /* On NFTL, we have to find the media headers before we can read the + BBTs, since they're stored in the media header eraseblocks. */ + numparts = nftl_partscan(mtd, parts); + if (!numparts) return -EIO; + this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_td->veroffs = 7; + this->bbt_td->pages[0] = doc->mh0_page + 1; + if (doc->mh1_page != -1) { + this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_md->veroffs = 7; + this->bbt_md->pages[0] = doc->mh1_page + 1; + } else { + this->bbt_md = NULL; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static int __init inftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[5]; + + if (this->numchips > doc->chips_per_floor) { + printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); + return -EIO; + } + + if (DoC_is_MillenniumPlus(doc)) { + this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->pages[0] = 2; + this->bbt_md = NULL; + } else { + this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | + NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->offs = 8; + this->bbt_td->len = 8; + this->bbt_td->veroffs = 7; + this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_td->reserved_block_code = 0x01; + this->bbt_td->pattern = "MSYS_BBT"; + + this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | + NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_md->options |= NAND_BBT_WRITE; + this->bbt_md->offs = 8; + this->bbt_md->len = 8; + this->bbt_md->veroffs = 7; + this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_md->reserved_block_code = 0x01; + this->bbt_md->pattern = "TBB_SYSM"; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + memset((char *) parts, 0, sizeof(parts)); + numparts = inftl_partscan(mtd, parts); + /* At least for now, require the INFTL Media Header. We could probably + do without it for non-INFTL use, since all it gives us is + autopartitioning, but I want to give it more thought. */ + if (!numparts) return -EIO; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static inline int __init doc2000_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = doc2000_write_byte; + this->read_byte = doc2000_read_byte; + this->write_buf = doc2000_writebuf; + this->read_buf = doc2000_readbuf; + this->verify_buf = doc2000_verifybuf; + this->scan_bbt = nftl_scan_bbt; + + doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (NFTL Model)"; + return (4 * doc->chips_per_floor); +} + +static inline int __init doc2001_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = doc2001_write_byte; + this->read_byte = doc2001_read_byte; + this->write_buf = doc2001_writebuf; + this->read_buf = doc2001_readbuf; + this->verify_buf = doc2001_verifybuf; + + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { + /* It's not a Millennium; it's one of the newer + DiskOnChip 2000 units with a similar ASIC. + Treat it like a Millennium, except that it + can have multiple chips. */ + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (INFTL Model)"; + this->scan_bbt = inftl_scan_bbt; + return (4 * doc->chips_per_floor); + } else { + /* Bog-standard Millennium */ + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium"; + this->scan_bbt = nftl_scan_bbt; + return 1; + } +} + +static inline int __init doc2001plus_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->write_byte = NULL; + this->read_byte = doc2001plus_read_byte; + this->write_buf = doc2001plus_writebuf; + this->read_buf = doc2001plus_readbuf; + this->verify_buf = doc2001plus_verifybuf; + this->scan_bbt = inftl_scan_bbt; + this->hwcontrol = NULL; + this->select_chip = doc2001plus_select_chip; + this->cmdfunc = doc2001plus_command; + this->enable_hwecc = doc2001plus_enable_hwecc; + + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium Plus"; + + return 1; +} + +static inline int __init doc_probe(unsigned long physadr) +{ + unsigned char ChipID; + struct mtd_info *mtd; + struct nand_chip *nand; + struct doc_priv *doc; + void __iomem *virtadr; + unsigned char save_control; + unsigned char tmp, tmpb, tmpc; + int reg, len, numchips; + int ret = 0; + + virtadr = ioremap(physadr, DOC_IOREMAP_LEN); + if (!virtadr) { + printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); + return -EIO; + } + + /* 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. + */ + save_control = ReadDOC(virtadr, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + virtadr, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + virtadr, DOCControl); + + ChipID = ReadDOC(virtadr, ChipID); + + switch(ChipID) { + case DOC_ChipID_Doc2k: + reg = DoC_2k_ECCStatus; + break; + case DOC_ChipID_DocMil: + reg = DoC_ECCConf; + break; + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + case 0: + /* Possible Millennium Plus, need to do more checks */ + /* Possibly release from power down mode */ + for (tmp = 0; (tmp < 4); tmp++) + ReadDOC(virtadr, Mplus_Power); + + /* Reset the Millennium Plus ASIC */ + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + + mdelay(1); + /* Enable the Millennium Plus ASIC */ + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | + DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + mdelay(1); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_DocMilPlus16: + reg = DoC_Mplus_Toggle; + break; + case DOC_ChipID_DocMilPlus32: + printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); + default: + ret = -ENODEV; + goto notfound; + } + break; + + default: + ret = -ENODEV; + goto notfound; + } + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + if ((tmp == tmpb) || (tmp != tmpc)) { + printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); + ret = -ENODEV; + goto notfound; + } + + for (mtd = doclist; mtd; mtd = doc->nextdoc) { + unsigned char oldval; + unsigned char newval; + nand = mtd->priv; + doc = nand->priv; + /* Use the alias resolution register to determine if this is + in fact the same DOC aliased to a new address. If writes + to one chip's alias resolution register change the value on + the other chip, they're the same chip. */ + if (ChipID == DOC_ChipID_DocMilPlus16) { + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + newval = ReadDOC(virtadr, Mplus_AliasResolution); + } else { + oldval = ReadDOC(doc->virtadr, AliasResolution); + newval = ReadDOC(virtadr, AliasResolution); + } + if (oldval != newval) + continue; + if (ChipID == DOC_ChipID_DocMilPlus16) { + WriteDOC(~newval, virtadr, Mplus_AliasResolution); + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it + } else { + WriteDOC(~newval, virtadr, AliasResolution); + oldval = ReadDOC(doc->virtadr, AliasResolution); + WriteDOC(newval, virtadr, AliasResolution); // restore it + } + newval = ~newval; + if (oldval == newval) { + printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); + goto notfound; + } + } + + printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); + + len = sizeof(struct mtd_info) + + sizeof(struct nand_chip) + + sizeof(struct doc_priv) + + (2 * sizeof(struct nand_bbt_descr)); + mtd = kmalloc(len, GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); + ret = -ENOMEM; + goto fail; + } + memset(mtd, 0, len); + + nand = (struct nand_chip *) (mtd + 1); + doc = (struct doc_priv *) (nand + 1); + nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); + nand->bbt_md = nand->bbt_td + 1; + + mtd->priv = nand; + mtd->owner = THIS_MODULE; + + nand->priv = doc; + nand->select_chip = doc200x_select_chip; + nand->hwcontrol = doc200x_hwcontrol; + nand->dev_ready = doc200x_dev_ready; + nand->waitfunc = doc200x_wait; + nand->block_bad = doc200x_block_bad; + nand->enable_hwecc = doc200x_enable_hwecc; + nand->calculate_ecc = doc200x_calculate_ecc; + nand->correct_data = doc200x_correct_data; + + nand->autooob = &doc200x_oobinfo; + nand->eccmode = NAND_ECC_HW6_512; + nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; + + doc->physadr = physadr; + doc->virtadr = virtadr; + doc->ChipID = ChipID; + doc->curfloor = -1; + doc->curchip = -1; + doc->mh0_page = -1; + doc->mh1_page = -1; + doc->nextdoc = doclist; + + if (ChipID == DOC_ChipID_Doc2k) + numchips = doc2000_init(mtd); + else if (ChipID == DOC_ChipID_DocMilPlus16) + numchips = doc2001plus_init(mtd); + else + numchips = doc2001_init(mtd); + + if ((ret = nand_scan(mtd, numchips))) { + /* DBB note: i believe nand_release is necessary here, as + buffers may have been allocated in nand_base. Check with + Thomas. FIX ME! */ + /* nand_release will call del_mtd_device, but we haven't yet + added it. This is handled without incident by + del_mtd_device, as far as I can tell. */ + nand_release(mtd); + kfree(mtd); + goto fail; + } + + /* Success! */ + doclist = mtd; + return 0; + +notfound: + /* Put back the contents of the DOCControl register, in case it's not + actually a DiskOnChip. */ + WriteDOC(save_control, virtadr, DOCControl); +fail: + iounmap(virtadr); + return ret; +} + +static void release_nanddoc(void) +{ + struct mtd_info *mtd, *nextmtd; + struct nand_chip *nand; + struct doc_priv *doc; + + for (mtd = doclist; mtd; mtd = nextmtd) { + nand = mtd->priv; + doc = nand->priv; + + nextmtd = doc->nextdoc; + nand_release(mtd); + iounmap(doc->virtadr); + kfree(mtd); + } +} + +static int __init init_nanddoc(void) +{ + int i, ret = 0; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 510 + * primitve element to generate roots = 1 + * generator polinomial degree = 4 + */ + rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); + if (!rs_decoder) { + printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + return -ENOMEM; + } + + if (doc_config_location) { + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + ret = doc_probe(doc_config_location); + if (ret < 0) + goto outerr; + } else { + for (i=0; (doc_locations[i] != 0xffffffff); i++) { + doc_probe(doc_locations[i]); + } + } + /* No banner message any more. Print a message if no DiskOnChip + found, so the user knows we at least tried. */ + if (!doclist) { + printk(KERN_INFO "No valid DiskOnChip devices found\n"); + ret = -ENODEV; + goto outerr; + } + return 0; +outerr: + free_rs(rs_decoder); + return ret; +} + +static void __exit cleanup_nanddoc(void) +{ + /* Cleanup the nand/DoC resources */ + release_nanddoc(); + + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +} + +module_init(init_nanddoc); +module_exit(cleanup_nanddoc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); --- linux-2.4.21/drivers/mtd/nand/edb7312.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/edb7312.c @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.3 2002/06/06 12:58:16 mag Exp $ + * $Id: edb7312.c,v 1.11 2004/11/04 12:53:10 gleixner 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 @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -52,41 +53,28 @@ * Module stuff */ -static int ep7312_fio_pbase = EP7312_FIO_PBASE; -static int ep7312_pxdr = EP7312_PXDR; -static int ep7312_pxddr = EP7312_PXDDR; - -#ifdef MODULE -MODULE_PARM(ep7312_fio_pbase, "i"); -MODULE_PARM(ep7312_pxdr, "i"); -MODULE_PARM(ep7312_pxddr, "i"); - -__setup("ep7312_fio_pbase=",ep7312_fio_pbase); -__setup("ep7312_pxdr=",ep7312_pxdr); -__setup("ep7312_pxddr=",ep7312_pxddr); -#endif +static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE; +static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR; +static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR; #ifdef CONFIG_MTD_PARTITIONS /* * Define static partitions for flash device */ static struct mtd_partition partition_info[] = { - { name: "EP7312 Nand Flash", - offset: 0, - size: 8*1024*1024 } + { .name = "EP7312 Nand Flash", + .offset = 0, + .size = 8*1024*1024 } }; #define NUM_PARTITIONS 1 -extern int parse_cmdline_partitions(struct mtd_info *master, - struct mtd_partition **pparts, - const char *mtd_id); #endif /* * hardware specific access to control-lines */ -static void ep7312_hwcontrol(int cmd) +static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) { switch(cmd) { @@ -116,10 +104,13 @@ /* * read device ready pin */ -static int ep7312_device_ready(void) +static int ep7312_device_ready(struct mtd_info *mtd) { return 1; } +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif /* * Main initialization routine @@ -130,7 +121,7 @@ const char *part_type = 0; int mtd_parts_nb = 0; struct mtd_partition *mtd_parts = 0; - int ep7312_fio_base; + void __iomem * ep7312_fio_base; /* Allocate memory for MTD device structure and private data */ ep7312_mtd = kmalloc(sizeof(struct mtd_info) + @@ -142,7 +133,7 @@ } /* map physical adress */ - ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K); + ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K); if(!ep7312_fio_base) { printk("ioremap EDB7312 NAND flash failed\n"); kfree(ep7312_mtd); @@ -174,42 +165,22 @@ this->chip_delay = 15; /* Scan to find existence of the device */ - if (nand_scan (ep7312_mtd)) { + if (nand_scan (ep7312_mtd, 1)) { iounmap((void *)ep7312_fio_base); kfree (ep7312_mtd); return -ENXIO; } - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk("Unable to allocate NAND data buffer for EDB7312.\n"); - iounmap((void *)ep7312_fio_base); - kfree (ep7312_mtd); - return -ENOMEM; - } - - /* Allocate memory for internal data buffer */ - this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); - if (!this->data_cache) { - printk("Unable to allocate NAND data cache for EDB7312.\n"); - kfree (this->data_buf); - iounmap((void *)ep7312_fio_base); - kfree (ep7312_mtd); - return -ENOMEM; - } - this->cache_page = -1; - -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, - "edb7312-nand"); +#ifdef CONFIG_MTD_PARTITIONS + ep7312_mtd->name = "edb7312-nand"; + mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, + &mtd_parts, 0); if (mtd_parts_nb > 0) part_type = "command line"; else mtd_parts_nb = 0; #endif - if (mtd_parts_nb == 0) - { + if (mtd_parts_nb == 0) { mtd_parts = partition_info; mtd_parts_nb = NUM_PARTITIONS; part_type = "static"; @@ -231,12 +202,11 @@ { struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1]; - /* Unregister the device */ - del_mtd_device (ep7312_mtd); + /* Release resources, unregister device */ + nand_release (ap7312_mtd); /* Free internal data buffer */ kfree (this->data_buf); - kfree (this->data_cache); /* Free the MTD device structure */ kfree (ep7312_mtd); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/h1910.c @@ -0,0 +1,208 @@ +/* + * drivers/mtd/nand/h1910.c + * + * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) + * + * Derived from drivers/mtd/nand/edb7312.c + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: h1910.c,v 1.5 2004/11/04 12:53:10 gleixner 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 + * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is + * a 128Mibit (16MiB x 8 bits) NAND flash device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for CLPS7111_VIRT_BASE */ +#include +#include +#include + +/* + * MTD structure for EDB7312 board + */ +static struct mtd_info *h1910_nand_mtd = NULL; + +/* + * Module stuff + */ + +#ifdef CONFIG_MTD_PARTITIONS +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + { name: "h1910 NAND Flash", + offset: 0, + size: 16*1024*1024 } +}; +#define NUM_PARTITIONS 1 + +#endif + + +/* + * hardware specific access to control-lines + */ +static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip* this = (struct nand_chip *) (mtd->priv); + + switch(cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_R |= (1 << 2); + this->IO_ADDR_W |= (1 << 2); + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_R &= ~(1 << 2); + this->IO_ADDR_W &= ~(1 << 2); + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_R |= (1 << 3); + this->IO_ADDR_W |= (1 << 3); + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_R &= ~(1 << 3); + this->IO_ADDR_W &= ~(1 << 3); + break; + + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + } +} + +/* + * read device ready pin + */ +#if 0 +static int h1910_device_ready(struct mtd_info *mtd) +{ + return (GPLR(55) & GPIO_bit(55)); +} +#endif + +/* + * Main initialization routine + */ +static int __init h1910_init (void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + void __iomem *nandaddr; + + if (!machine_is_h1900()) + return -ENODEV; + + nandaddr = __ioremap(0x08000000, 0x1000, 0, 1); + if (!nandaddr) { + printk("Failed to ioremap nand flash.\n"); + return -ENOMEM; + } + + /* Allocate memory for MTD device structure and private data */ + h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!h1910_nand_mtd) { + printk("Unable to allocate h1910 NAND MTD device structure.\n"); + iounmap ((void *) nandaddr); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&h1910_nand_mtd[1]); + + /* Initialize structures */ + memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + h1910_nand_mtd->priv = this; + + /* + * Enable VPEN + */ + GPSR(37) = GPIO_bit(37); + + /* insert callbacks */ + this->IO_ADDR_R = nandaddr; + this->IO_ADDR_W = nandaddr; + this->hwcontrol = h1910_hwcontrol; + this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ + /* 15 us command delay time */ + this->chip_delay = 50; + this->eccmode = NAND_ECC_SOFT; + this->options = NAND_NO_AUTOINCR; + + /* Scan to find existence of the device */ + if (nand_scan (h1910_nand_mtd, 1)) { + printk(KERN_NOTICE "No NAND device - returning -ENXIO\n"); + kfree (h1910_nand_mtd); + iounmap ((void *) nandaddr); + return -ENXIO; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, + "h1910-nand"); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(h1910_init); + +/* + * Clean up routine + */ +static void __exit h1910_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1]; + + /* Release resources, unregister device */ + nand_release (h1910_nand_mtd); + + /* Release io resource */ + iounmap ((void *) this->IO_ADDR_W); + + /* Free the MTD device structure */ + kfree (h1910_nand_mtd); +} +module_exit(h1910_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joshua Wise "); +MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/nand_base.c @@ -0,0 +1,2691 @@ +/* + * drivers/mtd/nand.c + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 02-08-2004 tglx: support for strange chips, which cannot auto increment + * pages on read / read_oob + * + * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes + * pointed this out, as he marked an auto increment capable chip + * as NOAUTOINCR in the board driver. + * Make reads over block boundaries work too + * + * 04-14-2004 tglx: first working version for 2k page size chips + * + * 05-19-2004 tglx: Basic support for Renesas AG-AND chips + * + * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared + * among multiple independend devices. Suggestions and initial patch + * from Ben Dooks + * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ecc support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * + * $Id: nand_base.c,v 1.135 2005/03/01 09:32:45 gleixner 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_oobinfo nand_oob_8 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { {3, 2}, {6, 2} } +}; + +static struct nand_oobinfo nand_oob_16 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { {8, 8} } +}; + +static struct nand_oobinfo nand_oob_64 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + +/* This is used for padding purposes in nand_write_oob */ +static u_char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +/* + * NAND low-level MTD interface functions + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); + +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 * eccbuf, struct nand_oobinfo *oobsel); +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 * eccbuf, struct nand_oobinfo *oobsel); +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 kvec *vecs, + unsigned long count, loff_t to, size_t * retlen); +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); +static void nand_sync (struct mtd_info *mtd); + +/* Some internal functions */ +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, + struct nand_oobinfo *oobsel, int mode); +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); +#else +#define nand_verify_pages(...) (0) +#endif + +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void nand_release_device (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* De-select the NAND device */ + this->select_chip(mtd, -1); + /* Do we have a hardware controller ? */ + if (this->controller) { + spin_lock(&this->controller->lock); + this->controller->active = NULL; + spin_unlock(&this->controller->lock); + } + /* Release the chip */ + spin_lock (&this->chip_lock); + this->state = FL_READY; + wake_up (&this->wq); + spin_unlock (&this->chip_lock); +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswith + */ +static u_char nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readb(this->IO_ADDR_R); +} + +/** + * nand_write_byte - [DEFAULT] write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 8it buswith + */ +static void nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith with + * endianess conversion + */ +static u_char nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); +} + +/** + * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 16bit buswith with + * endianess conversion + */ +static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith without + * endianess conversion + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readw(this->IO_ADDR_R); +} + +/** + * nand_write_word - [DEFAULT] write one word to the chip + * @mtd: MTD device structure + * @word: data word to write + * + * Default write function for 16bit buswith without + * endianess conversion + */ +static void nand_write_word(struct mtd_info *mtd, u16 word) +{ + struct nand_chip *this = mtd->priv; + writew(word, this->IO_ADDR_W); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chip: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + switch(chip) { + case -1: + this->hwcontrol(mtd, NAND_CTL_CLRNCE); + break; + case 0: + this->hwcontrol(mtd, NAND_CTL_SETNCE); + break; + + default: + BUG(); + } +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswith + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswith + */ +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 8bit buswith + */ +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_W); + +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswith + */ +static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 16bit buswith + */ +static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int page, chipnr, res = 0; + struct nand_chip *this = mtd->priv; + u16 bad; + + if (getchip) { + page = (int)(ofs >> this->page_shift); + chipnr = (int)(ofs >> this->chip_shift); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + } else + page = (int) ofs; + + if (this->options & NAND_BUSWIDTH_16) { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); + bad = cpu_to_le16(this->read_word(mtd)); + if (this->badblockpos & 0x1) + bad >>= 1; + if ((bad & 0xFF) != 0xff) + res = 1; + } else { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); + if (this->read_byte(mtd) != 0xff) + res = 1; + } + + if (getchip) { + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + } + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. +*/ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + u_char buf[2] = {0, 0}; + size_t retlen; + int block; + + /* Get block number */ + block = ((int) ofs) >> this->bbt_erase_shift; + if (this->bbt) + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Do we have a flash based bad block table ? */ + if (this->options & NAND_USE_FLASH_BBT) + return nand_update_bbt (mtd, ofs); + + /* We write two bytes, so we dont have to mess with 16 bit access */ + ofs += mtd->oobsize + (this->badblockpos & ~0x01); + return nand_write_oob (mtd, ofs , 2, &retlen, buf); +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * Check, if the device is write protected + * + * The function expects, that the device is already selected + */ +static int nand_check_wp (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + + if (!this->bbt) + return this->block_bad(mtd, ofs, getchip); + + /* Return info from the table */ + return nand_isbad_bbt (mtd, ofs, allowbbt); +} + +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +static void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + unsigned long timeo = jiffies + 2; + + /* wait until command is processed or timeout occures */ + do { + if (this->dev_ready(mtd)) + return; + } while (time_before(jiffies, timeo)); +} + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page + * devices (256/512 Bytes per page) + */ +static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + this->write_byte(mtd, readcmd); + } + this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + nand_wait_ready(mtd); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page devices + * We dont have the seperate regions as we have in the small page devices. + * We must emulate NAND_CMD_READOOB to keep the code compatible. + * + */ +static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->oobblock; + command = NAND_CMD_READ0; + } + + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the command to the device. */ + this->write_byte(mtd, (command & 0xff)); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column & 0xff); + this->write_byte(mtd, column >> 8); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 128MiB */ + if (this->chipsize > (128 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); + return; + + case NAND_CMD_READ0: + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the start read command */ + this->write_byte(mtd, NAND_CMD_READSTART); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + /* Fall through into ready check */ + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + nand_wait_ready(mtd); +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @this: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ + struct nand_chip *active = this; + + DECLARE_WAITQUEUE (wait, current); + + /* + * Grab the lock and see if the device is available + */ +retry: + /* Hardware controller shared among independend devices */ + if (this->controller) { + spin_lock (&this->controller->lock); + if (this->controller->active) + active = this->controller->active; + else + this->controller->active = this; + spin_unlock (&this->controller->lock); + } + + if (active == this) { + spin_lock (&this->chip_lock); + if (this->state == FL_READY) { + this->state = new_state; + spin_unlock (&this->chip_lock); + return; + } + } + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&active->wq, &wait); + spin_unlock (&active->chip_lock); + schedule (); + remove_wait_queue (&active->wq, &wait); + goto retry; +} + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state to select the max. timeout value + * + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + * +*/ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + + unsigned long timeo = jiffies; + int status; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) + this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + + while (time_before(jiffies, timeo)) { + /* Check, if we were interrupted */ + if (this->state != state) + return 0; + + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) + break; + } + cond_resched(); + } + status = (int) this->read_byte(mtd); + return status; +} + +/** + * nand_write_page - [GENERIC] write one page + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @cached: 1 = enable cached programming if supported by chip + * + * Nand_page_program function is used for write and writev ! + * This function will always program a full page of data + * If you call it with a non page aligned buffer, you're lost :) + * + * Cached programming is not supported yet. + */ +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, + u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) +{ + int i, status; + u_char ecc_code[32]; + int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + int *oob_config = oobsel->eccpos; + int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; + int eccbytes = 0; + + /* FIXME: Enable cached programming */ + cached = 0; + + /* Send command to begin auto page programming */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data, take care of eccmode */ + switch (eccmode) { + /* No ecc, write all */ + case NAND_ECC_NONE: + printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + + /* Software ecc 3/256, write all */ + case NAND_ECC_SOFT: + for (; eccsteps; eccsteps--) { + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < 3; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + datidx += this->eccsize; + } + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + default: + eccbytes = this->eccbytes; + for (; eccsteps; eccsteps--) { + /* enable hardware ecc logic for write */ + this->enable_hwecc(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < eccbytes; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + /* If the hardware ecc provides syndromes then + * the ecc code must be written immidiately after + * the data bytes (words) */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, ecc_code, eccbytes); + datidx += this->eccsize; + } + break; + } + + /* Write out OOB data */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); + else + this->write_buf(mtd, oob_buf, mtd->oobsize); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); + + if (!cached) { + /* call wait ready function */ + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + return -EIO; + } + } else { + /* FIXME: Implement cached programming ! */ + /* wait until cache is ready*/ + // status = this->waitfunc (mtd, this, FL_CACHEDRPG); + } + return 0; +} + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +/** + * nand_verify_pages - [GENERIC] verify the chip contents after a write + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @numpages: number of pages to verify + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @chipnr: number of the current chip + * @oobmode: 1 = full buffer verify, 0 = ecc only + * + * 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. + */ +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) +{ + int i, j, datidx = 0, oobofs = 0, res = -EIO; + int eccsteps = this->eccsteps; + int hweccbytes; + u_char oobdata[64]; + + hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; + + /* Send command to read back the first page */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + + for(;;) { + for (j = 0; j < eccsteps; j++) { + /* Loop through and verify the data */ + if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + datidx += mtd->eccsize; + /* Have we a hw generator layout ? */ + if (!hweccbytes) + continue; + if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + oobofs += hweccbytes; + } + + /* check, if we must compare all data or if we just have to + * compare the ecc bytes + */ + if (oobmode) { + if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + } else { + /* Read always, else autoincrement fails */ + this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); + + if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { + int ecccnt = oobsel->eccbytes; + + for (i = 0; i < ecccnt; i++) { + int idx = oobsel->eccpos[i]; + if (oobdata[idx] != oob_buf[oobofs + idx] ) { + DEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed ECC write " + "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + goto out; + } + } + } + } + oobofs += mtd->oobsize - hweccbytes * eccsteps; + page++; + numpages--; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + * Do this also before returning, so the chip is + * ready for the next command. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* All done, return happy */ + if (!numpages) + return 0; + + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this)) + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + } + /* + * Terminate the read command. We come here in case of an error + * So we must issue a reset command. + */ +out: + this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); + return res; +} +#endif + +/** + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff + */ +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff); +} + + +/** + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * This function simply calls nand_do_read_ecc with flags = 0xff + */ +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) +{ + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags) +{ + int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; + int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; + struct nand_chip *this = mtd->priv; + u_char *data_poi, *oob_data = oob_buf; + u_char ecc_calc[32]; + u_char ecc_code[32]; + int eccmode, eccsteps; + int *oob_config, datidx; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + int eccbytes; + int compareecc = 1; + int oobreadlen; + + + 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 */ + if (flags & NAND_GET_DEVICE) + nand_get_device (this, mtd, FL_READING); + + /* use userspace supplied oobinfo, if zero */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) + oobsel = this->autooob; + + eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + oob_config = oobsel->eccpos; + + /* Select the NAND device */ + chipnr = (int)(from >> this->chip_shift); + this->select_chip(mtd, chipnr); + + /* First we calculate the starting page */ + realpage = (int) (from >> this->page_shift); + page = realpage & this->pagemask; + + /* Get raw starting column */ + col = from & (mtd->oobblock - 1); + + end = mtd->oobblock; + ecc = this->eccsize; + eccbytes = this->eccbytes; + + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) + compareecc = 0; + + oobreadlen = mtd->oobsize; + if (this->options & NAND_HWECC_SYNDROME) + oobreadlen -= oobsel->eccbytes; + + /* Loop until all data read */ + while (read < len) { + + int aligned = (!col && (len - read) >= end); + /* + * If the read is not page aligned, we have to read into data buffer + * due to ecc, else we read into return buffer direct + */ + if (aligned) + data_poi = &buf[read]; + else + data_poi = this->data_buf; + + /* Check, if we have this page in the buffer + * + * FIXME: Make it work when we must provide oob data too, + * check the usage of data_buf oob field + */ + if (realpage == this->pagebuf && !oob_buf) { + /* aligned read ? */ + if (aligned) + memcpy (data_poi, this->data_buf, end); + goto readdata; + } + + /* Check, if we must send the read command */ + if (sndcmd) { + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + /* get oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE) + oob_data = &this->data_buf[end]; + + eccsteps = this->eccsteps; + + switch (eccmode) { + case NAND_ECC_NONE: { /* No ECC, Read in a page */ + static unsigned long lastwhinge = 0; + if ((lastwhinge / HZ) != (jiffies / HZ)) { + printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); + lastwhinge = jiffies; + } + this->read_buf(mtd, data_poi, end); + break; + } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ + this->read_buf(mtd, data_poi, end); + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + break; + + default: + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { + this->enable_hwecc(mtd, NAND_ECC_READ); + this->read_buf(mtd, &data_poi[datidx], ecc); + + /* HW ecc with syndrome calculation must read the + * syndrome from flash immidiately after the data */ + if (!compareecc) { + /* Some hw ecc generators need to know when the + * syndrome is read from flash */ + this->enable_hwecc(mtd, NAND_ECC_READSYN); + this->read_buf(mtd, &oob_data[i], eccbytes); + /* We calc error correction directly, it checks the hw + * generator for an error, reads back the syndrome and + * does the error correction on the fly */ + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); + ecc_failed++; + } + } else { + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + } + } + break; + } + + /* read oobdata */ + this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + + /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ + if (!compareecc) + goto readoob; + + /* Pick the ECC bytes out of the oob data */ + for (j = 0; j < oobsel->eccbytes; j++) + ecc_code[j] = oob_data[oob_config[j]]; + + /* correct data, if neccecary */ + for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + + /* Get next chunk of ecc bytes */ + j += eccbytes; + + /* Check, if we have a fs supplied oob-buffer, + * This is the legacy mode. Used by YAFFS1 + * Should go away some day + */ + if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { + int *p = (int *)(&oob_data[mtd->oobsize]); + p[i] = ecc_status; + } + + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + ecc_failed++; + } + } + + readoob: + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + /* without autoplace. Legacy mode used by YAFFS1 */ + switch(oobsel->useecc) { + case MTD_NANDECC_AUTOPLACE: + /* Walk through the autoplace chunks */ + for (i = 0, j = 0; j < mtd->oobavail; i++) { + int from = oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy(&oob_buf[oob], &oob_data[from], num); + j+= num; + } + oob += mtd->oobavail; + break; + case MTD_NANDECC_PLACE: + /* YAFFS1 legacy mode */ + oob_data += this->eccsteps * sizeof (int); + default: + oob_data += mtd->oobsize; + } + } + readdata: + /* Partial page read, transfer data into fs buffer */ + if (!aligned) { + for (j = col; j < end && read < len; j++) + buf[read++] = data_poi[j]; + this->pagebuf = realpage; + } else + read += mtd->oobblock; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + if (read == len) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + if (flags & NAND_GET_DEVICE) + nand_release_device(mtd); + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + *retlen = read; + return ecc_failed ? -EBADMSG : 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * NAND read out-of-band data from the spare area + */ +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, chipnr; + struct nand_chip *this = mtd->priv; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + 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); + chipnr = (int)(from >> this->chip_shift); + + /* Mask to get column */ + col = from & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); + /* + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ + i = 0; + while (i < len) { + int thislen = mtd->oobsize - col; + thislen = min_t(int, thislen, len); + this->read_buf(mtd, &buf[i], thislen); + i += thislen; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* Read more ? */ + if (i < len) { + page++; + col = 0; + + /* Check, if we cross a chip boundary */ + if (!(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { + /* For subsequent page reads set offset to 0 */ + this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); + } + } + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Return happy */ + *retlen = len; + return 0; +} + +/** + * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @from: offset to read from + * @len: number of bytes to read + * @ooblen: number of oob data bytes to read + * + * Read raw data including oob into buffer + */ +int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) +{ + struct nand_chip *this = mtd->priv; + int page = (int) (from >> this->page_shift); + int chip = (int) (from >> this->chip_shift); + int sndcmd = 1; + int cnt = 0; + int pagesize = mtd->oobblock + mtd->oobsize; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + this->select_chip (mtd, chip); + + /* Add requested oob length */ + len += ooblen; + + while (len) { + if (sndcmd) + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); + sndcmd = 0; + + this->read_buf (mtd, &buf[cnt], pagesize); + + len -= pagesize; + cnt += pagesize; + page++; + + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + return 0; +} + + +/** + * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer + * @mtd: MTD device structure + * @fsbuf: buffer given by fs driver + * @oobsel: out of band selection structre + * @autoplace: 1 = place given buffer into the oob bytes + * @numpages: number of pages to prepare + * + * Return: + * 1. Filesystem buffer available and autoplacement is off, + * return filesystem buffer + * 2. No filesystem buffer or autoplace is off, return internal + * buffer + * 3. Filesystem buffer is given and autoplace selected + * put data from fs buffer into internal buffer and + * retrun internal buffer + * + * Note: The internal buffer is filled with 0xff. This must + * be done only once, when no autoplacement happens + * Autoplacement sets the buffer dirty flag, which + * forces the 0xff fill before using the buffer again. + * +*/ +static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, + int autoplace, int numpages) +{ + struct nand_chip *this = mtd->priv; + int i, len, ofs; + + /* Zero copy fs supplied buffer */ + if (fsbuf && !autoplace) + return fsbuf; + + /* Check, if the buffer must be filled with ff again */ + if (this->oobdirty) { + memset (this->oob_buf, 0xff, + mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + this->oobdirty = 0; + } + + /* If we have no autoplacement or no fs buffer use the internal one */ + if (!autoplace || !fsbuf) + return this->oob_buf; + + /* Walk through the pages and place the data */ + this->oobdirty = 1; + ofs = 0; + while (numpages--) { + for (i = 0, len = 0; len < mtd->oobavail; i++) { + int to = ofs + oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy (&this->oob_buf[to], fsbuf, num); + len += num; + fsbuf += num; + } + ofs += mtd->oobavail; + } + return this->oob_buf; +} + +#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 + +/** + * nand_write - [MTD Interface] compability function for nand_write_ecc + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL + * +*/ +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); +} + +/** + * nand_write_ecc - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * 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 * eccbuf, struct nand_oobinfo *oobsel) +{ + int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; + int autoplace = 0, numpages, totalpages; + struct nand_chip *this = mtd->priv; + u_char *oobbuf, *bufstart; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + *retlen = 0; + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Calculate chipnr */ + chipnr = (int)(to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + + /* Setup variables and oob buffer */ + totalpages = len >> this->page_shift; + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) + this->pagebuf = -1; + + /* Set it relative to chip */ + page &= this->pagemask; + startpage = page; + /* Calc number of pages we can write in one go */ + numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); + bufstart = (u_char *)buf; + + /* Loop until all data is written */ + while (written < len) { + + this->data_poi = (u_char*) &buf[written]; + /* Write one page. If this is the last page to write + * or the last page in this block, then use the + * real pageprogram command, else select cached programming + * if supported by the chip. + */ + ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); + goto out; + } + /* Next oob page */ + oob += mtd->oobsize; + /* Update written bytes count */ + written += mtd->oobblock; + if (written == len) + goto cmp; + + /* Increment page address */ + page++; + + /* Have we hit a block boundary ? Then we have to verify and + * if verify is ok, we have to setup the oob buffer for + * the next pages. + */ + if (!(page & (ppblock - 1))){ + int ofs; + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, + page - startpage, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + goto out; + } + *retlen = written; + + ofs = autoplace ? mtd->oobavail : mtd->oobsize; + if (eccbuf) + eccbuf += (page - startpage) * ofs; + totalpages -= page - startpage; + numpages = min (totalpages, ppblock); + page &= this->pagemask; + startpage = page; + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, + autoplace, numpages); + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + } + /* Verify the remaining pages */ +cmp: + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, totalpages, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (!ret) + *retlen = written; + else + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + + +/** + * nand_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * 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 column, page, status, ret = -EIO, chipnr; + struct nand_chip *this = mtd->priv; + + 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); + chipnr = (int) (to >> this->chip_shift); + + /* Mask to get column */ + column = to & (mtd->oobsize - 1); + + /* 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; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Reset the chip. Some chips (like the Toshiba TC5832DC found + in one of my DiskOnChip 2000 test units) will clear the whole + data page too if we don't do this. I have no clue why, but + I seem to have 'fixed' it in the doc2000 driver in + August 1999. dwmw2. */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == this->pagebuf) + this->pagebuf = -1; + + if (NAND_MUST_PAD(this)) { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); + /* prepad 0xff for partial programming */ + this->write_buf(mtd, ffchars, column); + /* write data */ + this->write_buf(mtd, buf, len); + /* postpad 0xff for partial programming */ + this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); + } else { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); + /* write data */ + this->write_buf(mtd, buf, len); + } + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + ret = -EIO; + goto out; + } + /* Return happy */ + *retlen = len; + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); + + if (this->verify_buf(mtd, buf, len)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; + } +#endif + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + + +/** + * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * + * NAND write with kvec. This just calls the ecc function + */ +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen) +{ + return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); +} + +/** + * nand_writev_ecc - [MTD Interface] write with iovec with ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND write with iovec with ecc + */ +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + int i, page, len, total_len, ret = -EIO, written = 0, chipnr; + int oob, numpages, autoplace = 0, startpage; + struct nand_chip *this = mtd->priv; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + u_char *oobbuf, *bufstart; + + /* Preset written len for early exit */ + *retlen = 0; + + /* 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, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); + + /* 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; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(total_len)) { + printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Get the current chip-nr */ + chipnr = (int) (to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + + /* Setup start page */ + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) + this->pagebuf = -1; + + startpage = page & this->pagemask; + + /* Loop until all kvec' data has been written */ + len = 0; + while (count) { + /* If the given tuple is >= pagesize then + * write it out from the iov + */ + if ((vecs->iov_len - len) >= mtd->oobblock) { + /* Calc number of pages we can write + * out of this iov in one go */ + numpages = (vecs->iov_len - len) >> this->page_shift; + /* Do not cross block boundaries */ + numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + bufstart = (u_char *)vecs->iov_base; + bufstart += len; + this->data_poi = bufstart; + oob = 0; + for (i = 1; i <= numpages; i++) { + /* Write one page. If this is the last page to write + * then use the real pageprogram command, else select + * cached programming if supported by the chip. + */ + ret = nand_write_page (mtd, this, page & this->pagemask, + &oobbuf[oob], oobsel, i != numpages); + if (ret) + goto out; + this->data_poi += mtd->oobblock; + len += mtd->oobblock; + oob += mtd->oobsize; + page++; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } else { + /* We must use the internal buffer, read data out of each + * tuple until we have a full page to write + */ + int cnt = 0; + while (cnt < mtd->oobblock) { + if (vecs->iov_base != NULL && vecs->iov_len) + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + this->pagebuf = page; + this->data_poi = this->data_buf; + bufstart = this->data_poi; + numpages = 1; + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + ret = nand_write_page (mtd, this, page & this->pagemask, + oobbuf, oobsel, 0); + if (ret) + goto out; + page++; + } + + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); + if (ret) + goto out; + + written += mtd->oobblock * numpages; + /* All done ? */ + if (!count) + break; + + startpage = page & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!startpage) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + *retlen = written; + return ret; +} + +/** + * single_erease_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips + */ +static void single_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * multi_erease_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function + * Erase 4 consecutive blocks + */ +static void multi_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks + */ +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand (mtd, instr, 0); +} + +#define BBT_PAGE_MASK 0xffffff3f +/** + * nand_erase_intern - [NAND Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks + */ +int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +{ + int page, len, status, pages_per_block, ret, chipnr; + struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ + + 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 & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << this->phys_erase_shift) - 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; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int) (instr->addr >> this->page_shift); + chipnr = (int) (instr->addr >> this->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check the WP bit */ + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ + if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { + printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Invalidate the page cache, if we erase the block which contains + the current cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) + this->pagebuf = -1; + + this->erase_cmd (mtd, page & this->pagemask); + + status = this->waitfunc (mtd, this, FL_ERASING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = (page << this->page_shift); + goto erase_exit; + } + + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } + + /* Increment page address and decrement length */ + len -= (1 << this->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function + */ +static void nand_sync (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device (mtd); +} + + +/** + * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + return nand_block_checkbad (mtd, ofs, 1, 0); +} + +/** + * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + int ret; + + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return this->block_markbad(mtd, ofs); +} + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. Buffers are allocated if + * they are not provided by the board driver + * + */ +int nand_scan (struct mtd_info *mtd, int maxchips) +{ + int i, j, nand_maf_id, nand_dev_id, busw, maf_id; + struct nand_chip *this = mtd->priv; + + /* Get buswidth to select the correct functions*/ + busw = this->options & NAND_BUSWIDTH_16; + + /* check for proper chip_delay setup, set 20us if not */ + if (!this->chip_delay) + this->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (this->cmdfunc == NULL) + this->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + + if (!this->select_chip) + this->select_chip = nand_select_chip; + if (!this->write_byte) + this->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!this->read_byte) + this->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!this->write_word) + this->write_word = nand_write_word; + if (!this->read_word) + this->read_word = nand_read_word; + if (!this->block_bad) + this->block_bad = nand_block_bad; + if (!this->block_markbad) + this->block_markbad = nand_default_block_markbad; + if (!this->write_buf) + this->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!this->read_buf) + this->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!this->verify_buf) + this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; + if (!this->scan_bbt) + this->scan_bbt = nand_default_bbt; + + /* Select the device */ + this->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + nand_maf_id = this->read_byte(mtd); + nand_dev_id = this->read_byte(mtd); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + + if (nand_dev_id != nand_flash_ids[i].id) + continue; + + if (!mtd->name) mtd->name = nand_flash_ids[i].name; + this->chipsize = nand_flash_ids[i].chipsize << 20; + + /* New devices have all the information in additional id bytes */ + if (!nand_flash_ids[i].pagesize) { + int extid; + /* The 3rd id byte contains non relevant data ATM */ + extid = this->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = this->read_byte(mtd); + /* Calc pagesize */ + mtd->oobblock = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* Old devices have this data hardcoded in the + * device id table */ + mtd->erasesize = nand_flash_ids[i].erasesize; + mtd->oobblock = nand_flash_ids[i].pagesize; + mtd->oobsize = mtd->oobblock / 32; + busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; + } + + /* Try to identify manufacturer */ + for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { + if (nand_manuf_ids[maf_id].id == nand_maf_id) + break; + } + + /* Check, if buswidth is correct. Hardware drivers should set + * this correct ! */ + if (busw != (this->options & NAND_BUSWIDTH_16)) { + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[maf_id].name , mtd->name); + printk (KERN_WARNING + "NAND bus width %d instead %d bit\n", + (this->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + this->select_chip(mtd, -1); + return 1; + } + + /* Calculate the address shift from the page size */ + this->page_shift = ffs(mtd->oobblock) - 1; + this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + + /* Set the bad block position */ + this->badblockpos = mtd->oobblock > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + this->options &= ~NAND_CHIPOPTIONS_MSK; + this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; + /* Set this as a default. Board drivers can override it, if neccecary */ + this->options |= NAND_NO_AUTOINCR; + /* Check if this is a not a samsung device. Do not clear the options + * for chips which are not having an extended id. + */ + if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) + this->options &= ~NAND_SAMSUNG_LP_OPTIONS; + + /* Check for AND chips with 4 page planes */ + if (this->options & NAND_4PAGE_ARRAY) + this->erase_cmd = multi_erase_cmd; + else + this->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function ! */ + if (mtd->oobblock > 512 && this->cmdfunc == nand_command) + this->cmdfunc = nand_command_lp; + + printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); + break; + } + + if (!nand_flash_ids[i].name) { + printk (KERN_WARNING "No NAND device found!!!\n"); + this->select_chip(mtd, -1); + return 1; + } + + for (i=1; i < maxchips; i++) { + this->select_chip(mtd, i); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + if (nand_maf_id != this->read_byte(mtd) || + nand_dev_id != this->read_byte(mtd)) + break; + } + if (i > 1) + printk(KERN_INFO "%d NAND chips detected\n", i); + + /* Allocate buffers, if neccecary */ + if (!this->oob_buf) { + size_t len; + len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); + this->oob_buf = kmalloc (len, GFP_KERNEL); + if (!this->oob_buf) { + printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); + return -ENOMEM; + } + this->options |= NAND_OOBBUF_ALLOC; + } + + if (!this->data_buf) { + size_t len; + len = mtd->oobblock + mtd->oobsize; + this->data_buf = kmalloc (len, GFP_KERNEL); + if (!this->data_buf) { + if (this->options & NAND_OOBBUF_ALLOC) + kfree (this->oob_buf); + printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); + return -ENOMEM; + } + this->options |= NAND_DATABUF_ALLOC; + } + + /* Store the number of chips and calc total size for mtd */ + this->numchips = i; + mtd->size = i * this->chipsize; + /* Convert chipsize to number of pages per chip -1. */ + this->pagemask = (this->chipsize >> this->page_shift) - 1; + /* Preset the internal oob buffer */ + memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + + /* If no default placement scheme is given, select an + * appropriate one */ + if (!this->autooob) { + /* Select the appropriate default oob placement scheme for + * placement agnostic filesystems */ + switch (mtd->oobsize) { + case 8: + this->autooob = &nand_oob_8; + break; + case 16: + this->autooob = &nand_oob_16; + break; + case 64: + this->autooob = &nand_oob_64; + break; + default: + printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + BUG(); + } + } + + /* The number of bytes available for the filesystem to place fs dependend + * oob data */ + if (this->options & NAND_BUSWIDTH_16) { + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); + if (this->autooob->eccbytes & 0x01) + mtd->oobavail--; + } else + mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); + + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize + * fallback to software ECC + */ + this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; + + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + if (mtd->oobblock == 256) { + printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 512; /* set eccsize to 512 */ + break; + + case NAND_ECC_HW3_256: + break; + + case NAND_ECC_NONE: + printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); + this->eccmode = NAND_ECC_NONE; + break; + + case NAND_ECC_SOFT: + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + BUG(); + } + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + BUG(); + } + + mtd->eccsize = this->eccsize; + + /* Set the number of read / write steps for one page to ensure ECC generation */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + this->eccsteps = mtd->oobblock / 512; + break; + case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + this->eccsteps = mtd->oobblock / 256; + break; + + case NAND_ECC_NONE: + this->eccsteps = 1; + break; + } + + /* Initialize state, waitqueue and spinlock */ + this->state = FL_READY; + init_waitqueue_head (&this->wq); + spin_lock_init (&this->chip_lock); + + /* De-select the device */ + this->select_chip(mtd, -1); + + /* Invalidate the pagebuffer reference */ + this->pagebuf = -1; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + 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->writev_ecc = nand_writev_ecc; + mtd->sync = nand_sync; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; + mtd->block_isbad = nand_block_isbad; + mtd->block_markbad = nand_block_markbad; + + /* and make the autooob the default one */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + + mtd->owner = THIS_MODULE; + + /* Check, if we should skip the bad block table scan */ + if (this->options & NAND_SKIP_BBTSCAN) + return 0; + + /* Build bad block table */ + return this->scan_bbt (mtd); +} + +/** + * nand_release - [NAND Interface] Free resources held by the NAND device + * @mtd: MTD device structure +*/ +void nand_release (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions (mtd); +#endif + /* Deregister the device */ + del_mtd_device (mtd); + + /* Free bad block table memory, if allocated */ + if (this->bbt) + kfree (this->bbt); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_OOBBUF_ALLOC) + kfree (this->oob_buf); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_DATABUF_ALLOC) + kfree (this->data_buf); +} + +EXPORT_SYMBOL (nand_scan); +EXPORT_SYMBOL (nand_release); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); +MODULE_DESCRIPTION ("Generic NAND flash driver code"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/nand_bbt.c @@ -0,0 +1,1081 @@ +/* + * drivers/mtd/nand_bbt.c + * + * Overview: + * Bad block table support for the NAND driver + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_bbt.c,v 1.31 2005/02/16 17:09:36 dedekind 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. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the bbt descriptor(s). If a bbt is found + * then the contents are read and the memory based bbt is created. If a + * mirrored bbt is selected then the mirror is searched too and the + * versions are compared. If the mirror has a greater version number + * than the mirror bbt is used to build the memory based bbt. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the bbt's is out of date or does not exist it is (re)created. + * If no bbt exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created bbts like the one found on M-SYS DOC devices + * the bbt is searched and read but never created + * + * The autogenerated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the oob area with an ident pattern and a version + * number which indicates which of both tables is more up to date. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space neccecary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. + * If the SCAN_EMPTY option is set then check, if all bytes except the + * pattern area contain 0xff + * +*/ +static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i, end = 0; + uint8_t *p = buf; + + if (td->options & NAND_BBT_SCANEMPTY) { + end = paglen + td->offs; + for (i = 0; i < end; i++) { + if (p[i] != 0xff) + return -1; + } + p += end; + } + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; + for (i = end; i < len; i++) { + if (*p++ != 0xff) + return -1; + } + } + return 0; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @bits: number of bits per block + * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks + * + * Read the bad block table starting from page. + * + */ +static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, + int bits, int offs, int reserved_block_code) +{ + int res, i, j, act = 0; + struct nand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + uint8_t msk = (uint8_t) ((1 << bits) - 1); + + totlen = (num * bits) >> 3; + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); + res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); + if (res < 0) { + if (retlen != len) { + printk (KERN_INFO "nand_bbt: Error reading bad block table\n"); + return res; + } + printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && + (tmp == reserved_block_code)) { + printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + continue; + } + /* Leave it for now, if its matured we can move this + * message to MTD_DEBUG_LEVEL0 */ + printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + /* Factory marked bad or worn out ? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + } + } + totlen -= len; + from += len; + } + return 0; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. +*/ +static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd->priv; + int res = 0, i; + int bits; + + bits = td->options & NAND_BBT_NRBITS_MSK; + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + if (res) + return res; + offs += this->chipsize >> (this->bbt_erase_shift + 2); + } + } else { + res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); + if (res) + return res; + } + return 0; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + * +*/ +static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd->priv; + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + td->version[0] = buf[mtd->oobblock + td->veroffs]; + printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + md->version[0] = buf[mtd->oobblock + md->veroffs]; + printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); + } + + return 1; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd->priv; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + + printk (KERN_INFO "Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCANALLPAGES) + len = 1 << (this->bbt_erase_shift - this->page_shift); + else { + if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; + } + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + } + + if (chip == -1) { + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it + * makes shifting and masking less painful */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; + } + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = startblock << (this->bbt_erase_shift - 1); + } + + for (i = startblock; i < numblocks;) { + int ret; + + if (bd->options & NAND_BBT_SCANEMPTY) + if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) + return ret; + + for (j = 0; j < len; j++) { + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + size_t retlen; + + /* No need to read pages fully, just read required OOB bytes */ + ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, + readlen, &retlen, &buf[0]); + if (ret) + return ret; + } + if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } + } + i += 2; + from += (1 << this->bbt_erase_shift); + } + + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. + * Search is preformed either from the beginning up or from the end of + * the device downwards. The search starts always at the start of a + * block. + * If the option NAND_BBT_PERCHIP is given, each chip is searched + * for a bbt, which contains the bad block information of this chip. + * This is neccecary to provide support for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page + * in a block. + */ +static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, chips; + int bits, startblock, block, dir; + int scanlen = mtd->oobblock + mtd->oobsize; + int bbtblocks; + + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) -1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + /* Read first page */ + nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); + if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { + td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + if (td->options & NAND_BBT_VERSION) { + td->version[i] = buf[mtd->oobblock + td->veroffs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + printk (KERN_WARNING "Bad block table not found for chip %d\n", i); + else + printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s) +*/ +static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt (mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt (mtd, buf, md); + + /* Force result check */ + return 1; +} + + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table + * +*/ +static int write_bbt (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) +{ + struct nand_chip *this = mtd->priv; + struct nand_oobinfo oobinfo; + struct erase_info einfo; + int i, j, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, bbtoffs, pageoffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device ? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int) (this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip ? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int) (mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + + /* There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* Automatic placement of the bad block table */ + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + printk (KERN_ERR "No space left to write bad block table\n"); + return -ENOSPC; +write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; + default: return -EINVAL; + } + + bbtoffs = chip * (numblocks >> 2); + + to = ((loff_t) page) << this->page_shift; + + memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); + oobinfo.useecc = MTD_NANDECC_PLACEONLY; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + if (retlen != len) { + printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); + return res; + } + printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); + } + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); + /* Preset the bbt's oob area with 0xff */ + memset (&buf[len + pageoffs * mtd->oobsize], 0xff, + ((len >> this->page_shift) - pageoffs) * mtd->oobsize); + if (td->options & NAND_BBT_VERSION) { + buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; + } + } else { + /* Calc length */ + len = (size_t) (numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); + /* Preset the buffer with 0xff */ + memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); + offs = 0; + /* Pattern is located in oob area of first page */ + memcpy (&buf[len + td->offs], td->pattern, td->len); + if (td->options & NAND_BBT_VERSION) { + buf[len + td->veroffs] = td->version[chip]; + } + } + + /* walk through the memory table */ + for (i = 0; i < numblocks; ) { + uint8_t dat; + dat = this->bbt[bbtoffs + (i >> 2)]; + for (j = 0; j < 4; j++ , i++) { + int sftcnt = (i << (3 - sft)) & sftmsk; + /* Do not store the reserved bbt blocks ! */ + buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset (&einfo, 0, sizeof (einfo)); + einfo.mtd = mtd; + einfo.addr = (unsigned long) to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand (mtd, &einfo, 1); + if (res < 0) { + printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); + return res; + } + + res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); + return res; + } + printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", + (unsigned int) to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks +*/ +static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt (mtd, this->data_buf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if neccecary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt + * and creates / updates the bbt(s) if neccecary + * Creation is neccecary if no bbt was found for the chip/device + * Update is neccecary if one of the tables is missing or the + * version nr. of one table is less than the other +*/ +static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, chipsel, res; + struct nand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + rd = NULL; + rd2 = NULL; + /* Per chip or per device ? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table avilable ? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + writeops = 0x03; + goto create; + } + + if (td->pages[i] == -1) { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + goto writecheck; + } + + if (md->pages[i] == -1) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + goto writecheck; + } + + if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + goto writecheck; + } + + if (((int8_t) (td->version[i] - md->version[i])) > 0) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + } else { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + } + + goto writecheck; + + } else { + if (td->pages[i] == -1) { + writeops = 0x01; + goto create; + } + rd = td; + goto writecheck; + } +create: + /* Create the bad block table by scanning the device ? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + create_bbt (mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; +writecheck: + /* read back first ? */ + if (rd) + read_abs_bbt (mtd, buf, rd, chipsel); + /* If they weren't versioned, read both. */ + if (rd2) + read_abs_bbt (mtd, buf, rd2, chipsel); + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. +*/ +static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, j, chips, block, nrblocks, update; + uint8_t oldval, newval; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) update = 1; + block += 2; + } + /* If we want reserved blocks to be recorded to flash, and some + new ones have been marked, then we need to update the stored + bbts. This should only happen once. */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + } +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * +*/ +int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate memory (2bit per block) */ + this->bbt = kmalloc (len, GFP_KERNEL); + if (!this->bbt) { + printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); + return -ENOMEM; + } + /* Clear the memory bad block table */ + memset (this->bbt, 0x00, len); + + /* If no primary table decriptor is given, scan the device + * to build a memory based bad block table + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree (this->bbt); + this->bbt = NULL; + } + return res; + } + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "nand_bbt: Out of memory\n"); + kfree (this->bbt); + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page ? */ + if (td->options & NAND_BBT_ABSPAGE) { + res = read_abs_bbts (mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + res = search_read_bbts (mtd, buf, td, md); + } + + if (res) + res = check_create (mtd, buf, bd); + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region (mtd, td); + if (md) + mark_bbt_region (mtd, md); + + kfree (buf); + return res; +} + + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s) +*/ +int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "nand_update_bbt: Out of memory\n"); + return -ENOMEM; + } + + writeops = md != NULL ? 0x03 : 0x01; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int) (offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + } + +out: + kfree (buf); + return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr smallpage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the nand_scan_bbt function + * +*/ +int nand_default_bbt (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Default for AG-AND. We must use a flash based + * bad block table as the devices have factory marked + * _good_ blocks. Erasing those blocks leads to loss + * of the good / bad information, so we _must_ store + * this information in a good / bad table during + * startup + */ + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + this->options |= NAND_USE_FLASH_BBT; + return nand_scan_bbt (mtd, &agand_flashbased); + } + + + /* Is a flash based bad block table requested ? */ + if (this->options & NAND_USE_FLASH_BBT) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + } + return nand_scan_bbt (mtd, this->badblock_pattern); +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + * +*/ +int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int) (offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + + switch ((int)res) { + case 0x00: return 0; + case 0x01: return 1; + case 0x02: return allowbbt ? 0 : 1; + } + return 1; +} + +EXPORT_SYMBOL (nand_scan_bbt); +EXPORT_SYMBOL (nand_default_bbt); --- linux-2.4.21/drivers/mtd/nand/nand_ecc.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/nand_ecc.c @@ -1,22 +1,44 @@ /* - * drivers/mtd/nand_ecc.c + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. * - * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) * Toshiba America Electronics Components, Inc. * - * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $ + * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 2.1 as published by the Free Software Foundation. + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. * - * This file contains an ECC algorithm from Toshiba that detects and - * corrects 1 bit errors in a 256 byte block of data. + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. */ #include #include #include +#include /* * Pre-calculated 256-way 1 byte column parity @@ -41,7 +63,12 @@ }; -/* +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * * Creates non-inverted ECC code from line parity */ static void nand_trans_result(u_char reg2, u_char reg3, @@ -81,10 +108,13 @@ ecc_code[1] = tmp2; } -/* - * Calculate 3 byte ECC code for 256 byte block +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC */ -void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { u_char idx, reg1, reg2, reg3; int j; @@ -114,12 +144,19 @@ ecc_code[0] = ~ecc_code[0]; ecc_code[1] = ~ecc_code[1]; ecc_code[2] = ((~reg1) << 2) | 0x03; + return 0; } -/* +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * * 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) +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { u_char a, b, c, d1, d2, d3, add, bit, i; @@ -209,5 +246,5 @@ EXPORT_SYMBOL(nand_correct_data); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill "); +MODULE_AUTHOR("Steven J. Hill "); MODULE_DESCRIPTION("Generic NAND ECC support"); --- linux-2.4.21/drivers/mtd/nand/nand_ids.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/nand_ids.c @@ -3,8 +3,7 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * - * $Id: nand_ids.c,v 1.1 2002/12/02 22:06:04 gleixner Exp $ + * $Id: nand_ids.c,v 1.12 2005/02/16 09:33:27 gleixner 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 @@ -13,26 +12,97 @@ */ #include #include - /* * Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { - {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V - {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V - {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V - {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V - {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V - {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V - {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V - {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V - {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V - {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V - {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V - {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V - {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V - {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V - {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + /* These are the new chips with large page size. The pagesize + * and the erasesize is determined from the extended id bytes + */ + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! + * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes + * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 + * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go + * There are more speed improvements for reads and writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, + {NULL,} }; @@ -44,10 +114,11 @@ {NAND_MFR_SAMSUNG, "Samsung"}, {NAND_MFR_FUJITSU, "Fujitsu"}, {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, {0x0, "Unknown"} }; - EXPORT_SYMBOL (nand_manuf_ids); EXPORT_SYMBOL (nand_flash_ids); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/nandsim.c @@ -0,0 +1,1613 @@ +/* + * NAND flash simulator. + * + * Author: Artem B. Bityuckiy , + * + * Copyright (C) 2004 Nokia Corporation + * + * Note: NS means "NAND Simulator". + * Note: Input means input TO flash chip, output means output FROM chip. + * + * 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, 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: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_NS_ABS_POS +#include +#endif + + +/* Default simulator parameters values */ +#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) +#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 +#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 +#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ +#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ +#endif + +#ifndef CONFIG_NANDSIM_ACCESS_DELAY +#define CONFIG_NANDSIM_ACCESS_DELAY 25 +#endif +#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY +#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 +#endif +#ifndef CONFIG_NANDSIM_ERASE_DELAY +#define CONFIG_NANDSIM_ERASE_DELAY 2 +#endif +#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE +#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 +#endif +#ifndef CONFIG_NANDSIM_INPUT_CYCLE +#define CONFIG_NANDSIM_INPUT_CYCLE 50 +#endif +#ifndef CONFIG_NANDSIM_BUS_WIDTH +#define CONFIG_NANDSIM_BUS_WIDTH 8 +#endif +#ifndef CONFIG_NANDSIM_DO_DELAYS +#define CONFIG_NANDSIM_DO_DELAYS 0 +#endif +#ifndef CONFIG_NANDSIM_LOG +#define CONFIG_NANDSIM_LOG 0 +#endif +#ifndef CONFIG_NANDSIM_DBG +#define CONFIG_NANDSIM_DBG 0 +#endif + +static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; +static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; +static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE; +static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE; +static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; +static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; +static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; +static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; +static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; +static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; +static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; +static uint log = CONFIG_NANDSIM_LOG; +static uint dbg = CONFIG_NANDSIM_DBG; + +module_param(first_id_byte, uint, 0400); +module_param(second_id_byte, uint, 0400); +module_param(third_id_byte, uint, 0400); +module_param(fourth_id_byte, uint, 0400); +module_param(access_delay, uint, 0400); +module_param(programm_delay, uint, 0400); +module_param(erase_delay, uint, 0400); +module_param(output_cycle, uint, 0400); +module_param(input_cycle, uint, 0400); +module_param(bus_width, uint, 0400); +module_param(do_delays, uint, 0400); +module_param(log, uint, 0400); +module_param(dbg, uint, 0400); + +MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); +MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); +MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); +MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); +MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); +MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); +MODULE_PARM_DESC(log, "Perform logging if not zero"); +MODULE_PARM_DESC(dbg, "Output debug information if not zero"); + +/* The largest possible page size */ +#define NS_LARGEST_PAGE_SIZE 2048 + +/* The prefix for simulator output */ +#define NS_OUTPUT_PREFIX "[nandsim]" + +/* Simulator's output macros (logging, debugging, warning, error) */ +#define NS_LOG(args...) \ + do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) +#define NS_DBG(args...) \ + do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) +#define NS_WARN(args...) \ + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) +#define NS_ERR(args...) \ + do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) + +/* Busy-wait delay macros (microseconds, milliseconds) */ +#define NS_UDELAY(us) \ + do { if (do_delays) udelay(us); } while(0) +#define NS_MDELAY(us) \ + do { if (do_delays) mdelay(us); } while(0) + +/* Is the nandsim structure initialized ? */ +#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) + +/* Good operation completion status */ +#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) + +/* Operation failed completion status */ +#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) + +/* Calculate the page offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET(ns) \ + (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + +/* Calculate the OOB offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) + +/* After a command is input, the simulator goes to one of the following states */ +#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ +#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ +#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ +#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */ +#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ +#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ +#define STATE_CMD_STATUS 0x00000007 /* read status */ +#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */ +#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */ +#define STATE_CMD_READID 0x0000000A /* read ID */ +#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ +#define STATE_CMD_RESET 0x0000000C /* reset */ +#define STATE_CMD_MASK 0x0000000F /* command states mask */ + +/* After an addres is input, the simulator goes to one of these states */ +#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ +#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ +#define STATE_ADDR_ZERO 0x00000030 /* one byte zero address was accepted */ +#define STATE_ADDR_MASK 0x00000030 /* address states mask */ + +/* Durind data input/output the simulator is in these states */ +#define STATE_DATAIN 0x00000100 /* waiting for data input */ +#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ + +#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ +#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ +#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ +#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ +#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ + +/* Previous operation is done, ready to accept new requests */ +#define STATE_READY 0x00000000 + +/* This state is used to mark that the next state isn't known yet */ +#define STATE_UNKNOWN 0x10000000 + +/* Simulator's actions bit masks */ +#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ +#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */ +#define ACTION_SECERASE 0x00300000 /* erase sector */ +#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ +#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ +#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ +#define ACTION_MASK 0x00700000 /* action mask */ + +#define NS_OPER_NUM 12 /* Number of operations supported by the simulator */ +#define NS_OPER_STATES 6 /* Maximum number of states in operation */ + +#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ +#define OPT_PAGE256 0x00000001 /* 256-byte page chips */ +#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ +#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ +#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ +#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ +#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ +#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ +#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ + +/* Remove action bits ftom state */ +#define NS_STATE(x) ((x) & ~ACTION_MASK) + +/* + * Maximum previous states which need to be saved. Currently saving is + * only needed for page programm operation with preceeded read command + * (which is only valid for 512-byte pages). + */ +#define NS_MAX_PREVSTATES 1 + +/* + * The structure which describes all the internal simulator data. + */ +struct nandsim { + struct mtd_partition part; + + uint busw; /* flash chip bus width (8 or 16) */ + u_char ids[4]; /* chip's ID bytes */ + uint32_t options; /* chip's characteristic bits */ + uint32_t state; /* current chip state */ + uint32_t nxstate; /* next expected state */ + + uint32_t *op; /* current operation, NULL operations isn't known yet */ + uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ + uint16_t npstates; /* number of previous states saved */ + uint16_t stateidx; /* current state index */ + + /* The simulated NAND flash image */ + union flash_media { + u_char *byte; + uint16_t *word; + } mem; + + /* Internal buffer of page + OOB size bytes */ + union internal_buffer { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ + } buf; + + /* NAND flash "geometry" */ + struct nandsin_geometry { + uint32_t totsz; /* total flash size, bytes */ + uint32_t secsz; /* flash sector (erase block) size, bytes */ + uint pgsz; /* NAND flash page size, bytes */ + uint oobsz; /* page OOB area size, bytes */ + uint32_t totszoob; /* total flash size including OOB, bytes */ + uint pgszoob; /* page size including OOB , bytes*/ + uint secszoob; /* sector size including OOB, bytes */ + uint pgnum; /* total number of pages */ + uint pgsec; /* number of pages per sector */ + uint secshift; /* bits number in sector size */ + uint pgshift; /* bits number in page size */ + uint oobshift; /* bits number in OOB size */ + uint pgaddrbytes; /* bytes per page address */ + uint secaddrbytes; /* bytes per sector address */ + uint idbytes; /* the number ID bytes that this chip outputs */ + } geom; + + /* NAND flash internal registers */ + struct nandsim_regs { + unsigned command; /* the command register */ + u_char status; /* the status register */ + uint row; /* the page number */ + uint column; /* the offset within page */ + uint count; /* internal counter */ + uint num; /* number of bytes which must be processed */ + uint off; /* fixed page offset */ + } regs; + + /* NAND flash lines state */ + struct ns_lines_status { + int ce; /* chip Enable */ + int cle; /* command Latch Enable */ + int ale; /* address Latch Enable */ + int wp; /* write Protect */ + } lines; +}; + +/* + * Operations array. To perform any operation the simulator must pass + * through the correspondent states chain. + */ +static struct nandsim_operations { + uint32_t reqopts; /* options which are required to perform the operation */ + uint32_t states[NS_OPER_STATES]; /* operation's states */ +} ops[NS_OPER_NUM] = { + /* Read page + OOB from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read page + OOB from the second half */ + {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, + STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the second half */ + {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Erase sector */ + {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, + /* Read status */ + {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, + /* Read multi-plane status */ + {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, + /* Read ID */ + {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, + /* Large page devices read page */ + {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, + STATE_DATAOUT, STATE_READY}} +}; + +/* MTD structure for NAND controller */ +static struct mtd_info *nsmtd; + +static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; + +/* + * Initialize the nandsim structure. + * + * RETURNS: 0 if success, -ERRNO if failure. + */ +static int +init_nandsim(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct nandsim *ns = (struct nandsim *)(chip->priv); + int i; + + if (NS_IS_INITIALIZED(ns)) { + NS_ERR("init_nandsim: nandsim is already initialized\n"); + return -EIO; + } + + /* Force mtd to not do delays */ + chip->chip_delay = 0; + + /* Initialize the NAND flash parameters */ + ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; + ns->geom.totsz = mtd->size; + ns->geom.pgsz = mtd->oobblock; + ns->geom.oobsz = mtd->oobsize; + ns->geom.secsz = mtd->erasesize; + ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; + ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; + ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; + ns->geom.secshift = ffs(ns->geom.secsz) - 1; + ns->geom.pgshift = chip->page_shift; + ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; + ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; + ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; + ns->options = 0; + + if (ns->geom.pgsz == 256) { + ns->options |= OPT_PAGE256; + } + else if (ns->geom.pgsz == 512) { + ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + if (ns->busw == 8) + ns->options |= OPT_PAGE512_8BIT; + } else if (ns->geom.pgsz == 2048) { + ns->options |= OPT_PAGE2048; + } else { + NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); + return -EIO; + } + + if (ns->options & OPT_SMALLPAGE) { + if (ns->geom.totsz < (64 << 20)) { + ns->geom.pgaddrbytes = 3; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 4; + ns->geom.secaddrbytes = 3; + } + } else { + if (ns->geom.totsz <= (128 << 20)) { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 3; + } + } + + /* Detect how many ID bytes the NAND chip outputs */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (second_id_byte != nand_flash_ids[i].id) + continue; + if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) + ns->options |= OPT_AUTOINCR; + } + + if (ns->busw == 16) + NS_WARN("16-bit flashes support wasn't tested\n"); + + printk("flash size: %u MiB\n", ns->geom.totsz >> 20); + printk("page size: %u bytes\n", ns->geom.pgsz); + printk("OOB area size: %u bytes\n", ns->geom.oobsz); + printk("sector size: %u KiB\n", ns->geom.secsz >> 10); + printk("pages number: %u\n", ns->geom.pgnum); + printk("pages per sector: %u\n", ns->geom.pgsec); + printk("bus width: %u\n", ns->busw); + printk("bits in sector size: %u\n", ns->geom.secshift); + printk("bits in page size: %u\n", ns->geom.pgshift); + printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); + printk("page address bytes: %u\n", ns->geom.pgaddrbytes); + printk("sector address bytes: %u\n", ns->geom.secaddrbytes); + printk("options: %#x\n", ns->options); + + /* Map / allocate and initialize the flash image */ +#ifdef CONFIG_NS_ABS_POS + ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", + (void *)CONFIG_NS_ABS_POS); + return -ENOMEM; + } +#else + ns->mem.byte = vmalloc(ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", + ns->geom.totszoob); + return -ENOMEM; + } + memset(ns->mem.byte, 0xFF, ns->geom.totszoob); +#endif + + /* Allocate / initialize the internal buffer */ + ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->buf.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", + ns->geom.pgszoob); + goto error; + } + memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); + + /* Fill the partition_info structure */ + ns->part.name = "NAND simulator partition"; + ns->part.offset = 0; + ns->part.size = ns->geom.totsz; + + return 0; + +error: +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return -ENOMEM; +} + +/* + * Free the nandsim structure. + */ +static void +free_nandsim(struct nandsim *ns) +{ + kfree(ns->buf.byte); + +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return; +} + +/* + * Returns the string representation of 'state' state. + */ +static char * +get_state_name(uint32_t state) +{ + switch (NS_STATE(state)) { + case STATE_CMD_READ0: + return "STATE_CMD_READ0"; + case STATE_CMD_READ1: + return "STATE_CMD_READ1"; + case STATE_CMD_PAGEPROG: + return "STATE_CMD_PAGEPROG"; + case STATE_CMD_READOOB: + return "STATE_CMD_READOOB"; + case STATE_CMD_READSTART: + return "STATE_CMD_READSTART"; + case STATE_CMD_ERASE1: + return "STATE_CMD_ERASE1"; + case STATE_CMD_STATUS: + return "STATE_CMD_STATUS"; + case STATE_CMD_STATUS_M: + return "STATE_CMD_STATUS_M"; + case STATE_CMD_SEQIN: + return "STATE_CMD_SEQIN"; + case STATE_CMD_READID: + return "STATE_CMD_READID"; + case STATE_CMD_ERASE2: + return "STATE_CMD_ERASE2"; + case STATE_CMD_RESET: + return "STATE_CMD_RESET"; + case STATE_ADDR_PAGE: + return "STATE_ADDR_PAGE"; + case STATE_ADDR_SEC: + return "STATE_ADDR_SEC"; + case STATE_ADDR_ZERO: + return "STATE_ADDR_ZERO"; + case STATE_DATAIN: + return "STATE_DATAIN"; + case STATE_DATAOUT: + return "STATE_DATAOUT"; + case STATE_DATAOUT_ID: + return "STATE_DATAOUT_ID"; + case STATE_DATAOUT_STATUS: + return "STATE_DATAOUT_STATUS"; + case STATE_DATAOUT_STATUS_M: + return "STATE_DATAOUT_STATUS_M"; + case STATE_READY: + return "STATE_READY"; + case STATE_UNKNOWN: + return "STATE_UNKNOWN"; + } + + NS_ERR("get_state_name: unknown state, BUG\n"); + return NULL; +} + +/* + * Check if command is valid. + * + * RETURNS: 1 if wrong command, 0 if right. + */ +static int +check_command(int cmd) +{ + switch (cmd) { + + case NAND_CMD_READ0: + case NAND_CMD_READSTART: + case NAND_CMD_PAGEPROG: + case NAND_CMD_READOOB: + case NAND_CMD_ERASE1: + case NAND_CMD_STATUS: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + case NAND_CMD_ERASE2: + case NAND_CMD_RESET: + case NAND_CMD_READ1: + return 0; + + case NAND_CMD_STATUS_MULTI: + default: + return 1; + } +} + +/* + * Returns state after command is accepted by command number. + */ +static uint32_t +get_state_by_command(unsigned command) +{ + switch (command) { + case NAND_CMD_READ0: + return STATE_CMD_READ0; + case NAND_CMD_READ1: + return STATE_CMD_READ1; + case NAND_CMD_PAGEPROG: + return STATE_CMD_PAGEPROG; + case NAND_CMD_READSTART: + return STATE_CMD_READSTART; + case NAND_CMD_READOOB: + return STATE_CMD_READOOB; + case NAND_CMD_ERASE1: + return STATE_CMD_ERASE1; + case NAND_CMD_STATUS: + return STATE_CMD_STATUS; + case NAND_CMD_STATUS_MULTI: + return STATE_CMD_STATUS_M; + case NAND_CMD_SEQIN: + return STATE_CMD_SEQIN; + case NAND_CMD_READID: + return STATE_CMD_READID; + case NAND_CMD_ERASE2: + return STATE_CMD_ERASE2; + case NAND_CMD_RESET: + return STATE_CMD_RESET; + } + + NS_ERR("get_state_by_command: unknown command, BUG\n"); + return 0; +} + +/* + * Move an address byte to the correspondent internal register. + */ +static inline void +accept_addr_byte(struct nandsim *ns, u_char bt) +{ + uint byte = (uint)bt; + + if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) + ns->regs.column |= (byte << 8 * ns->regs.count); + else { + ns->regs.row |= (byte << 8 * (ns->regs.count - + ns->geom.pgaddrbytes + + ns->geom.secaddrbytes)); + } + + return; +} + +/* + * Switch to STATE_READY state. + */ +static inline void +switch_to_ready_state(struct nandsim *ns, u_char status) +{ + NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); + + ns->state = STATE_READY; + ns->nxstate = STATE_UNKNOWN; + ns->op = NULL; + ns->npstates = 0; + ns->stateidx = 0; + ns->regs.num = 0; + ns->regs.count = 0; + ns->regs.off = 0; + ns->regs.row = 0; + ns->regs.column = 0; + ns->regs.status = status; +} + +/* + * If the operation isn't known yet, try to find it in the global array + * of supported operations. + * + * Operation can be unknown because of the following. + * 1. New command was accepted and this is the firs call to find the + * correspondent states chain. In this case ns->npstates = 0; + * 2. There is several operations which begin with the same command(s) + * (for example program from the second half and read from the + * second half operations both begin with the READ1 command). In this + * case the ns->pstates[] array contains previous states. + * + * Thus, the function tries to find operation containing the following + * states (if the 'flag' parameter is 0): + * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state + * + * If (one and only one) matching operation is found, it is accepted ( + * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is + * zeroed). + * + * If there are several maches, the current state is pushed to the + * ns->pstates. + * + * The operation can be unknown only while commands are input to the chip. + * As soon as address command is accepted, the operation must be known. + * In such situation the function is called with 'flag' != 0, and the + * operation is searched using the following pattern: + * ns->pstates[0], ... ns->pstates[ns->npstates],
+ * + * It is supposed that this pattern must either match one operation on + * none. There can't be ambiguity in that case. + * + * If no matches found, the functions does the following: + * 1. if there are saved states present, try to ignore them and search + * again only using the last command. If nothing was found, switch + * to the STATE_READY state. + * 2. if there are no saved states, switch to the STATE_READY state. + * + * RETURNS: -2 - no matched operations found. + * -1 - several matches. + * 0 - operation is found. + */ +static int +find_operation(struct nandsim *ns, uint32_t flag) +{ + int opsfound = 0; + int i, j, idx = 0; + + for (i = 0; i < NS_OPER_NUM; i++) { + + int found = 1; + + if (!(ns->options & ops[i].reqopts)) + /* Ignore operations we can't perform */ + continue; + + if (flag) { + if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) + continue; + } else { + if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) + continue; + } + + for (j = 0; j < ns->npstates; j++) + if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) + && (ns->options & ops[idx].reqopts)) { + found = 0; + break; + } + + if (found) { + idx = i; + opsfound += 1; + } + } + + if (opsfound == 1) { + /* Exact match */ + ns->op = &ops[idx].states[0]; + if (flag) { + /* + * In this case the find_operation function was + * called when address has just began input. But it isn't + * yet fully input and the current state must + * not be one of STATE_ADDR_*, but the STATE_ADDR_* + * state must be the next state (ns->nxstate). + */ + ns->stateidx = ns->npstates - 1; + } else { + ns->stateidx = ns->npstates; + } + ns->npstates = 0; + ns->state = ns->op[ns->stateidx]; + ns->nxstate = ns->op[ns->stateidx + 1]; + NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", + idx, get_state_name(ns->state), get_state_name(ns->nxstate)); + return 0; + } + + if (opsfound == 0) { + /* Nothing was found. Try to ignore previous commands (if any) and search again */ + if (ns->npstates != 0) { + NS_DBG("find_operation: no operation found, try again with state %s\n", + get_state_name(ns->state)); + ns->npstates = 0; + return find_operation(ns, 0); + + } + NS_DBG("find_operation: no operations found\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return -2; + } + + if (flag) { + /* This shouldn't happen */ + NS_DBG("find_operation: BUG, operation must be known if address is input\n"); + return -2; + } + + NS_DBG("find_operation: there is still ambiguity\n"); + + ns->pstates[ns->npstates++] = ns->state; + + return -1; +} + +/* + * If state has any action bit, perform this action. + * + * RETURNS: 0 if success, -1 if error. + */ +static int +do_state_action(struct nandsim *ns, uint32_t action) +{ + int i, num; + int busdiv = ns->busw == 8 ? 1 : 2; + + action &= ACTION_MASK; + + /* Check that page address input is correct */ + if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { + NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); + return -1; + } + + switch (action) { + + case ACTION_CPY: + /* + * Copy page data to the internal buffer. + */ + + /* Column shouldn't be very large */ + if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { + NS_ERR("do_state_action: column number is too large\n"); + break; + } + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); + + NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", + num, NS_RAW_OFFSET(ns) + ns->regs.off); + + if (ns->regs.off == 0) + NS_LOG("read page %d\n", ns->regs.row); + else if (ns->regs.off < ns->geom.pgsz) + NS_LOG("read page %d (second half)\n", ns->regs.row); + else + NS_LOG("read OOB of page %d\n", ns->regs.row); + + NS_UDELAY(access_delay); + NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_SECERASE: + /* + * Erase sector. + */ + + if (ns->lines.wp) { + NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); + return -1; + } + + if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec + || (ns->regs.row & ~(ns->geom.secsz - 1))) { + NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); + return -1; + } + + ns->regs.row = (ns->regs.row << + 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; + ns->regs.column = 0; + + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", + ns->regs.row, NS_RAW_OFFSET(ns)); + NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); + + memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); + + NS_MDELAY(erase_delay); + + break; + + case ACTION_PRGPAGE: + /* + * Programm page - move internal buffer data to the page. + */ + + if (ns->lines.wp) { + NS_WARN("do_state_action: device is write-protected, programm\n"); + return -1; + } + + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + if (num != ns->regs.count) { + NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", + ns->regs.count, num); + return -1; + } + + for (i = 0; i < num; i++) + ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; + + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", + num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); + NS_LOG("programm page %d\n", ns->regs.row); + + NS_UDELAY(programm_delay); + NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_ZEROOFF: + NS_DBG("do_state_action: set internal offset to 0\n"); + ns->regs.off = 0; + break; + + case ACTION_HALFOFF: + if (!(ns->options & OPT_PAGE512_8BIT)) { + NS_ERR("do_state_action: BUG! can't skip half of page for non-512" + "byte page size 8x chips\n"); + return -1; + } + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); + ns->regs.off = ns->geom.pgsz/2; + break; + + case ACTION_OOBOFF: + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); + ns->regs.off = ns->geom.pgsz; + break; + + default: + NS_DBG("do_state_action: BUG! unknown action\n"); + } + + return 0; +} + +/* + * Switch simulator's state. + */ +static void +switch_state(struct nandsim *ns) +{ + if (ns->op) { + /* + * The current operation have already been identified. + * Just follow the states chain. + */ + + ns->stateidx += 1; + ns->state = ns->nxstate; + ns->nxstate = ns->op[ns->stateidx + 1]; + + NS_DBG("switch_state: operation is known, switch to the next state, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* See, whether we need to do some action */ + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + } else { + /* + * We don't yet know which operation we perform. + * Try to identify it. + */ + + /* + * The only event causing the switch_state function to + * be called with yet unknown operation is new command. + */ + ns->state = get_state_by_command(ns->regs.command); + + NS_DBG("switch_state: operation is unknown, try to find it\n"); + + if (find_operation(ns, 0) != 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + } + + /* For 16x devices column means the page offset in words */ + if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { + NS_DBG("switch_state: double the column number for 16x device\n"); + ns->regs.column <<= 1; + } + + if (NS_STATE(ns->nxstate) == STATE_READY) { + /* + * The current state is the last. Return to STATE_READY + */ + + u_char status = NS_STATUS_OK(ns); + + /* In case of data states, see if all bytes were input/output */ + if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) + && ns->regs.count != ns->regs.num) { + NS_WARN("switch_state: not all bytes were processed, %d left\n", + ns->regs.num - ns->regs.count); + status = NS_STATUS_FAILED(ns); + } + + NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); + + switch_to_ready_state(ns, status); + + return; + } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { + /* + * If the next state is data input/output, switch to it now + */ + + ns->state = ns->nxstate; + ns->nxstate = ns->op[++ns->stateidx + 1]; + ns->regs.num = ns->regs.count = 0; + + NS_DBG("switch_state: the next state is data I/O, switch, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* + * Set the internal register to the count of bytes which + * are expected to be input or output + */ + switch (NS_STATE(ns->state)) { + case STATE_DATAIN: + case STATE_DATAOUT: + ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + break; + + case STATE_DATAOUT_ID: + ns->regs.num = ns->geom.idbytes; + break; + + case STATE_DATAOUT_STATUS: + case STATE_DATAOUT_STATUS_M: + ns->regs.count = ns->regs.num = 0; + break; + + default: + NS_ERR("switch_state: BUG! unknown data state\n"); + } + + } else if (ns->nxstate & STATE_ADDR_MASK) { + /* + * If the next state is address input, set the internal + * register to the number of expected address bytes + */ + + ns->regs.count = 0; + + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + + default: + NS_ERR("switch_state: BUG! unknown address state\n"); + } + } else { + /* + * Just reset internal counters. + */ + + ns->regs.num = 0; + ns->regs.count = 0; + } +} + +static void +ns_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + switch (cmd) { + + /* set CLE line high */ + case NAND_CTL_SETCLE: + NS_DBG("ns_hwcontrol: start command latch cycles\n"); + ns->lines.cle = 1; + break; + + /* set CLE line low */ + case NAND_CTL_CLRCLE: + NS_DBG("ns_hwcontrol: stop command latch cycles\n"); + ns->lines.cle = 0; + break; + + /* set ALE line high */ + case NAND_CTL_SETALE: + NS_DBG("ns_hwcontrol: start address latch cycles\n"); + ns->lines.ale = 1; + break; + + /* set ALE line low */ + case NAND_CTL_CLRALE: + NS_DBG("ns_hwcontrol: stop address latch cycles\n"); + ns->lines.ale = 0; + break; + + /* set WP line high */ + case NAND_CTL_SETWP: + NS_DBG("ns_hwcontrol: enable write protection\n"); + ns->lines.wp = 1; + break; + + /* set WP line low */ + case NAND_CTL_CLRWP: + NS_DBG("ns_hwcontrol: disable write protection\n"); + ns->lines.wp = 0; + break; + + /* set CE line low */ + case NAND_CTL_SETNCE: + NS_DBG("ns_hwcontrol: enable chip\n"); + ns->lines.ce = 1; + break; + + /* set CE line high */ + case NAND_CTL_CLRNCE: + NS_DBG("ns_hwcontrol: disable chip\n"); + ns->lines.ce = 0; + break; + + default: + NS_ERR("hwcontrol: unknown command\n"); + } + + return; +} + +static u_char +ns_nand_read_byte(struct mtd_info *mtd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + u_char outb = 0x00; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); + return outb; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); + return outb; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_byte: unexpected data output cycle, state is %s " + "return %#x\n", get_state_name(ns->state), (uint)outb); + return outb; + } + + /* Status register may be read as many times as it is wanted */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { + NS_DBG("read_byte: return %#x status\n", ns->regs.status); + return ns->regs.status; + } + + /* Check if there is any data in the internal buffer which may be read */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); + return outb; + } + + switch (NS_STATE(ns->state)) { + case STATE_DATAOUT: + if (ns->busw == 8) { + outb = ns->buf.byte[ns->regs.count]; + ns->regs.count += 1; + } else { + outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); + ns->regs.count += 2; + } + break; + case STATE_DATAOUT_ID: + NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); + outb = ns->ids[ns->regs.count]; + ns->regs.count += 1; + break; + default: + BUG(); + } + + if (ns->regs.count == ns->regs.num) { + NS_DBG("read_byte: all bytes were read\n"); + + /* + * The OPT_AUTOINCR allows to read next conseqitive pages without + * new read operation cycle. + */ + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + + } + + return outb; +} + +static void +ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("write_byte: chip is disabled, ignore write\n"); + return; + } + if (ns->lines.ale && ns->lines.cle) { + NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); + return; + } + + if (ns->lines.cle == 1) { + /* + * The byte written is a command. + */ + + if (byte == NAND_CMD_RESET) { + NS_LOG("reset chip\n"); + switch_to_ready_state(ns, NS_STATUS_OK(ns)); + return; + } + + /* + * Chip might still be in STATE_DATAOUT + * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or + * STATE_DATAOUT_STATUS_M state. If so, switch state. + */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS + || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M + || ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT)) + switch_state(ns); + + /* Check if chip is expecting command */ + if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + } + + /* Check that the command byte is correct */ + if (check_command(byte)) { + NS_ERR("write_byte: unknown command %#x\n", (uint)byte); + return; + } + + NS_DBG("command byte corresponding to %s state accepted\n", + get_state_name(get_state_by_command(byte))); + ns->regs.command = byte; + switch_state(ns); + + } else if (ns->lines.ale == 1) { + /* + * The byte written is an address. + */ + + if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { + + NS_DBG("write_byte: operation isn't known yet, identify it\n"); + + if (find_operation(ns, 1) < 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + ns->regs.count = 0; + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + default: + BUG(); + } + } + + /* Check that chip is expecting address */ + if (!(ns->nxstate & STATE_ADDR_MASK)) { + NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " + "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_ERR("write_byte: no more address bytes expected\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + accept_addr_byte(ns, byte); + + ns->regs.count += 1; + + NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", + (uint)byte, ns->regs.count, ns->regs.num); + + if (ns->regs.count == ns->regs.num) { + NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); + switch_state(ns); + } + + } else { + /* + * The byte written is an input data. + */ + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " + "switch to %s\n", (uint)byte, + get_state_name(ns->state), get_state_name(STATE_READY)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", + ns->regs.num); + return; + } + + if (ns->busw == 8) { + ns->buf.byte[ns->regs.count] = byte; + ns->regs.count += 1; + } else { + ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); + ns->regs.count += 2; + } + } + + return; +} + +static int +ns_device_ready(struct mtd_info *mtd) +{ + NS_DBG("device_ready\n"); + return 1; +} + +static uint16_t +ns_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("read_word\n"); + + return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); +} + +static void +ns_nand_write_word(struct mtd_info *mtd, uint16_t word) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("write_word\n"); + + chip->write_byte(mtd, word & 0xFF); + chip->write_byte(mtd, word >> 8); +} + +static void +ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_buf: data input isn't expected, state is %s, " + "switch to STATE_READY\n", get_state_name(ns->state)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("write_buf: too many input bytes\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(ns->buf.byte + ns->regs.count, buf, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); + } +} + +static void +ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_buf: chip is disabled\n"); + return; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_buf: ALE or CLE pin is high\n"); + return; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", + get_state_name(ns->state)); + return; + } + + if (NS_STATE(ns->state) != STATE_DATAOUT) { + int i; + + for (i = 0; i < len; i++) + buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd); + + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("read_buf: too many bytes to read\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(buf, ns->buf.byte + ns->regs.count, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + } + + return; +} + +static int +ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); + + if (!memcmp(buf, &ns_verify_buf[0], len)) { + NS_DBG("verify_buf: the buffer is OK\n"); + return 0; + } else { + NS_DBG("verify_buf: the buffer is wrong\n"); + return -EFAULT; + } +} + +/* + * Having only NAND chip IDs we call nand_scan which detects NAND flash + * parameters and then calls scan_bbt in order to scan/find/build the + * NAND flash bad block table. But since at that moment the NAND flash + * image isn't allocated in the simulator, errors arise. To avoid this + * we redefine the scan_bbt callback and initialize the nandsim structure + * before the flash media scanning. + */ +int ns_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct nandsim *ns = (struct nandsim *)(chip->priv); + int retval; + + if (!NS_IS_INITIALIZED(ns)) + if ((retval = init_nandsim(mtd)) != 0) { + NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); + return retval; + } + if ((retval = nand_default_bbt(mtd)) != 0) { + free_nandsim(ns); + return retval; + } + + return 0; +} + +/* + * Module initialization function + */ +int __init ns_init_module(void) +{ + struct nand_chip *chip; + struct nandsim *nand; + int retval = -ENOMEM; + + if (bus_width != 8 && bus_width != 16) { + NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); + return -EINVAL; + } + + /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ + nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim), GFP_KERNEL); + if (!nsmtd) { + NS_ERR("unable to allocate core structures.\n"); + return -ENOMEM; + } + memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim)); + chip = (struct nand_chip *)(nsmtd + 1); + nsmtd->priv = (void *)chip; + nand = (struct nandsim *)(chip + 1); + chip->priv = (void *)nand; + + /* + * Register simulator's callbacks. + */ + chip->hwcontrol = ns_hwcontrol; + chip->read_byte = ns_nand_read_byte; + chip->dev_ready = ns_device_ready; + chip->scan_bbt = ns_scan_bbt; + chip->write_byte = ns_nand_write_byte; + chip->write_buf = ns_nand_write_buf; + chip->read_buf = ns_nand_read_buf; + chip->verify_buf = ns_nand_verify_buf; + chip->write_word = ns_nand_write_word; + chip->read_word = ns_nand_read_word; + chip->eccmode = NAND_ECC_SOFT; + + /* + * Perform minimum nandsim structure initialization to handle + * the initial ID read command correctly + */ + if (third_id_byte != 0xFF || fourth_id_byte != 0xFF) + nand->geom.idbytes = 4; + else + nand->geom.idbytes = 2; + nand->regs.status = NS_STATUS_OK(nand); + nand->nxstate = STATE_UNKNOWN; + nand->options |= OPT_PAGE256; /* temporary value */ + nand->ids[0] = first_id_byte; + nand->ids[1] = second_id_byte; + nand->ids[2] = third_id_byte; + nand->ids[3] = fourth_id_byte; + if (bus_width == 16) { + nand->busw = 16; + chip->options |= NAND_BUSWIDTH_16; + } + + if ((retval = nand_scan(nsmtd, 1)) != 0) { + NS_ERR("can't register NAND Simulator\n"); + if (retval > 0) + retval = -ENXIO; + goto error; + } + + /* Register NAND as one big partition */ + add_mtd_partitions(nsmtd, &nand->part, 1); + + return 0; + +error: + kfree(nsmtd); + + return retval; +} + +module_init(ns_init_module); + +/* + * Module clean-up function + */ +static void __exit ns_cleanup_module(void) +{ + struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + + free_nandsim(ns); /* Free nandsim private resources */ + nand_release(nsmtd); /* Unregisterd drived */ + kfree(nsmtd); /* Free other structures */ +} + +module_exit(ns_cleanup_module); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Artem B. Bityuckiy"); +MODULE_DESCRIPTION ("The NAND flash simulator"); + --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/ppchameleonevb.c @@ -0,0 +1,420 @@ +/* + * drivers/mtd/nand/ppchameleonevb.c + * + * Copyright (C) 2003 DAVE Srl (info@wawnet.biz) + * + * Derived from drivers/mtd/nand/edb7312.c + * + * + * $Id: ppchameleonevb.c,v 1.6 2004/11/05 16:07:16 kalev 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 devices found on the + * PPChameleon/PPChameleonEVB system. + * PPChameleon options (autodetected): + * - BA model: no NAND + * - ME model: 32MB (Samsung K9F5608U0B) + * - HI model: 128MB (Samsung K9F1G08UOM) + * PPChameleonEVB options: + * - 32MB (Samsung K9F5608U0B) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef USE_READY_BUSY_PIN +#define USE_READY_BUSY_PIN +/* see datasheets (tR) */ +#define NAND_BIG_DELAY_US 25 +#define NAND_SMALL_DELAY_US 10 + +/* handy sizes */ +#define SZ_4M 0x00400000 +#define NAND_SMALL_SIZE 0x02000000 +#define NAND_MTD_NAME "ppchameleon-nand" +#define NAND_EVB_MTD_NAME "ppchameleonevb-nand" + +/* GPIO pins used to drive NAND chip mounted on processor module */ +#define NAND_nCE_GPIO_PIN (0x80000000 >> 1) +#define NAND_CLE_GPIO_PIN (0x80000000 >> 2) +#define NAND_ALE_GPIO_PIN (0x80000000 >> 3) +#define NAND_RB_GPIO_PIN (0x80000000 >> 4) +/* GPIO pins used to drive NAND chip mounted on EVB */ +#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14) +#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15) +#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16) +#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31) + +/* + * MTD structure for PPChameleonEVB board + */ +static struct mtd_info *ppchameleon_mtd = NULL; +static struct mtd_info *ppchameleonevb_mtd = NULL; + +/* + * Module stuff + */ +static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR; +static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR; + +#ifdef MODULE +module_param(ppchameleon_fio_pbase, ulong, 0); +module_param(ppchameleonevb_fio_pbase, ulong, 0); +#else +__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase); +__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase); +#endif + +#ifdef CONFIG_MTD_PARTITIONS +/* + * Define static partitions for flash devices + */ +static struct mtd_partition partition_info_hi[] = { + { name: "PPChameleon HI Nand Flash", + offset: 0, + size: 128*1024*1024 } +}; + +static struct mtd_partition partition_info_me[] = { + { name: "PPChameleon ME Nand Flash", + offset: 0, + size: 32*1024*1024 } +}; + +static struct mtd_partition partition_info_evb[] = { + { name: "PPChameleonEVB Nand Flash", + offset: 0, + size: 32*1024*1024 } +}; + +#define NUM_PARTITIONS 1 + +extern int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id); +#endif + + +/* + * hardware specific access to control-lines + */ +static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd) +{ + switch(cmd) { + + case NAND_CTL_SETCLE: + MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRCLE: + MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_SETALE: + MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRALE: + MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_SETNCE: + MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR); + break; + case NAND_CTL_CLRNCE: + MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR); + break; + } +} + +static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd) +{ + switch(cmd) { + + case NAND_CTL_SETCLE: + MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRCLE: + MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_SETALE: + MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRALE: + MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_SETNCE: + MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR); + break; + case NAND_CTL_CLRNCE: + MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR); + break; + } +} + +#ifdef USE_READY_BUSY_PIN +/* + * read device ready pin + */ +static int ppchameleon_device_ready(struct mtd_info *minfo) +{ + if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN) + return 1; + return 0; +} + +static int ppchameleonevb_device_ready(struct mtd_info *minfo) +{ + if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN) + return 1; + return 0; +} +#endif + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +const char *part_probes_evb[] = { "cmdlinepart", NULL }; +#endif + +/* + * Main initialization routine + */ +static int __init ppchameleonevb_init (void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + void __iomem *ppchameleon_fio_base; + void __iomem *ppchameleonevb_fio_base; + + + /********************************* + * Processor module NAND (if any) * + *********************************/ + /* Allocate memory for MTD device structure and private data */ + ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), GFP_KERNEL); + if (!ppchameleon_mtd) { + printk("Unable to allocate PPChameleon NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical address */ + ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M); + if(!ppchameleon_fio_base) { + printk("ioremap PPChameleon NAND flash failed\n"); + kfree(ppchameleon_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&ppchameleon_mtd[1]); + + /* Initialize structures */ + memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + ppchameleon_mtd->priv = this; + + /* Initialize GPIOs */ + /* Pin mapping for NAND chip */ + /* + CE GPIO_01 + CLE GPIO_02 + ALE GPIO_03 + R/B GPIO_04 + */ + /* output select */ + out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF); + /* three-state select */ + out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF); + /* enable output driver */ + out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN); +#ifdef USE_READY_BUSY_PIN + /* three-state select */ + out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF); + /* high-impedecence */ + out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN)); + /* input select */ + out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000); +#endif + + /* insert callbacks */ + this->IO_ADDR_R = ppchameleon_fio_base; + this->IO_ADDR_W = ppchameleon_fio_base; + this->hwcontrol = ppchameleon_hwcontrol; +#ifdef USE_READY_BUSY_PIN + this->dev_ready = ppchameleon_device_ready; +#endif + this->chip_delay = NAND_BIG_DELAY_US; + /* ECC mode */ + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existence of the device (it could not be mounted) */ + if (nand_scan (ppchameleon_mtd, 1)) { + iounmap((void *)ppchameleon_fio_base); + kfree (ppchameleon_mtd); + goto nand_evb_init; + } + +#ifndef USE_READY_BUSY_PIN + /* Adjust delay if necessary */ + if (ppchameleon_mtd->size == NAND_SMALL_SIZE) + this->chip_delay = NAND_SMALL_DELAY_US; +#endif + +#ifdef CONFIG_MTD_PARTITIONS + ppchameleon_mtd->name = "ppchameleon-nand"; + mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + if (ppchameleon_mtd->size == NAND_SMALL_SIZE) + mtd_parts = partition_info_me; + else + mtd_parts = partition_info_hi; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb); + +nand_evb_init: + /**************************** + * EVB NAND (always present) * + ****************************/ + /* Allocate memory for MTD device structure and private data */ + ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), GFP_KERNEL); + if (!ppchameleonevb_mtd) { + printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical address */ + ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M); + if(!ppchameleonevb_fio_base) { + printk("ioremap PPChameleonEVB NAND flash failed\n"); + kfree(ppchameleonevb_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&ppchameleonevb_mtd[1]); + + /* Initialize structures */ + memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + ppchameleonevb_mtd->priv = this; + + /* Initialize GPIOs */ + /* Pin mapping for NAND chip */ + /* + CE GPIO_14 + CLE GPIO_15 + ALE GPIO_16 + R/B GPIO_31 + */ + /* output select */ + out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0); + out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF); + /* three-state select */ + out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0); + out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF); + /* enable output driver */ + out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | + NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN); +#ifdef USE_READY_BUSY_PIN + /* three-state select */ + out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC); + /* high-impedecence */ + out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN)); + /* input select */ + out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001); +#endif + + /* insert callbacks */ + this->IO_ADDR_R = ppchameleonevb_fio_base; + this->IO_ADDR_W = ppchameleonevb_fio_base; + this->hwcontrol = ppchameleonevb_hwcontrol; +#ifdef USE_READY_BUSY_PIN + this->dev_ready = ppchameleonevb_device_ready; +#endif + this->chip_delay = NAND_SMALL_DELAY_US; + + /* ECC mode */ + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existence of the device */ + if (nand_scan (ppchameleonevb_mtd, 1)) { + iounmap((void *)ppchameleonevb_fio_base); + kfree (ppchameleonevb_mtd); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; + mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = partition_info_evb; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(ppchameleonevb_init); + +/* + * Clean up routine + */ +static void __exit ppchameleonevb_cleanup (void) +{ + struct nand_chip *this; + + /* Release resources, unregister device(s) */ + nand_release (ppchameleon_mtd); + nand_release (ppchameleonevb_mtd); + + /* Release iomaps */ + this = (struct nand_chip *) &ppchameleon_mtd[1]; + iounmap((void *) this->IO_ADDR_R; + this = (struct nand_chip *) &ppchameleonevb_mtd[1]; + iounmap((void *) this->IO_ADDR_R; + + /* Free the MTD device structure */ + kfree (ppchameleon_mtd); + kfree (ppchameleonevb_mtd); +} +module_exit(ppchameleonevb_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("DAVE Srl "); +MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/rtc_from4.c @@ -0,0 +1,683 @@ +/* + * drivers/mtd/nand/rtc_from4.c + * + * Copyright (C) 2004 Red Hat, Inc. + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin 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 AG-AND flash device found on the + * Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), + * which utilizes the Renesas HN29V1G91T-30 part. + * This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MTD structure for Renesas board + */ +static struct mtd_info *rtc_from4_mtd = NULL; + +#define RTC_FROM4_MAX_CHIPS 2 + +/* HS77x9 processor register defines */ +#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60)) +#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62)) +#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64)) +#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66)) +#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68)) +#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C)) +#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80)) + +/* + * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor) + */ +/* Address where flash is mapped */ +#define RTC_FROM4_FIO_BASE 0x14000000 + +/* CLE and ALE are tied to address lines 5 & 4, respectively */ +#define RTC_FROM4_CLE (1 << 5) +#define RTC_FROM4_ALE (1 << 4) + +/* address lines A24-A22 used for chip selection */ +#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000) +#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000) +#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000) +/* mask address lines A24-A22 used for chip selection */ +#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA) + +/* FPGA status register for checking device ready (bit zero) */ +#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002) +#define RTC_FROM4_DEVICE_READY 0x0001 + +/* FPGA Reed-Solomon ECC Control register */ + +#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050) +#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7) +#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6) +#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5) + +/* FPGA Reed-Solomon ECC code base */ +#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060) +#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080) + +/* FPGA Reed-Solomon ECC check register */ +#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) +#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) + +#define ERR_STAT_ECC_AVAILABLE 0x20 + +/* Undefine for software ECC */ +#define RTC_FROM4_HWECC 1 + +/* Define as 1 for no virtual erase blocks (in JFFS2) */ +#define RTC_FROM4_NO_VIRTBLOCKS 0 + +/* + * Module stuff + */ +static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); + +const static struct mtd_partition partition_info[] = { + { + .name = "Renesas flash partition 1", + .offset = 0, + .size = MTDPART_SIZ_FULL + }, +}; +#define NUM_PARTITIONS 1 + +/* + * hardware specific flash bbt decriptors + * Note: this is to allow debugging by disabling + * NAND_BBT_CREATE and/or NAND_BBT_WRITE + * + */ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr rtc_from4_bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 40, + .len = 4, + .veroffs = 44, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 40, + .len = 4, + .veroffs = 44, + .maxblocks = 4, + .pattern = mirror_pattern +}; + + + +#ifdef RTC_FROM4_HWECC + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * hardware specific Out Of Band information + */ +static struct nand_oobinfo rtc_from4_nand_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 32, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31}, + .oobfree = { {32, 32} } +}; + +/* Aargh. I missed the reversed bit order, when I + * was talking to Renesas about the FPGA. + * + * The table is used for bit reordering and inversion + * of the ecc byte which we get from the FPGA + */ +static uint8_t revbits[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +#endif + + + +/* + * rtc_from4_hwcontrol - hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: hardware control command + * + * Address lines (A5 and A4) are used to control Command and Address Latch + * Enable on this board, so set the read/write address appropriately. + * + * Chip Enable is also controlled by the Chip Select (CS5) and + * Address lines (A24-A22), so no action is required here. + * + */ +static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip* this = (struct nand_chip *) (mtd->priv); + + switch(cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE); + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE); + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE); + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE); + break; + + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + + } +} + + +/* + * rtc_from4_nand_select_chip - hardware specific chip select + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * The chip select is based on address lines A24-A22. + * This driver uses flash slots 3 and 4 (A23-A22). + * + */ +static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK); + + switch(chip) { + + case 0: /* select slot 3 chip */ + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3); + break; + case 1: /* select slot 4 chip */ + this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4); + this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4); + break; + + } +} + + +/* + * rtc_from4_nand_device_ready - hardware specific ready/busy check + * @mtd: MTD device structure + * + * This board provides the Ready/Busy state in the status register + * of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal. + * + */ +static int rtc_from4_nand_device_ready(struct mtd_info *mtd) +{ + unsigned short status; + + status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR)); + + return (status & RTC_FROM4_DEVICE_READY); + +} + + +/* + * deplete - code to perform device recovery in case there was a power loss + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * If there was a sudden loss of power during an erase operation, a + * "device recovery" operation must be performed when power is restored + * to ensure correct operation. This routine performs the required steps + * for the requested chip. + * + * See page 86 of the data sheet for details. + * + */ +static void deplete(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + /* wait until device is ready */ + while (!this->dev_ready(mtd)); + + this->select_chip(mtd, chip); + + /* Send the commands for device recovery, phase 1 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + + /* Send the commands for device recovery, phase 2 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + +} + + +#ifdef RTC_FROM4_HWECC +/* + * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function + * @mtd: MTD device structure + * @mode: I/O mode; read or write + * + * enable hardware ECC for data read or write + * + */ +static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode) +{ + volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL); + unsigned short status; + + switch (mode) { + case NAND_ECC_READ : + status = RTC_FROM4_RS_ECC_CTL_CLR + | RTC_FROM4_RS_ECC_CTL_FD_E; + + *rs_ecc_ctl = status; + break; + + case NAND_ECC_READSYN : + status = 0x00; + + *rs_ecc_ctl = status; + break; + + case NAND_ECC_WRITE : + status = RTC_FROM4_RS_ECC_CTL_CLR + | RTC_FROM4_RS_ECC_CTL_GEN + | RTC_FROM4_RS_ECC_CTL_FD_E; + + *rs_ecc_ctl = status; + break; + + default: + BUG(); + break; + } + +} + + +/* + * rtc_from4_calculate_ecc - hardware specific code to read ECC code + * @mtd: MTD device structure + * @dat: buffer containing the data to generate ECC codes + * @ecc_code ECC codes calculated + * + * The ECC code is calculated by the FPGA. All we have to do is read the values + * from the FPGA registers. + * + * Note: We read from the inverted registers, since data is inverted before + * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code + * + */ +static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN); + unsigned short value; + int i; + + for (i = 0; i < 8; i++) { + value = *rs_eccn; + ecc_code[i] = (unsigned char)value; + rs_eccn++; + } + ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ +} + + +/* + * rtc_from4_correct_data - hardware specific code to correct data using ECC code + * @mtd: MTD device structure + * @buf: buffer containing the data to generate ECC codes + * @ecc1 ECC codes read + * @ecc2 ECC codes calculated + * + * The FPGA tells us fast, if there's an error or not. If no, we go back happy + * else we read the ecc results from the fpga and call the rs library to decode + * and hopefully correct the error. + * + */ +static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) +{ + int i, j, res; + unsigned short status; + uint16_t par[6], syn[6]; + uint8_t ecc[8]; + volatile unsigned short *rs_ecc; + + status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK)); + + if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) { + return 0; + } + + /* Read the syndrom pattern from the FPGA and correct the bitorder */ + rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC); + for (i = 0; i < 8; i++) { + ecc[i] = revbits[(*rs_ecc) & 0xFF]; + rs_ecc++; + } + + /* convert into 6 10bit syndrome fields */ + par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) | + (((uint16_t)ecc[1] << 8) & 0x300)]; + par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) | + (((uint16_t)ecc[2] << 6) & 0x3c0)]; + par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) | + (((uint16_t)ecc[3] << 4) & 0x3f0)]; + par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) | + (((uint16_t)ecc[4] << 2) & 0x3fc)]; + par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) | + (((uint16_t)ecc[6] << 8) & 0x300)]; + par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0); + + /* Convert to computable syndrome */ + for (i = 0; i < 6; i++) { + syn[i] = par[0]; + for (j = 1; j < 6; j++) + if (par[j] != rs_decoder->nn) + syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)]; + + /* Convert to index form */ + syn[i] = rs_decoder->index_of[syn[i]]; + } + + /* Let the library code do its magic.*/ + res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL); + if (res > 0) { + DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " + "ECC corrected %d errors on read\n", res); + } + return res; +} + + +/** + * rtc_from4_errstat - perform additional error status checks + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state or the operation + * @status: status code returned from read status + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * + * Perform additional error status checks on erase and write failures + * to determine if errors are correctable. For this device, correctable + * 1-bit errors on erase and write are considered acceptable. + * + * note: see pages 34..37 of data sheet for details. + * + */ +static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) +{ + int er_stat=0; + int rtn, retlen; + size_t len; + uint8_t *buf; + int i; + + this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1); + + if (state == FL_ERASING) { + for (i=0; i<4; i++) { + if (status & 1<<(i+1)) { + this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<(i+1); /* err_ecc_not_avail */ + } + } + } + } else if (state == FL_WRITING) { + /* single bank write logic */ + this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<1; /* err_ecc_not_avail */ + } else { + len = mtd->oobblock; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n"); + er_stat = 1; /* if we can't check, assume failed */ + } else { + /* recovery read */ + /* page read */ + rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1); + if (rtn) { /* if read failed or > 1-bit error corrected */ + er_stat |= 1<<1; /* ECC read failed */ + } + kfree(buf); + } + } + } + + rtn = status; + if (er_stat == 0) { /* if ECC is available */ + rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ + } + + return rtn; +} +#endif + + +/* + * Main initialization routine + */ +int __init rtc_from4_init (void) +{ + struct nand_chip *this; + unsigned short bcr1, bcr2, wcr2; + int i; + + /* Allocate memory for MTD device structure and private data */ + rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!rtc_from4_mtd) { + printk ("Unable to allocate Renesas NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&rtc_from4_mtd[1]); + + /* Initialize structures */ + memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + rtc_from4_mtd->priv = this; + + /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */ + bcr1 = *SH77X9_BCR1 & ~0x0002; + bcr1 |= 0x0002; + *SH77X9_BCR1 = bcr1; + + /* set */ + bcr2 = *SH77X9_BCR2 & ~0x0c00; + bcr2 |= 0x0800; + *SH77X9_BCR2 = bcr2; + + /* set area 5 wait states */ + wcr2 = *SH77X9_WCR2 & ~0x1c00; + wcr2 |= 0x1c00; + *SH77X9_WCR2 = wcr2; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = rtc_from4_fio_base; + this->IO_ADDR_W = rtc_from4_fio_base; + /* Set address of hardware control function */ + this->hwcontrol = rtc_from4_hwcontrol; + /* Set address of chip select function */ + this->select_chip = rtc_from4_nand_select_chip; + /* command delay time (in us) */ + this->chip_delay = 100; + /* return the status of the Ready/Busy line */ + this->dev_ready = rtc_from4_nand_device_ready; + +#ifdef RTC_FROM4_HWECC + printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n"); + + this->eccmode = NAND_ECC_HW8_512; + this->options |= NAND_HWECC_SYNDROME; + /* return the status of extra status and ECC checks */ + this->errstat = rtc_from4_errstat; + /* set the nand_oobinfo to support FPGA H/W error detection */ + this->autooob = &rtc_from4_nand_oobinfo; + this->enable_hwecc = rtc_from4_enable_hwecc; + this->calculate_ecc = rtc_from4_calculate_ecc; + this->correct_data = rtc_from4_correct_data; +#else + printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); + + this->eccmode = NAND_ECC_SOFT; +#endif + + /* set the bad block tables to support debugging */ + this->bbt_td = &rtc_from4_bbt_main_descr; + this->bbt_md = &rtc_from4_bbt_mirror_descr; + + /* Scan to find existence of the device */ + if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { + kfree(rtc_from4_mtd); + return -ENXIO; + } + + /* Perform 'device recovery' for each chip in case there was a power loss. */ + for (i=0; i < this->numchips; i++) { + deplete(rtc_from4_mtd, i); + } + +#if RTC_FROM4_NO_VIRTBLOCKS + /* use a smaller erase block to minimize wasted space when a block is bad */ + /* note: this uses eight times as much RAM as using the default and makes */ + /* mounts take four times as long. */ + rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; +#endif + + /* Register the partitions */ + add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + +#ifdef RTC_FROM4_HWECC + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree = 6 + */ + rs_decoder = init_rs(10, 0x409, 0, 1, 6); + if (!rs_decoder) { + printk (KERN_ERR "Could not create a RS decoder\n"); + nand_release(rtc_from4_mtd); + kfree(rtc_from4_mtd); + return -ENOMEM; + } +#endif + /* Return happy */ + return 0; +} +module_init(rtc_from4_init); + + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit rtc_from4_cleanup (void) +{ + /* Release resource, unregister partitions */ + nand_release(rtc_from4_mtd); + + /* Free the MTD device structure */ + kfree (rtc_from4_mtd); + +#ifdef RTC_FROM4_HWECC + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +#endif +} +module_exit(rtc_from4_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("d.marlin + * + * Samsung S3C2410 NAND driver + * + * Changelog: + * 21-Sep-2004 BJD Initial version + * 23-Sep-2004 BJD Mulitple device support + * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode + * 12-Oct-2004 BJD Fixed errors in use of platform data + * 18-Feb-2004 BJD Fix sparse errors + * + * $Id: s3c2410.c,v 1.8 2005/02/18 14:46:12 bjd 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 +#include + +#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define PFX "s3c2410-nand: " + +#ifdef CONFIG_MTD_NAND_S3C2410_HWECC +static int hardware_ecc = 1; +#else +static int hardware_ecc = 0; +#endif + +/* new oob placement block for use with hardware ecc generation + */ + +static struct nand_oobinfo nand_hw_eccoob = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2 }, + .oobfree = { {8, 8} } +}; + +/* controller and mtd information */ + +struct s3c2410_nand_info; + +struct s3c2410_nand_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct s3c2410_nand_set *set; + struct s3c2410_nand_info *info; + int scan_res; +}; + +/* overview of the s3c2410 nand state */ + +struct s3c2410_nand_info { + /* mtd info */ + struct nand_hw_control controller; + struct s3c2410_nand_mtd *mtds; + struct s3c2410_platform_nand *platform; + + /* device info */ + struct device *device; + struct resource *area; + struct clk *clk; + void __iomem *regs; + int mtd_count; +}; + +/* conversion functions */ + +static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) +{ + return container_of(mtd, struct s3c2410_nand_mtd, mtd); +} + +static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) +{ + return s3c2410_nand_mtd_toours(mtd)->info; +} + +static struct s3c2410_nand_info *to_nand_info(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) +{ + return dev->platform_data; +} + +/* timing calculations */ + +#define NS_IN_KHZ 10000000 + +static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max) +{ + int result; + + result = (wanted * NS_IN_KHZ) / clk; + result++; + + pr_debug("result %d from %ld, %d\n", result, clk, wanted); + + if (result > max) { + printk("%d ns is too big for current clock rate %ld\n", + wanted, clk); + return -1; + } + + if (result < 1) + result = 1; + + return result; +} + +#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ) + +/* controller setup */ + +static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, + struct device *dev) +{ + struct s3c2410_platform_nand *plat = to_nand_plat(dev); + unsigned int tacls, twrph0, twrph1; + unsigned long clkrate = clk_get_rate(info->clk); + unsigned long cfg; + + /* calculate the timing information for the controller */ + + if (plat != NULL) { + tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); + twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); + twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); + } else { + /* default timings */ + tacls = 8; + twrph0 = 8; + twrph1 = 8; + } + + if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { + printk(KERN_ERR PFX "cannot get timings suitable for board\n"); + return -EINVAL; + } + + printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n", + to_ns(tacls, clkrate), + to_ns(twrph0, clkrate), + to_ns(twrph1, clkrate)); + + cfg = S3C2410_NFCONF_EN; + cfg |= S3C2410_NFCONF_TACLS(tacls-1); + cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); + cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); + + pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); + + writel(cfg, info->regs + S3C2410_NFCONF); + return 0; +} + +/* select chip */ + +static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct nand_chip *this = mtd->priv; + unsigned long cur; + + nmtd = this->priv; + info = nmtd->info; + + cur = readl(info->regs + S3C2410_NFCONF); + + if (chip == -1) { + cur |= S3C2410_NFCONF_nFCE; + } else { + if (chip > nmtd->set->nr_chips) { + printk(KERN_ERR PFX "chip %d out of range\n", chip); + return; + } + + if (info->platform != NULL) { + if (info->platform->select_chip != NULL) + (info->platform->select_chip)(nmtd->set, chip); + } + + cur &= ~S3C2410_NFCONF_nFCE; + } + + writel(cur, info->regs + S3C2410_NFCONF); +} + +/* command and control functions */ + +static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long cur; + + switch (cmd) { + case NAND_CTL_SETNCE: + cur = readl(info->regs + S3C2410_NFCONF); + cur &= ~S3C2410_NFCONF_nFCE; + writel(cur, info->regs + S3C2410_NFCONF); + break; + + case NAND_CTL_CLRNCE: + cur = readl(info->regs + S3C2410_NFCONF); + cur |= S3C2410_NFCONF_nFCE; + writel(cur, info->regs + S3C2410_NFCONF); + break; + + /* we don't need to implement these */ + case NAND_CTL_SETCLE: + case NAND_CTL_CLRCLE: + case NAND_CTL_SETALE: + case NAND_CTL_CLRALE: + pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd); + break; + } +} + +/* s3c2410_nand_command + * + * This function implements sending commands and the relevant address + * information to the chip, via the hardware controller. Since the + * S3C2410 generates the correct ALE/CLE signaling automatically, we + * do not need to use hwcontrol. +*/ + +static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + register struct nand_chip *this = mtd->priv; + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + + writeb(readcmd, info->regs + S3C2410_NFCMD); + } + writeb(command, info->regs + S3C2410_NFCMD); + + /* Set ALE and clear CLE to start address cycle */ + + if (column != -1 || page_addr != -1) { + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + writeb(column, info->regs + S3C2410_NFADDR); + } + if (page_addr != -1) { + writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR); + writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR); + /* One more address cycle for higher density devices */ + if (this->chipsize & 0x0c000000) + writeb((unsigned char) ((page_addr >> 16) & 0x0f), + info->regs + S3C2410_NFADDR); + } + /* Latch in address */ + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + + udelay(this->chip_delay); + writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD); + + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + + +/* s3c2410_nand_devready() + * + * returns 0 if the nand is busy, 1 if it is ready +*/ + +static int s3c2410_nand_devready(struct mtd_info *mtd) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; +} + +/* ECC handling functions */ + +static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", + mtd, dat, read_ecc, calc_ecc); + + pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", + read_ecc[0], read_ecc[1], read_ecc[2], + calc_ecc[0], calc_ecc[1], calc_ecc[2]); + + if (read_ecc[0] == calc_ecc[0] && + read_ecc[1] == calc_ecc[1] && + read_ecc[2] == calc_ecc[2]) + return 0; + + /* we curently have no method for correcting the error */ + + return -1; +} + +static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2410_NFCONF); + ctrl |= S3C2410_NFCONF_INITECC; + writel(ctrl, info->regs + S3C2410_NFCONF); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + + ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); + ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); + ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); + + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", + ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + + +/* over-ride the standard functions for a little more speed? */ + +static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + readsb(this->IO_ADDR_R, buf, len); +} + +static void s3c2410_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + writesb(this->IO_ADDR_W, buf, len); +} + +/* device management functions */ + +static int s3c2410_nand_remove(struct device *dev) +{ + struct s3c2410_nand_info *info = to_nand_info(dev); + + dev_set_drvdata(dev, NULL); + + if (info == NULL) + return 0; + + /* first thing we need to do is release all our mtds + * and their partitions, then go through freeing the + * resources used + */ + + if (info->mtds != NULL) { + struct s3c2410_nand_mtd *ptr = info->mtds; + int mtdno; + + for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { + pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); + nand_release(&ptr->mtd); + } + + kfree(info->mtds); + } + + /* free the common resources */ + + if (info->clk != NULL && !IS_ERR(info->clk)) { + clk_disable(info->clk); + clk_unuse(info->clk); + clk_put(info->clk); + } + + if (info->regs != NULL) { + iounmap(info->regs); + info->regs = NULL; + } + + if (info->area != NULL) { + release_resource(info->area); + kfree(info->area); + info->area = NULL; + } + + kfree(info); + + return 0; +} + +#ifdef CONFIG_MTD_PARTITIONS +static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *mtd, + struct s3c2410_nand_set *set) +{ + if (set == NULL) + return add_mtd_device(&mtd->mtd); + + if (set->nr_partitions > 0 && set->partitions != NULL) { + return add_mtd_partitions(&mtd->mtd, + set->partitions, + set->nr_partitions); + } + + return add_mtd_device(&mtd->mtd); +} +#else +static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *mtd, + struct s3c2410_nand_set *set) +{ + return add_mtd_device(&mtd->mtd); +} +#endif + +/* s3c2410_nand_init_chip + * + * init a single instance of an chip +*/ + +static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd, + struct s3c2410_nand_set *set) +{ + struct nand_chip *chip = &nmtd->chip; + + chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; + chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; + chip->hwcontrol = s3c2410_nand_hwcontrol; + chip->dev_ready = s3c2410_nand_devready; + chip->cmdfunc = s3c2410_nand_command; + chip->write_buf = s3c2410_nand_write_buf; + chip->read_buf = s3c2410_nand_read_buf; + chip->select_chip = s3c2410_nand_select_chip; + chip->chip_delay = 50; + chip->priv = nmtd; + chip->options = 0; + chip->controller = &info->controller; + + nmtd->info = info; + nmtd->mtd.priv = chip; + nmtd->set = set; + + if (hardware_ecc) { + chip->correct_data = s3c2410_nand_correct_data; + chip->enable_hwecc = s3c2410_nand_enable_hwecc; + chip->calculate_ecc = s3c2410_nand_calculate_ecc; + chip->eccmode = NAND_ECC_HW3_512; + chip->autooob = &nand_hw_eccoob; + } else { + chip->eccmode = NAND_ECC_SOFT; + } +} + +/* s3c2410_nand_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices +*/ + +static int s3c2410_nand_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c2410_platform_nand *plat = to_nand_plat(dev); + struct s3c2410_nand_info *info; + struct s3c2410_nand_mtd *nmtd; + struct s3c2410_nand_set *sets; + struct resource *res; + int err = 0; + int size; + int nr_sets; + int setno; + + pr_debug("s3c2410_nand_probe(%p)\n", dev); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR PFX "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info, sizeof(*info)); + dev_set_drvdata(dev, info); + + spin_lock_init(&info->controller.lock); + + /* get the clock source and enable it */ + + info->clk = clk_get(dev, "nand"); + if (IS_ERR(info->clk)) { + printk(KERN_ERR PFX "failed to get clock"); + err = -ENOENT; + goto exit_error; + } + + clk_use(info->clk); + clk_enable(info->clk); + + /* allocate and map the resource */ + + res = pdev->resource; /* assume that the flash has one resource */ + size = res->end - res->start + 1; + + info->area = request_mem_region(res->start, size, pdev->name); + + if (info->area == NULL) { + printk(KERN_ERR PFX "cannot reserve register region\n"); + err = -ENOENT; + goto exit_error; + } + + info->device = dev; + info->platform = plat; + info->regs = ioremap(res->start, size); + + if (info->regs == NULL) { + printk(KERN_ERR PFX "cannot reserve register region\n"); + err = -EIO; + goto exit_error; + } + + printk(KERN_INFO PFX "mapped registers at %p\n", info->regs); + + /* initialise the hardware */ + + err = s3c2410_nand_inithw(info, dev); + if (err != 0) + goto exit_error; + + sets = (plat != NULL) ? plat->sets : NULL; + nr_sets = (plat != NULL) ? plat->nr_sets : 1; + + info->mtd_count = nr_sets; + + /* allocate our information */ + + size = nr_sets * sizeof(*info->mtds); + info->mtds = kmalloc(size, GFP_KERNEL); + if (info->mtds == NULL) { + printk(KERN_ERR PFX "failed to allocate mtd storage\n"); + err = -ENOMEM; + goto exit_error; + } + + memzero(info->mtds, size); + + /* initialise all possible chips */ + + nmtd = info->mtds; + + for (setno = 0; setno < nr_sets; setno++, nmtd++) { + pr_debug("initialising set %d (%p, info %p)\n", + setno, nmtd, info); + + s3c2410_nand_init_chip(info, nmtd, sets); + + nmtd->scan_res = nand_scan(&nmtd->mtd, + (sets) ? sets->nr_chips : 1); + + if (nmtd->scan_res == 0) { + s3c2410_nand_add_partition(info, nmtd, sets); + } + + if (sets != NULL) + sets++; + } + + pr_debug("initialised ok\n"); + return 0; + + exit_error: + s3c2410_nand_remove(dev); + + if (err == 0) + err = -EINVAL; + return err; +} + +static struct device_driver s3c2410_nand_driver = { + .name = "s3c2410-nand", + .bus = &platform_bus_type, + .probe = s3c2410_nand_probe, + .remove = s3c2410_nand_remove, +}; + +static int __init s3c2410_nand_init(void) +{ + printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); + return driver_register(&s3c2410_nand_driver); +} + +static void __exit s3c2410_nand_exit(void) +{ + driver_unregister(&s3c2410_nand_driver); +} + +module_init(s3c2410_nand_init); +module_exit(s3c2410_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/sharpsl.c @@ -0,0 +1,260 @@ +/* + * drivers/mtd/nand/sharpsl.c + * + * Copyright (C) 2004 Richard Purdie + * + * $Id: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $ + * + * Based on Sharp's NAND driver sharp_sl.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *sharpsl_io_base; +static int sharpsl_phys_base = 0x0C000000; + +/* register offset */ +#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ +#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ +#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ +#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ +#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ + +/* Flash control bit */ +#define FLRYBY (1 << 5) +#define FLCE1 (1 << 4) +#define FLWP (1 << 3) +#define FLALE (1 << 2) +#define FLCLE (1 << 1) +#define FLCE0 (1 << 0) + + +/* + * MTD structure for SharpSL + */ +static struct mtd_info *sharpsl_mtd = NULL; + +/* + * Define partitions for flash device + */ +#define DEFAULT_NUM_PARTITIONS 3 + +static int nr_partitions; +static struct mtd_partition sharpsl_nand_default_partition_info[] = { + { + .name = "System Area", + .offset = 0, + .size = 7 * 1024 * 1024, + }, + { + .name = "Root Filesystem", + .offset = 7 * 1024 * 1024, + .size = 30 * 1024 * 1024, + }, + { + .name = "Home Filesystem", + .offset = MTDPART_OFS_APPEND , + .size = MTDPART_SIZ_FULL , + }, +}; + +/* + * hardware specific access to control-lines + */ +static void +sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd) +{ + switch (cmd) { + case NAND_CTL_SETCLE: + writeb(readb(FLASHCTL) | FLCLE, FLASHCTL); + break; + case NAND_CTL_CLRCLE: + writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL); + break; + + case NAND_CTL_SETALE: + writeb(readb(FLASHCTL) | FLALE, FLASHCTL); + break; + case NAND_CTL_CLRALE: + writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL); + break; + + case NAND_CTL_SETNCE: + writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL); + break; + case NAND_CTL_CLRNCE: + writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL); + break; + } +} + +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr sharpsl_bbt = { + .options = 0, + .offs = 4, + .len = 2, + .pattern = scan_ff_pattern +}; + +static int +sharpsl_nand_dev_ready(struct mtd_info* mtd) +{ + return !((readb(FLASHCTL) & FLRYBY) == 0); +} + +static void +sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode) +{ + writeb(0 ,ECCCLRR); +} + +static int +sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + ecc_code[0] = ~readb(ECCLPUB); + ecc_code[1] = ~readb(ECCLPLB); + ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; + return readb(ECCCNTR) != 0; +} + + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + + +/* + * Main initialization routine + */ +int __init +sharpsl_nand_init(void) +{ + struct nand_chip *this; + struct mtd_partition* sharpsl_partition_info; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); + if (!sharpsl_mtd) { + printk ("Unable to allocate SharpSL NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical adress */ + sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); + if(!sharpsl_io_base){ + printk("ioremap to access Sharp SL NAND chip failed\n"); + kfree(sharpsl_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&sharpsl_mtd[1]); + + /* Initialize structures */ + memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + sharpsl_mtd->priv = this; + + /* + * PXA initialize + */ + writeb(readb(FLASHCTL) | FLWP, FLASHCTL); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = FLASHIO; + this->IO_ADDR_W = FLASHIO; + /* Set address of hardware control function */ + this->hwcontrol = sharpsl_nand_hwcontrol; + this->dev_ready = sharpsl_nand_dev_ready; + /* 15 us command delay time */ + this->chip_delay = 15; + /* set eccmode using hardware ECC */ + this->eccmode = NAND_ECC_HW3_256; + this->enable_hwecc = sharpsl_nand_enable_hwecc; + this->calculate_ecc = sharpsl_nand_calculate_ecc; + this->correct_data = nand_correct_data; + this->badblock_pattern = &sharpsl_bbt; + + /* Scan to find existence of the device */ + err=nand_scan(sharpsl_mtd,1); + if (err) { + iounmap(sharpsl_io_base); + kfree(sharpsl_mtd); + return err; + } + + /* Register the partitions */ + sharpsl_mtd->name = "sharpsl-nand"; + nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, + &sharpsl_partition_info, 0); + + if (nr_partitions <= 0) { + nr_partitions = DEFAULT_NUM_PARTITIONS; + sharpsl_partition_info = sharpsl_nand_default_partition_info; + if (machine_is_poodle()) { + sharpsl_partition_info[1].size=30 * 1024 * 1024; + } else if (machine_is_corgi() || machine_is_shepherd()) { + sharpsl_partition_info[1].size=25 * 1024 * 1024; + } else if (machine_is_husky()) { + sharpsl_partition_info[1].size=53 * 1024 * 1024; + } + } + + if (machine_is_husky()) { + /* Need to use small eraseblock size for backward compatibility */ + sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS; + } + + add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); + + /* Return happy */ + return 0; +} +module_init(sharpsl_nand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit sharpsl_nand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1]; + + /* Release resources, unregister device */ + nand_release(sharpsl_mtd); + + iounmap(sharpsl_io_base); + + /* Free the MTD device structure */ + kfree(sharpsl_mtd); +} +module_exit(sharpsl_nand_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); --- linux-2.4.21/drivers/mtd/nand/spia.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nand/spia.c @@ -1,14 +1,14 @@ /* * drivers/mtd/nand/spia.c * - * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * * 10-29-2001 TG change to support hardwarespecific access * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.16 2002/03/05 13:50:47 dwmw2 Exp $ + * $Id: spia.c,v 1.24 2004/11/04 12:53:10 gleixner 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 @@ -20,6 +20,8 @@ * a 64Mibit (8MiB x 8 bits) NAND flash device. */ +#include +#include #include #include #include @@ -35,14 +37,14 @@ /* * Values specific to the SPIA board (used with EP7212 processor) */ -#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ -#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ -#define SPIA_PEDR = 0x0080 /* +#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ +#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ +#define SPIA_PEDR 0x0080 /* * IO offset to Port E data register * where the CLE, ALE and NCE pins * are wired to. */ -#define SPIA_PEDDR = 0x00c0 /* +#define SPIA_PEDDR 0x00c0 /* * IO offset to Port E data direction * register so we can control the IO * lines. @@ -57,26 +59,25 @@ static int spia_pedr = SPIA_PEDR; static int spia_peddr = SPIA_PEDDR; -MODULE_PARM(spia_io_base, "i"); -MODULE_PARM(spia_fio_base, "i"); -MODULE_PARM(spia_pedr, "i"); -MODULE_PARM(spia_peddr, "i"); - -__setup("spia_io_base=",spia_io_base); -__setup("spia_fio_base=",spia_fio_base); -__setup("spia_pedr=",spia_pedr); -__setup("spia_peddr=",spia_peddr); +module_param(spia_io_base, int, 0); +module_param(spia_fio_base, int, 0); +module_param(spia_pedr, int, 0); +module_param(spia_peddr, int, 0); /* * 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 } + { + .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 @@ -84,7 +85,7 @@ /* * hardware specific access to control-lines */ -void spia_hwcontrol(int cmd){ +static void spia_hwcontrol(struct mtd_info *mtd, int cmd){ switch(cmd){ @@ -131,37 +132,19 @@ (*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07; /* Set address of NAND IO lines */ - this->IO_ADDR_R = spia_fio_base; - this->IO_ADDR_W = spia_fio_base; + this->IO_ADDR_R = (void __iomem *) spia_fio_base; + this->IO_ADDR_W = (void __iomem *) spia_fio_base; /* Set address of hardware control function */ this->hwcontrol = spia_hwcontrol; /* 15 us command delay time */ this->chip_delay = 15; /* Scan to find existence of the device */ - if (nand_scan (spia_mtd)) { + if (nand_scan (spia_mtd, 1)) { 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; - } - - /* Allocate memory for internal data buffer */ - this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); - if (!this->data_cache) { - printk ("Unable to allocate NAND data cache for SPIA.\n"); - kfree (this->data_buf); - kfree (spia_mtd); - return = -ENOMEM; - } - this->cache_page = -1; - /* Register the partitions */ add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); @@ -176,14 +159,8 @@ #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); - kfree (this->page_cache); + /* Release resources, unregister device */ + nand_release (spia_mtd); /* Free the MTD device structure */ kfree (spia_mtd); @@ -192,5 +169,5 @@ #endif MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill + * + * This program is free software; you can 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 + * TI fido board. It supports 32MiB and 64MiB cards + * + * $Id: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * MTD structure for TOTO board + */ +static struct mtd_info *toto_mtd = NULL; + +static unsigned long toto_io_base = OMAP_FLASH_1_BASE; + +#define CONFIG_NAND_WORKAROUND 1 + +#define NAND_NCE 0x4000 +#define NAND_CLE 0x1000 +#define NAND_ALE 0x0002 +#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE) + +#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0) +#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE) +#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */ +#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2) +#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0) +#else +#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0) +#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE) +#endif +#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0) +#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE) + +/* + * Define partitions for flash devices + */ + +static struct mtd_partition partition_info64M[] = { + { .name = "toto kernel partition 1", + .offset = 0, + .size = 2 * SZ_1M }, + { .name = "toto file sys partition 2", + .offset = 2 * SZ_1M, + .size = 14 * SZ_1M }, + { .name = "toto user partition 3", + .offset = 16 * SZ_1M, + .size = 16 * SZ_1M }, + { .name = "toto devboard extra partition 4", + .offset = 32 * SZ_1M, + .size = 32 * SZ_1M }, +}; + +static struct mtd_partition partition_info32M[] = { + { .name = "toto kernel partition 1", + .offset = 0, + .size = 2 * SZ_1M }, + { .name = "toto file sys partition 2", + .offset = 2 * SZ_1M, + .size = 14 * SZ_1M }, + { .name = "toto user partition 3", + .offset = 16 * SZ_1M, + .size = 16 * SZ_1M }, +}; + +#define NUM_PARTITIONS32M 3 +#define NUM_PARTITIONS64M 4 +/* + * hardware specific access to control-lines +*/ + +static void toto_hwcontrol(struct mtd_info *mtd, int cmd) +{ + + udelay(1); /* hopefully enough time for tc make proceding write to clear */ + switch(cmd){ + + case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; + case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; + + case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; + case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; + + case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; + case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; + } + udelay(1); /* allow time to ensure gpio state to over take memory write */ +} + +/* + * Main initialization routine + */ +int __init toto_init (void) +{ + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!toto_mtd) { + printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&toto_mtd[1]); + + /* Initialize structures */ + memset((char *) toto_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + toto_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = toto_io_base; + this->IO_ADDR_W = toto_io_base; + this->hwcontrol = toto_hwcontrol; + this->dev_ready = NULL; + /* 25 us command delay time */ + this->chip_delay = 30; + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ + if (nand_scan (toto_mtd, 1)) { + err = -ENXIO; + goto out_mtd; + } + + /* Register the partitions */ + switch(toto_mtd->size){ + case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; + case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break; + default: { + printk (KERN_WARNING "Unsupported Nand device\n"); + err = -ENXIO; + goto out_buf; + } + } + + gpioreserve(NAND_MASK); /* claim our gpios */ + archflashwp(0,0); /* open up flash for writing */ + + goto out; + +out_buf: + kfree (this->data_buf); +out_mtd: + kfree (toto_mtd); +out: + return err; +} + +module_init(toto_init); + +/* + * Clean up routine + */ +static void __exit toto_cleanup (void) +{ + /* Release resources, unregister device */ + nand_release (toto_mtd); + + /* Free the MTD device structure */ + kfree (toto_mtd); + + /* stop flash writes */ + archflashwp(0,1); + + /* release gpios to system */ + gpiorelease(NAND_MASK); +} +module_exit(toto_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Woodruff "); +MODULE_DESCRIPTION("Glue layer for NAND flash on toto board"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/tx4925ndfmc.c @@ -0,0 +1,416 @@ +/* + * drivers/mtd/tx4925ndfmc.c + * + * Overview: + * This is a device driver for the NAND flash device found on the + * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports + * 16MiB, 32MiB and 64MiB cards. + * + * Author: MontaVista Software, Inc. source@mvista.com + * + * Derived from drivers/mtd/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $ + * + * Copyright (C) 2001 Toshiba Corporation + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct nand_oobinfo jffs2_oobinfo; + +/* + * MTD structure for RBTX4925 board + */ +static struct mtd_info *tx4925ndfmc_mtd = NULL; + +/* + * Define partitions for flash devices + */ + +static struct mtd_partition partition_info16k[] = { + { .name = "RBTX4925 flash partition 1", + .offset = 0, + .size = 8 * 0x00100000 }, + { .name = "RBTX4925 flash partition 2", + .offset = 8 * 0x00100000, + .size = 8 * 0x00100000 }, +}; + +static struct mtd_partition partition_info32k[] = { + { .name = "RBTX4925 flash partition 1", + .offset = 0, + .size = 8 * 0x00100000 }, + { .name = "RBTX4925 flash partition 2", + .offset = 8 * 0x00100000, + .size = 24 * 0x00100000 }, +}; + +static struct mtd_partition partition_info64k[] = { + { .name = "User FS", + .offset = 0, + .size = 16 * 0x00100000 }, + { .name = "RBTX4925 flash partition 2", + .offset = 16 * 0x00100000, + .size = 48 * 0x00100000}, +}; + +static struct mtd_partition partition_info128k[] = { + { .name = "Skip bad section", + .offset = 0, + .size = 16 * 0x00100000 }, + { .name = "User FS", + .offset = 16 * 0x00100000, + .size = 112 * 0x00100000 }, +}; +#define NUM_PARTITIONS16K 2 +#define NUM_PARTITIONS32K 2 +#define NUM_PARTITIONS64K 2 +#define NUM_PARTITIONS128K 2 + +/* + * hardware specific access to control-lines +*/ +static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) +{ + + switch(cmd){ + + case NAND_CTL_SETCLE: + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE; + break; + case NAND_CTL_CLRCLE: + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE; + break; + case NAND_CTL_SETALE: + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE; + break; + case NAND_CTL_CLRALE: + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE; + break; + case NAND_CTL_SETNCE: + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE; + break; + case NAND_CTL_CLRNCE: + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE; + break; + case NAND_CTL_SETWP: + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE; + break; + case NAND_CTL_CLRWP: + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE; + break; + } +} + +/* +* read device ready pin +*/ +static int tx4925ndfmc_device_ready(struct mtd_info *mtd) +{ + int ready; + ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1; + return ready; +} +void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* reset first */ + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK; + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB; +} +static void tx4925ndfmc_disable_ecc(void) +{ + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; +} +static void tx4925ndfmc_enable_read_ecc(void) +{ + tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; + tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ; +} +void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ + int i; + u_char *ecc = ecc_code; + tx4925ndfmc_enable_read_ecc(); + for (i = 0;i < 6;i++,ecc++) + *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr)); + tx4925ndfmc_disable_ecc(); +} +void tx4925ndfmc_device_setup(void) +{ + + *(unsigned char *)0xbb005000 &= ~0x08; + + /* reset NDFMC */ + tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST; + while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST); + + /* setup BusSeparete, Hold Time, Strobe Pulse Width */ + tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0; + tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW; +} +static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return tx4925_read_nfmc(this->IO_ADDR_R); +} + +static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + tx4925_write_nfmc(byte, this->IO_ADDR_W); +} + +static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); +} + +static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/* + * Send command to NAND device + */ +static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + this->write_byte(mtd, readcmd); + } + this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) + this->write_byte(mtd, column); + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + /* Turn off WE */ + this->hwcontrol (mtd, NAND_CTL_CLRWP); + return; + + case NAND_CMD_SEQIN: + /* Turn on WE */ + this->hwcontrol (mtd, NAND_CTL_SETWP); + return; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +#ifdef CONFIG_MTD_CMDLINE_PARTS +extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio +n **pparts, char *); +#endif + +/* + * Main initialization routine + */ +extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); +int __init tx4925ndfmc_init (void) +{ + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!tx4925ndfmc_mtd) { + printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + tx4925ndfmc_device_setup(); + + /* io is indirect via a register so don't need to ioremap address */ + + /* Get pointer to private data */ + this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]); + + /* Initialize structures */ + memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + tx4925ndfmc_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr); + this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr); + this->hwcontrol = tx4925ndfmc_hwcontrol; + this->enable_hwecc = tx4925ndfmc_enable_hwecc; + this->calculate_ecc = tx4925ndfmc_readecc; + this->correct_data = nand_correct_data; + this->eccmode = NAND_ECC_HW6_512; + this->dev_ready = tx4925ndfmc_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->read_byte = tx4925ndfmc_nand_read_byte; + this->write_byte = tx4925ndfmc_nand_write_byte; + this->cmdfunc = tx4925ndfmc_nand_command; + this->write_buf = tx4925ndfmc_nand_write_buf; + this->read_buf = tx4925ndfmc_nand_read_buf; + this->verify_buf = tx4925ndfmc_nand_verify_buf; + + /* Scan to find existance of the device */ + if (nand_scan (tx4925ndfmc_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } + + /* Register the partitions */ +#ifdef CONFIG_MTD_CMDLINE_PARTS + { + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc"); + if (mtd_parts_nb > 0) + add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb); + else + add_mtd_device(tx4925ndfmc_mtd); + } +#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */ + switch(tx4925ndfmc_mtd->size){ + case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break; + case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break; + case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; + case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; + default: { + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; + goto out_ior; + } + } +#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ + goto out; + +out_ior: +out: + return err; +} + +module_init(tx4925ndfmc_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit tx4925ndfmc_cleanup (void) +{ + /* Release resources, unregister device */ + nand_release (tx4925ndfmc_mtd); + + /* Free the MTD device structure */ + kfree (tx4925ndfmc_mtd); +} +module_exit(tx4925ndfmc_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alice Hennessy "); +MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); --- /dev/null +++ linux-2.4.21/drivers/mtd/nand/tx4938ndfmc.c @@ -0,0 +1,406 @@ +/* + * drivers/mtd/nand/tx4938ndfmc.c + * + * Overview: + * This is a device driver for the NAND flash device connected to + * TX4938 internal NAND Memory Controller. + * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit. + * + * Author: source@mvista.com + * + * Based on spia.c by Steven J. Hill + * + * $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $ + * + * Copyright (C) 2000-2001 Toshiba Corporation + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct nand_oobinfo jffs2_oobinfo; + +/* + * MTD structure for TX4938 NDFMC + */ +static struct mtd_info *tx4938ndfmc_mtd; + +/* + * Define partitions for flash device + */ +#define flush_wb() (void)tx4938_ndfmcptr->mcr; + +#define NUM_PARTITIONS 3 +#define NUMBER_OF_CIS_BLOCKS 24 +#define SIZE_OF_BLOCK 0x00004000 +#define NUMBER_OF_BLOCK_PER_ZONE 1024 +#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK) +#ifndef CONFIG_MTD_CMDLINE_PARTS +/* + * You can use the following sample of MTD partitions + * on the NAND Flash Memory 32MB or more. + * + * The following figure shows the image of the sample partition on + * the 32MB NAND Flash Memory. + * + * Block No. + * 0 +-----------------------------+ ------ + * | CIS | ^ + * 24 +-----------------------------+ | + * | kernel image | | Zone 0 + * | | | + * +-----------------------------+ | + * 1023 | unused area | v + * +-----------------------------+ ------ + * 1024 | JFFS2 | ^ + * | | | + * | | | Zone 1 + * | | | + * | | | + * | | v + * 2047 +-----------------------------+ ------ + * + */ +static struct mtd_partition partition_info[NUM_PARTITIONS] = { + { + .name = "RBTX4938 CIS Area", + .offset = 0, + .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK), + .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ + }, + { + .name = "RBTX4938 kernel image", + .offset = MTDPART_OFS_APPEND, + .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */ + .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ + }, + { + .name = "Root FS (JFFS2)", + .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */ + .size = MTDPART_SIZ_FULL + }, +}; +#endif + +static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) +{ + switch (cmd) { + case NAND_CTL_SETCLE: + tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE; + break; + case NAND_CTL_CLRCLE: + tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE; + break; + case NAND_CTL_SETALE: + tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE; + break; + case NAND_CTL_CLRALE: + tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE; + break; + /* TX4938_NDFMCR_CE bit is 0:high 1:low */ + case NAND_CTL_SETNCE: + tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE; + break; + case NAND_CTL_CLRNCE: + tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE; + break; + case NAND_CTL_SETWP: + tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE; + break; + case NAND_CTL_CLRWP: + tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE; + break; + } +} +static int tx4938ndfmc_dev_ready(struct mtd_info *mtd) +{ + flush_wb(); + return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY); +} +static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + u32 mcr = tx4938_ndfmcptr->mcr; + mcr &= ~TX4938_NDFMCR_ECC_ALL; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ; + ecc_code[1] = tx4938_ndfmcptr->dtr; + ecc_code[0] = tx4938_ndfmcptr->dtr; + ecc_code[2] = tx4938_ndfmcptr->dtr; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; +} +static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u32 mcr = tx4938_ndfmcptr->mcr; + mcr &= ~TX4938_NDFMCR_ECC_ALL; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; + tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON; +} + +static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return tx4938_read_nfmc(this->IO_ADDR_R); +} + +static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + tx4938_write_nfmc(byte, this->IO_ADDR_W); +} + +static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); +} + +static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/* + * Send command to NAND device + */ +static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + this->write_byte(mtd, readcmd); + } + this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) + this->write_byte(mtd, column); + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + /* Turn off WE */ + this->hwcontrol (mtd, NAND_CTL_CLRWP); + return; + + case NAND_CMD_SEQIN: + /* Turn on WE */ + this->hwcontrol (mtd, NAND_CTL_SETWP); + return; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* wait until command is processed */ + while (!this->dev_ready(mtd)); +} + +#ifdef CONFIG_MTD_CMDLINE_PARTS +extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); +#endif +/* + * Main initialization routine + */ +int __init tx4938ndfmc_init (void) +{ + struct nand_chip *this; + int bsprt = 0, hold = 0xf, spw = 0xf; + int protected = 0; + + if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) { + printk("TX4938 NDFMC: disabled by IOC PIOSEL\n"); + return -ENODEV; + } + bsprt = 1; + hold = 2; + spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */ + + if ((tx4938_ccfgptr->pcfg & + (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) + != TX4938_PCFG_NDF_SEL) { + printk("TX4938 NDFMC: disabled by PCFG.\n"); + return -ENODEV; + } + + /* reset NDFMC */ + tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST; + while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST) + ; + /* setup BusSeparete, Hold Time, Strobe Pulse Width */ + tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0; + tx4938_ndfmcptr->spr = hold << 4 | spw; + + /* Allocate memory for MTD device structure and private data */ + tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!tx4938ndfmc_mtd) { + printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]); + + /* Initialize structures */ + memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + tx4938ndfmc_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr; + this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr; + this->hwcontrol = tx4938ndfmc_hwcontrol; + this->dev_ready = tx4938ndfmc_dev_ready; + this->calculate_ecc = tx4938ndfmc_calculate_ecc; + this->correct_data = nand_correct_data; + this->enable_hwecc = tx4938ndfmc_enable_hwecc; + this->eccmode = NAND_ECC_HW3_256; + this->chip_delay = 100; + this->read_byte = tx4938ndfmc_nand_read_byte; + this->write_byte = tx4938ndfmc_nand_write_byte; + this->cmdfunc = tx4938ndfmc_nand_command; + this->write_buf = tx4938ndfmc_nand_write_buf; + this->read_buf = tx4938ndfmc_nand_read_buf; + this->verify_buf = tx4938ndfmc_nand_verify_buf; + + /* Scan to find existance of the device */ + if (nand_scan (tx4938ndfmc_mtd, 1)) { + kfree (tx4938ndfmc_mtd); + return -ENXIO; + } + + if (protected) { + printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); + tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + { + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc"); + if (mtd_parts_nb > 0) + add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb); + else + add_mtd_device(tx4938ndfmc_mtd); + } +#else + add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS ); +#endif + + return 0; +} +module_init(tx4938ndfmc_init); + +/* + * Clean up routine + */ +static void __exit tx4938ndfmc_cleanup (void) +{ + /* Release resources, unregister device */ + nand_release (tx4938ndfmc_mtd); + + /* Free the MTD device structure */ + kfree (tx4938ndfmc_mtd); +} +module_exit(tx4938ndfmc_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alice Hennessy "); +MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC"); --- linux-2.4.21/drivers/mtd/nftlcore.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nftlcore.c @@ -1,7 +1,7 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftlcore.c,v 1.87 2002/09/13 14:35:33 dwmw2 Exp $ */ +/* $Id: nftlcore.c,v 1.97 2004/11/16 18:28:59 dwmw2 Exp $ */ /* The contents of this file are distributed under the GNU General @@ -23,15 +23,13 @@ #include #include #include -#include +#include -#ifdef CONFIG_KMOD #include -#endif #include #include #include -#include +#include /* maximum number of loops while examining next block, to have a chance to detect consistency problems (they should never happen @@ -39,187 +37,107 @@ #define MAX_LOOPS 10000 -/* NFTL block device stuff */ -#define MAJOR_NR NFTL_MAJOR -#define DEVICE_REQUEST nftl_request -#define DEVICE_OFF(device) - - -#include -#include - -/* 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<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"); + if (mtd->type != MTD_NANDFLASH) return; - } - } - if (firstfree == -1) { - printk(KERN_WARNING "No more NFTL slot available\n"); + /* OK, this is moderately ugly. But probably safe. Alternatives? */ + if (memcmp(mtd->name, "DiskOnChip", 10)) + return; + + if (!mtd->block_isbad) { + printk(KERN_ERR +"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" +"Please use the new diskonchip driver under the NAND subsystem.\n"); return; } + DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); + nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); + if (!nftl) { - printk(KERN_WARNING "Out of memory for NFTL data structures\n"); + printk(KERN_WARNING "NFTL: out of memory for data structures\n"); return; } + memset(nftl, 0, sizeof(*nftl)); - init_MUTEX(&nftl->mutex); - - nftl->mtd = mtd; + nftl->mbd.mtd = mtd; + nftl->mbd.devnum = -1; + nftl->mbd.blksize = 512; + nftl->mbd.tr = tr; + memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); + nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; if (NFTL_mount(nftl) < 0) { - printk(KERN_WARNING "Could not mount NFTL device\n"); + printk(KERN_WARNING "NFTL: could not mount 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; + /* Calculate geometry */ nftl->cylinders = 1024; nftl->heads = 16; temp = nftl->cylinders * nftl->heads; - nftl->sectors = nftl->nr_sects / temp; - if (nftl->nr_sects % temp) { + nftl->sectors = nftl->mbd.size / temp; + if (nftl->mbd.size % temp) { nftl->sectors++; temp = nftl->cylinders * nftl->sectors; - nftl->heads = nftl->nr_sects / temp; + nftl->heads = nftl->mbd.size / temp; - if (nftl->nr_sects % temp) { + if (nftl->mbd.size % temp) { nftl->heads++; temp = nftl->heads * nftl->sectors; - nftl->cylinders = nftl->nr_sects / temp; + nftl->cylinders = nftl->mbd.size / temp; } } - if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) { - printk(KERN_WARNING "Cannot calculate an NFTL geometry to " - "match size of 0x%x.\n", nftl->nr_sects); - printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", + if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { + /* + Oh no we don't have + mbd.size == heads * cylinders * sectors + */ + printk(KERN_WARNING "NFTL: cannot calculate a geometry to " + "match size of 0x%lx.\n", nftl->mbd.size); + printk(KERN_WARNING "NFTL: 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; */ + (long)nftl->cylinders * (long)nftl->heads * + (long)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<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 (add_mtd_blktrans_dev(&nftl->mbd)) { 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); - } +#ifdef PSYCHO_DEBUG + printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); +#endif } -static void NFTL_notify_remove(struct mtd_info *mtd) +static void nftl_remove_dev(struct mtd_blktrans_dev *dev) { - int i; + struct NFTLrecord *nftl = (void *)dev; - for (i = 0; i < MAX_NFTLS; i++) { - if (NFTLs[i] && NFTLs[i]->mtd == mtd) - NFTL_unsetup(i); - } + DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); + + del_mtd_blktrans_dev(dev); + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); + kfree(nftl); } #ifdef CONFIG_NFTL_RW @@ -303,7 +221,7 @@ targetEUN = thisEUN; for (block = 0; block < nftl->EraseSize / 512; block ++) { - MTD_READOOB(nftl->mtd, + MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + (block * 512), 16 , &retlen, (char *)&oob); if (block == 2) { @@ -420,7 +338,7 @@ 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, + MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 8, &retlen, (char *)&oob.u); } @@ -444,17 +362,19 @@ if (BlockMap[block] == BLOCK_NIL) continue; - ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), - 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf); if (ret < 0) { - ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), 512, &retlen, - movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + movebuf); if (ret != -EIO) printk("Error went away on retry.\n"); } - MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512), - 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + memset(&oob, 0xff, sizeof(struct nftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo); } /* add the header so that it is now a valid chain */ @@ -462,7 +382,7 @@ = cpu_to_le16(thisVUC); oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; - MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, + MTD_WRITEOOB(nftl->mbd.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. */ @@ -484,7 +404,6 @@ 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 { @@ -502,7 +421,7 @@ return targetEUN; } -u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) +static 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 @@ -582,7 +501,7 @@ lastEUN = writeEUN; - MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, + MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci); DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", @@ -670,12 +589,12 @@ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; /* ... and on the flash itself */ - MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, + MTD_READOOB(nftl->mbd.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, + MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, &retlen, (char *)&oob.u); /* we link the new block to the chain only after the @@ -685,13 +604,13 @@ /* Both in our cache... */ nftl->ReplUnitTable[lastEUN] = writeEUN; /* ... and on the flash itself */ - MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, + MTD_READOOB(nftl->mbd.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, + MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, 8, &retlen, (char *)&oob.u); } @@ -704,12 +623,14 @@ return 0xffff; } -static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer) +static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, + char *buffer) { + struct NFTLrecord *nftl = (void *)mbd; u16 writeEUN; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); size_t retlen; - u8 eccbuf[6]; + struct nftl_oob oob; writeEUN = NFTL_findwriteunit(nftl, block); @@ -720,16 +641,20 @@ return 1; } - MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); - /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ + memset(&oob, 0xff, sizeof(struct nftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; + MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo); + /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */ return 0; } #endif /* CONFIG_NFTL_RW */ -static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) +static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, + char *buffer) { + struct NFTLrecord *nftl = (void *)mbd; u16 lastgoodEUN; u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); @@ -742,7 +667,7 @@ if (thisEUN != BLOCK_NIL) { while (thisEUN < nftl->nb_blocks) { - if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs, + if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else @@ -761,13 +686,13 @@ case SECTOR_IGNORE: break; default: - printk("Unknown status for block %d in EUN %d: %x\n", + printk("Unknown status for block %ld in EUN %d: %x\n", block, thisEUN, status); break; } if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", block / (nftl->EraseSize / 512)); return 1; } @@ -782,265 +707,22 @@ } 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, NAND_ECC_DISKONCHIP)) + if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer)) 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 */ - return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, - (unsigned long *) arg); - -#ifdef BLKGETSIZE64 - case BLKGETSIZE64: - return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9, - (u64 *)arg); -#endif - - 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< 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<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<rq_dev)); - res = 0; /* fail */ - goto repeat; - } - - nftl = NFTLs[dev / (1<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++; - BLK_INC_USE_COUNT; - if (!get_mtd_device(thisNFTL->mtd, -1)) { - BLK_DEC_USE_COUNT; - return -ENXIO; - } - - return 0; -} - -static int nftl_release(struct inode *inode, struct file *fp) +static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) { - struct NFTLrecord *thisNFTL; - - thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; - - DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); - - if (thisNFTL->mtd->sync) - thisNFTL->mtd->sync(thisNFTL->mtd); - thisNFTL->usecount--; - BLK_DEC_USE_COUNT; + struct NFTLrecord *nftl = (void *)dev; - put_mtd_device(thisNFTL->mtd); + geo->heads = nftl->heads; + geo->sectors = nftl->sectors; + geo->cylinders = nftl->cylinders; 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 = -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) - owner: THIS_MODULE, -#endif - open: nftl_open, - release: nftl_release, - ioctl: nftl_ioctl -}; -#endif - - /**************************************************************************** * @@ -1048,49 +730,33 @@ * ****************************************************************************/ -static struct mtd_notifier nftl_notifier = { - add: NFTL_notify_add, - remove: NFTL_notify_remove + +static struct mtd_blktrans_ops nftl_tr = { + .name = "nftl", + .major = NFTL_MAJOR, + .part_bits = NFTL_PARTN_BITS, + .getgeo = nftl_getgeo, + .readsect = nftl_readblock, +#ifdef CONFIG_NFTL_RW + .writesect = nftl_writeblock, +#endif + .add_mtd = nftl_add_mtd, + .remove_dev = nftl_remove_dev, + .owner = THIS_MODULE, }; extern char nftlmountrev[]; -int __init init_nftl(void) +static int __init init_nftl(void) { - int i; - -#ifdef PRERELEASE - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.87 $, nftlmount.c %s\n", nftlmountrev); -#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 { - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); - - /* set block size to 1kB each */ - for (i = 0; i < 256; i++) { - nftl_blocksizes[i] = 1024; - } - blksize_size[MAJOR_NR] = nftl_blocksizes; - - add_gendisk(&nftl_gendisk); - } - - register_mtd_user(&nftl_notifier); + printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.97 $, nftlmount.c %s\n", nftlmountrev); - return 0; + return register_mtd_blktrans(&nftl_tr); } static void __exit cleanup_nftl(void) { - unregister_mtd_user(&nftl_notifier); - unregister_blkdev(MAJOR_NR, "nftl"); - - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - - del_gendisk(&nftl_gendisk); + deregister_mtd_blktrans(&nftl_tr); } module_init(init_nftl); --- linux-2.4.21/drivers/mtd/nftlmount.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/nftlmount.c @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.31 2002/11/15 16:34:43 dwmw2 Exp $ + * $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev 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 @@ -21,26 +21,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define __NO_VERSION__ #include -#include #include -#include -#include -#include -#include #include #include -#include -#include #include #include #include -#include #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.31 $"; +char nftlmountrev[]="$Revision: 1.40 $"; /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update @@ -50,7 +41,6 @@ static int find_boot_record(struct NFTLrecord *nftl) { struct nftl_uci1 h1; - struct nftl_oob oob; unsigned int block, boot_record_count = 0; size_t retlen; u8 buf[SECTORSIZE]; @@ -59,8 +49,12 @@ /* Assume logical EraseSize == physical erasesize for starting the scan. We'll sort it out later if we find a MediaHeader which says otherwise */ - nftl->EraseSize = nftl->mtd->erasesize; - nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; + /* Actually, we won't. The new DiskOnChip driver has already scanned + the MediaHeader and adjusted the virtual erasesize it presents in + the mtd device accordingly. We could even get rid of + nftl->EraseSize if there were any point in doing so. */ + nftl->EraseSize = nftl->mbd.mtd->erasesize; + nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; nftl->MediaUnit = BLOCK_NIL; nftl->SpareMediaUnit = BLOCK_NIL; @@ -71,12 +65,15 @@ /* Check for ANAND header first. Then can whinge if it's found but later checks fail */ - if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { + ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf); + /* We ignore ret in case the ECC of the MediaHeader is invalid + (which is apparently acceptable) */ + if (retlen != SECTORSIZE) { static int warncount = 5; if (warncount) { printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n", - block * nftl->EraseSize, nftl->mtd->index, ret); + block * nftl->EraseSize, nftl->mbd.mtd->index, ret); if (!--warncount) printk(KERN_WARNING "Further failures for this block will not be printed\n"); } @@ -87,16 +84,16 @@ /* ANAND\0 not found. Continue */ #if 0 printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", - block * nftl->EraseSize, nftl->mtd->index); + block * nftl->EraseSize, nftl->mbd.mtd->index); #endif continue; } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, - 8, &retlen, (char *)&h1)) < 0) { + if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&h1) < 0)) { printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", - block * nftl->EraseSize, nftl->mtd->index, ret); + block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; } @@ -106,23 +103,23 @@ */ if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n", - block * nftl->EraseSize, nftl->mtd->index, + block * nftl->EraseSize, nftl->mbd.mtd->index, le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1)); continue; } /* Finally reread to check ECC */ - if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { + if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, + &retlen, buf, (char *)&oob, NULL) < 0)) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", - block * nftl->EraseSize, nftl->mtd->index, ret); + block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; } /* Paranoia. Check the ANAND header is still there after the ECC read */ if (memcmp(buf, "ANAND", 6)) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n", - block * nftl->EraseSize, nftl->mtd->index); + block * nftl->EraseSize, nftl->mbd.mtd->index); printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); continue; @@ -137,8 +134,12 @@ printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n", nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); /* if (debug) Print both side by side */ + if (boot_record_count < 2) { + /* We haven't yet seen two real ones */ return -1; } + continue; + } if (boot_record_count == 1) nftl->SpareMediaUnit = block; @@ -154,6 +155,10 @@ memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); /* Do some sanity checks on it */ +#if 0 +The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual +erasesize based on UnitSizeFactor. So the erasesize we read from the mtd +device is already correct. if (mh->UnitSizeFactor == 0) { printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); } else if (mh->UnitSizeFactor < 0xfc) { @@ -163,9 +168,10 @@ } else if (mh->UnitSizeFactor != 0xff) { printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", mh->UnitSizeFactor); - nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor); - nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; + nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); + nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; } +#endif nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); @@ -182,7 +188,7 @@ return -1; } - nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); /* If we're not using the last sectors in the device for some reason, reduce nb_blocks accordingly so we forget they're there */ @@ -218,11 +224,13 @@ /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ for (i = 0; i < nftl->nb_blocks; i++) { +#if 0 +The new DiskOnChip driver already scanned the bad block table. Just query it. if ((i & (SECTORSIZE - 1)) == 0) { /* read one sector for every SECTORSIZE of blocks */ - if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize + + if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + i + SECTORSIZE, SECTORSIZE, &retlen, buf, - (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { + (char *)&oob, NULL)) < 0) { printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", ret); kfree(nftl->ReplUnitTable); @@ -233,6 +241,9 @@ /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ if (buf[i & (SECTORSIZE - 1)] != 0xff) nftl->ReplUnitTable[i] = BLOCK_RESERVED; +#endif + if (nftl->mbd.mtd->block_isbad(nftl->mbd.mtd, i * nftl->EraseSize)) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; } nftl->MediaUnit = block; @@ -257,22 +268,18 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, int check_oob) { - int i, retlen; - u8 buf[SECTORSIZE]; + int i; + size_t retlen; + u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; for (i = 0; i < len; i += SECTORSIZE) { - /* we want to read the sector without ECC check here since a free - sector does not have ECC syndrome on it yet */ - if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0) + if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize, - &retlen, buf) < 0) - return -1; - if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0) + if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0) return -1; } address += SECTORSIZE; @@ -287,7 +294,6 @@ * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? - * 2. UnitSizeFactor != 0xFF */ int NFTL_formatblock(struct NFTLrecord *nftl, int block) { @@ -297,7 +303,7 @@ struct erase_info *instr = &nftl->instr; /* Read the Unit Control Information #1 for Wear-Leveling */ - if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, + if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) goto default_uci1; @@ -312,16 +318,16 @@ memset(instr, 0, sizeof(struct erase_info)); /* XXX: use async erase interface, XXX: test return code */ + instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; - MTD_ERASE(nftl->mtd, instr); + MTD_ERASE(nftl->mbd.mtd, instr); if (instr->state == MTD_ERASE_FAILED) { - /* could not format, FixMe: We should update the BadUnitTable - both in memory and on disk */ printk("Error while formatting block %d\n", block); - return -1; - } else { + goto fail; + } + /* increase and write Wear-Leveling info */ nb_erases = le32_to_cpu(uci.WearInfo); nb_erases++; @@ -334,14 +340,18 @@ * FixMe: is this check really necessary ? since we have check the * return code after the erase operation. */ if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) - return -1; + goto fail; uci.WearInfo = le32_to_cpu(nb_erases); - if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) - return -1; + goto fail; return 0; - } +fail: + /* could not format, update the bad block table (caller is responsible + for setting the ReplUnitTable to BLOCK_RESERVED on failure) */ + nftl->mbd.mtd->block_markbad(nftl->mbd.mtd, instr->addr); + return -1; } /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. @@ -357,13 +367,14 @@ { unsigned int block, i, status; struct nftl_bci bci; - int sectors_per_block, retlen; + int sectors_per_block; + size_t retlen; sectors_per_block = nftl->EraseSize / SECTORSIZE; block = first_block; for (;;) { for (i = 0; i < sectors_per_block; i++) { - if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE, + if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci) < 0) status = SECTOR_IGNORE; else @@ -383,7 +394,7 @@ /* sector not free actually : mark it as SECTOR_IGNORE */ bci.Status = SECTOR_IGNORE; bci.Status1 = SECTOR_IGNORE; - MTD_WRITEOOB(nftl->mtd, + MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, 8, &retlen, (char *)&bci); } @@ -446,8 +457,7 @@ printk("Formatting block %d\n", block); if (NFTL_formatblock(nftl, block) < 0) { - /* cannot format !!!! Mark it as Bad Unit, - FixMe: update the BadUnitTable on disk */ + /* cannot format !!!! Mark it as Bad Unit */ nftl->ReplUnitTable[block] = BLOCK_RESERVED; } else { nftl->ReplUnitTable[block] = BLOCK_FREE; @@ -476,7 +486,7 @@ size_t retlen; /* check erase mark. */ - if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; @@ -491,7 +501,7 @@ h1.EraseMark = cpu_to_le16(ERASE_MARK); h1.EraseMark1 = cpu_to_le16(ERASE_MARK); h1.WearInfo = cpu_to_le32(0); - if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) return -1; } else { @@ -503,7 +513,7 @@ SECTORSIZE, 0) != 0) return -1; - if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i, + if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i, 16, &retlen, buf) < 0) return -1; if (i == SECTORSIZE) { @@ -533,7 +543,7 @@ struct nftl_uci2 uci; size_t retlen; - if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) return 0; @@ -572,9 +582,9 @@ for (;;) { /* read the block header. If error, we format the chain */ - if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, + if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, &retlen, (char *)&h0) < 0 || - MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, + MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { s->ReplUnitTable[block] = BLOCK_NIL; do_format_chain = 1; --- linux-2.4.21/drivers/mtd/redboot.c~mtd-cvs +++ linux-2.4.21/drivers/mtd/redboot.c @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $ + * $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include @@ -28,13 +30,18 @@ struct fis_list *next; }; +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + 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) +static int parse_redboot_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long fis_origin) { int nrparts = 0; struct fis_image_desc *buf; @@ -43,31 +50,49 @@ int ret, i; size_t retlen; char *names; + char *nullname; int namelen = 0; + int nulllen = 0; + int numslots; + unsigned long offset; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + static char nullstring[] = "unallocated"; +#endif - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + buf = vmalloc(master->erasesize); 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 ( directory < 0 ) + offset = master->size + directory*master->erasesize; + else + offset = directory*master->erasesize; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = master->read(master, offset, + master->erasesize, &retlen, (void *)buf); if (ret) goto out; - if (retlen != PAGE_SIZE) { + if (retlen != master->erasesize) { ret = -EIO; goto out; } - /* RedBoot image could appear in any of the first three slots */ - for (i = 0; i < 3; i++) { - if (!memcmp(buf[i].name, "RedBoot", 8)) + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (buf[i].name[0] == 0xff) { + i = numslots; break; } - if (i == 3) { + if (!memcmp(buf[i].name, "FIS directory", 14)) + break; + } + if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); @@ -75,7 +100,7 @@ goto out; } - for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) @@ -90,7 +115,11 @@ goto out; } new_fl->img = &buf[i]; + if (fis_origin) { + buf[i].flash_base -= fis_origin; + } else { 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_ @@ -103,42 +132,69 @@ nrparts++; } - if (fl->img->flash_base) +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->img->flash_base) { nrparts++; + nulllen = sizeof(nullstring); + } 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) + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { nrparts++; + nulllen = sizeof(nullstring); } - parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL); + } +#endif + parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); if (!parts) { ret = -ENOMEM; goto out; } - names = (char *)&parts[nrparts]; - memset(parts, 0, sizeof(*parts)*nrparts + namelen); + + memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen); + + nullname = (char *)&parts[nrparts]; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (nulllen > 0) { + strcpy(nullname, nullstring); + } +#endif + names = nullname + nulllen; + i=0; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (fl->img->flash_base) { - parts[0].name = "unallocated space"; + parts[0].name = nullname; parts[0].size = fl->img->flash_base; parts[0].offset = 0; + i++; } +#endif for ( ; iimg->size; parts[i].offset = fl->img->flash_base; parts[i].name = names; strcpy(names, fl->img->name); +#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY + if (!memcmp(names, "RedBoot", 8) || + !memcmp(names, "RedBoot config", 15) || + !memcmp(names, "FIS directory", 14)) { + parts[i].mask_flags = MTD_WRITEABLE; + } +#endif names += strlen(names)+1; - if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + 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"; + parts[i].name = nullname; } +#endif tmp_fl = fl; fl = fl->next; kfree(tmp_fl); @@ -151,11 +207,28 @@ fl = fl->next; kfree(old); } - kfree(buf); + vfree(buf); return ret; } -EXPORT_SYMBOL(parse_redboot_partitions); +static struct mtd_part_parser redboot_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_redboot_partitions, + .name = "RedBoot", +}; + +static int __init redboot_parser_init(void) +{ + return register_mtd_parser(&redboot_parser); +} + +static void __exit redboot_parser_exit(void) +{ + deregister_mtd_parser(&redboot_parser); +} + +module_init(redboot_parser_init); +module_exit(redboot_parser_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse "); --- /dev/null +++ linux-2.4.21/drivers/mtd/ssfdc.c @@ -0,0 +1,1132 @@ +/* + * drivers/mtd/ssfdc.c + * + * Copyright (C) 2003 Simon Haynes (simon@baydel.con) + * Baydel Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This module provides a translation layer, via mtd, for smart + * media card access. It essentially enables the possibility + * of using cards on a hardware which does not have a hardware translation + * layer and interchanging them with hardware that does ie: PC card readers + * + * I had to write this module for a specific task and in a short timeframe + * for this reason I have imposed some restricions to make the job easier. + * + * To build an compile the driver I added the following lines + * to mtd/Config.in + * + * dep_tristate ' SSFDC support' CONFIG_SSFDC $CONFIG_MTD + * + * to /mtd/Makefile + * + * obj-$(CONFIG_SSFDC) += ssfdc.o + * + * and compiled the kernel via the usual methods. + * + * I am sure that there are many problems I don't know about but here are + * some that I know of + * + * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL + * I did this because I wanted a static number and I didn't know + * how to go about getting a new one. This needs addressing + * The dev nodes required are like standard. I only use minor 0 + * (/dev/ssfdca), and minor 1 (/dev/ssfdca1). + * You should be able to run fdisk on /dev/ssfdca and the first partition + * is /dev/ssfdca1. There is no working code in the module for changing the + * SMC and rebuilding the maps so the card should not be changed once the + * module is loaded. At present I only look for 1 partition. But this is a + * small commented hack. + * + * There is no support cards which do not have a 512 byte page size with 16 + * bytes of oob and an erase size of 16K. + * There are no checks for this at present. In addition the MTD reported size + * must be 16M or a multiple. + * + * Code to handle multiple partitions or multiple cards is incomplete + * Need to allocate data buffer and oob buffer on a per partition basis. + * As I am only concerned with one partition I will do this if I ever need to. + * The cached physical address variable also needs this attention. + * + * Recently I have started to work on media changes. Some of this is specific + * to my hardware and you will see references to pt_ssfdc_smc and smc_status. + * This code is incomplete and does not work. I have commented it for the moment + * but it should give an indication of what I think is required. Maybe there is + * something it mtd that can help + * + * 17th August 2004 MHB + * + * Following updating CVS I noticed some single bit data corruption. I believe + * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc + * and that mtd->read was applying it's own error corretion from the wrong ecc bytes + * I have now corrected this. + * + * During this time I noticed that while in allocate new I only seem to look for blocks + * in 1 zone. So this limits the partition size to 16MB with all the other SMC size + * restrictions + + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if (LINUX_VERSION_CODE >= 0x20100) +#include +#endif +#if (LINUX_VERSION_CODE >= 0x20303) +#include +#endif + +#include + +#define SSFDC_FORMAT 1 + +#define PDEBUG(fmt, args...) + +#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT + +#if (LINUX_VERSION_CODE < 0x20320) +#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +#define blk_init_queue(q, req) q = (req) +#define blk_cleanup_queue(q) q = NULL +#define request_arg_t void +#else +#define request_arg_t request_queue_t *q +#endif + +#define TRUE 1 +#define FALSE 0 + +#define SSFDC_MAJOR 44 + +#define MAJOR_NR SSFDC_MAJOR +#define DEVICE_NAME "ssfdc" +#define DEVICE_REQUEST do_ssfdc_request +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h" + + + +#define ZONE_SIZE (16 * 1024 * 1024) +#define SMC_BLOCK_SIZE (16 * 1024) +#define SECTOR_SIZE 512 +#define SECTORS_PER_ZONE (ZONE_SIZE / SECTOR_SIZE) +#define BLOCKS_PER_ZONE (ZONE_SIZE / SMC_BLOCK_SIZE) +#define SECTORS_PER_BLOCK (SMC_BLOCK_SIZE / SECTOR_SIZE) +#define OOB_SIZE 16 + + +#define MAX_DEVICES 4 +#define MAX_PARTITIONS 8 +#define PARTITION_BITS 3 +#define MAX_ZONES 8 + + +int ssfdc_major = SSFDC_MAJOR; +unsigned int ssfdc_cached = 0xFFFFFFFF; +static unsigned char ssfdc_scratch[16384]; +static unsigned char ssfdc_buffer[16]; +static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; +static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; + + +static struct nand_oobinfo ssfdc_ffoob_info = { + .useecc = 0, +}; + + +typedef struct minor_t { + atomic_t open; + int cached; + unsigned char * pt_data; + unsigned char * pt_oob; +} minor_t; + + + +typedef struct partition_t { + int type; + struct mtd_info *mtd; + int count; + unsigned int *zone; + unsigned int zoneCount; + minor_t minor[MAX_PARTITIONS]; + unsigned int last_written[MAX_ZONES]; +} partition_t; + +partition_t SMCParts[MAX_DEVICES]; + + +static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10}; + +static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS]; +smc_control * pt_ssfdc_smc; + + +static struct gendisk ssfdc_gendisk = { + major: SSFDC_MAJOR, + major_name: "ssfdc", + minor_shift: PARTITION_BITS, + max_p: MAX_PARTITIONS, + part: ssfdc_hd, + sizes: ssfdc_sizes, +}; + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); +static int ssfdc_open(struct inode *inode, struct file *file); +static int ssfdc_close(struct inode *inode, struct file *file); +static int ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block); +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset); +static int ssfdc_read_partitions(partition_t * pt_smcpart); +static void ssfdc_notify_add(struct mtd_info *mtd); +static void ssfdc_notify_remove(struct mtd_info *mtd); +static void ssfdc_tables(partition_t * pt_smcpart); +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc); +static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone); +int ssfdc_parity(int number); +static void ssfdc_erase_callback(struct erase_info *erase); + + + +static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq); + + +static struct mtd_notifier ssfdc_notifier = { + add: ssfdc_notify_add, + remove: ssfdc_notify_remove, +}; + + + +static struct block_device_operations ssfdc_fops = { + open: ssfdc_open, + release: ssfdc_close, + ioctl: ssfdc_ioctl, +}; + +static struct semaphore ssfdc_semaphore; + +static void ssfdc_notify_add(struct mtd_info *mtd) { + + + + + if(mtd->index >= 1) return; // Hack to limit SSFDC to 1 partition + + if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){ + PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index); + } + else { + memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t)); + SMCParts[mtd->index].mtd = mtd; + SMCParts[mtd->index].count = mtd->index; + SMCParts[mtd->index].type = 1; + SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE; + SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL); + + + if(!SMCParts[mtd->index].zone) { + printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index); + SMCParts[mtd->index].type = 0; + } + else { + memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192); + } + + ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type); + } + return; + +} +static int ssfdc_read_partitions(partition_t * pt_smcpart) { + + int whole, i, j, size; + +//=printk("ssfdc_read_partitions : start\n"); + + for(i=0; iminor[i].open) > 1)) { +//=printk("ssfdc_read_partitions : part %d busy\n", i); + + return -EBUSY; + } + + +//=printk("ssfdc_read_partitions : tables start\n"); + ssfdc_tables(pt_smcpart); +//=printk("ssfdc_read_partitions : tables end\n"); + + whole = pt_smcpart->count << PARTITION_BITS; + + + j = MAX_PARTITIONS - 1; + while (j-- > 0) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j); + invalidate_device(rdev, 1); + } + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects = 0; + } + + + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + size = size * (0x8 * 0x20); + +//=printk("ssfdc_read_partitions : register start\n"); + + register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS, + &ssfdc_fops, size); + +//=printk("ssfdc_read_partitions : register end\n"); + + + return 0; +} + + +static void ssfdc_notify_remove(struct mtd_info *mtd) { +int i, j, whole; + + i=mtd->index; + whole = i << PARTITION_BITS; + if(SMCParts[i].mtd == mtd) { + if(SMCParts[i].zone)kfree(SMCParts[i].zone); + memset((void *)&SMCParts[i].type, 0, sizeof(partition_t)); + for (j = 0; j < MAX_PARTITIONS; j++) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects=0; + } + } + return; + } + return; +} + + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) { + + int minor = MINOR(inode->i_rdev); + int ret = -EINVAL; + partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type; + struct hd_geometry geo; + int size; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + ret = 1; + goto ssfdc_ioctl_error; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + switch(cmd) { + + case HDIO_GETGEO: + memset(&geo, 0, sizeof(geo)); + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + geo.heads = 0x8; + geo.sectors = 0x20; + geo.cylinders = size; + geo.start = ssfdc_hd[minor].start_sect; +// printk(KERN_WARNING "ssfdc : HDIO_GETGEO heads %d, sectors %d, cylinders %d, start %lu\n", +// geo.heads, geo.sectors, geo.cylinders, geo.start); + copy_to_user((void *)arg, &geo, sizeof(geo)); + ret = 0; + break; + + case BLKGETSIZE64: + case BLKGETSIZE: + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + //=printk(KERN_WARNING "ssfdc : BLKGETSIZE %d, minor %d\n", size, minor); + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + case BLKSSZGET: + size = 512; + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + break; + + case BLKRRPART: + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + ret=0; + break; + case BLKFLSBUF: + printk(KERN_WARNING "ssfdc : block ioctl 0x%x\n", cmd); + break; + + default: + printk(KERN_WARNING "ssfdc: unknown ioctl 0x%x\n", cmd); + } + +//ssfdc_ioctl_error: + return(ret); + +} +static int ssfdc_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + + + if(!pt_smcpart->zone) + return -ENXIO; + + + BLK_INC_USE_COUNT; + + if (!get_mtd_device(pt_smcpart->mtd, -1)) { + BLK_DEC_USE_COUNT; + return -ENXIO; + } + + if ((file->f_mode & 2) && !(pt_smcpart->mtd->flags & MTD_CLEAR_BITS) ) { + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + return -EROFS; + } + + + atomic_inc(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + + PDEBUG("ssfdc_open : device %d\n", minor); + + return(0); +} + +static void ssfdc_tables(partition_t * pt_smcpart) { + + int * logical, * physical; + int offset = 0; + int zone, block; + int i, retlen; + int block_address, parity; + int h, l; + + for(zone=0; zonezoneCount; zone++) { + logical = pt_smcpart->zone + (2048 * zone); + memset((void *)logical, 0xFF, 1024 * sizeof(int)); + physical = pt_smcpart->zone + (2048 * zone) + 1024; + memset((void *)physical, 0xFF, 1024 * sizeof(int)); + + for(block=0; block < 1024; block++) { + offset = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset, sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_tables : failed to read OOB\n"); + pt_smcpart->type = 0; + return; + } + + l = (ssfdc_buffer[7] & 0xFF); + h = (ssfdc_buffer[6] & 0xFF); + block_address = l + (h << 8L); + + if((block_address & ~0x7FF) != 0x1000) { + continue; + } + + parity = block_address & 0x01; + + block_address &= 0x7FF; + block_address >>= 1; + + + if(ssfdc_parity(block_address) != parity) { + printk(KERN_WARNING "ssfdc_tables : parity error offset 0x%x, block 0x%x, parity 0x%x\nOOB : " + , offset, block_address, parity); + for(i=0; i<16; i++) { + printk("0x%02x ", (unsigned char)ssfdc_buffer[i]); + } + printk("\n"); + pt_smcpart->type = 0; + return; + } + + + /* Ok we have a valid block number so insert it */ + *(logical + block_address) = (offset/SMC_BLOCK_SIZE); + PDEBUG("ssfdc_tables : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)logical, block_address, (offset/SMC_BLOCK_SIZE)); + *(physical + block) = block_address; + PDEBUG("ssfdc_tables : physical 0x%x + 0x%x = 0x%x\n", (unsigned int)physical, block, block_address); + + + } + } + return; +} +int ssfdc_parity(int number) { + int i; + int parity = 1; // the 0x1000 bit + + for(i=0; i<10; i++) { + parity += ((number >> i) & 1); + } + PDEBUG("ssfdc_parity : number 0x%x, parity 0x%x\n", number, parity); + return(parity % 2); +} +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block) { + + unsigned int * logical; + + logical = pt_smcpart->zone + (zone * 2048); + + logical += block; + + if(*logical == 0xFFFFFFFF) { + PDEBUG("ssfdc_physical : physical for zone %d, block %d invalid\n", zone, block); + return(-1); + } + + PDEBUG("ssfdc_physical : physical for zone %d, block %d, 0x%x\n", zone, block, (*logical * SMC_BLOCK_SIZE)); + return(*logical * SMC_BLOCK_SIZE); +} + +static int ssfdc_close(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + atomic_dec(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + + return(0); +} + + +static void do_ssfdc_request(request_arg_t) +{ + int ret, minor; + partition_t *pt_smcpart; + int index; + do { + + INIT_REQUEST; + + + + minor = MINOR(CURRENT->rq_dev); + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + pt_smcpart = &SMCParts[index]; + if (pt_smcpart->type == SSFDC_FORMAT) { + ret = 0; + switch (CURRENT->cmd) { + case READ: + ret = ssfdc_read(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + case WRITE: + ret = ssfdc_write(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + default: + panic("do_ssfdc_request : unknown block command!\n"); + } + + } else { + ret = 1; + PDEBUG("not ssfdc partition type\n"); + } + + if (!ret) { + CURRENT->sector += CURRENT->current_nr_sectors; + } + + end_request((ret == 0) ? 1 : 0); + } while (1); +} + +static int ssfdc_write(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_written = 0; + int physical; + int * pt_logical; + int * pt_physical; + int new = -1; + int size; + int retlen; + int i; + int sc; + int ptr_done = 0; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; + int do_erase; +// unsigned char smc_status; + + + + offset = (sector % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + PDEBUG("write device %d, sector %d, count %d\n", + pt_smcpart->count, sector, nblocks); +/* + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_written < nblocks) { + + new = -1; + do_erase = FALSE; + + zone = (sector + sectors_written) / SECTORS_PER_ZONE; + block = ((sector + sectors_written) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_written) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + pt_logical = pt_smcpart->zone + (zone * 2048); + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024; + + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_written)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_written); + size *= SECTOR_SIZE; + + PDEBUG("write device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_written, size, (unsigned int)ptr); + + physical = ssfdc_physical(pt_smcpart, zone, block); + + + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to read physical\n"); + return -ENXIO; + } + + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + } + + for(sc=0; sc sc) { + PDEBUG("offset %d, sector %d\n", offset, sc); + continue; + } + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + + /* find out if the block is being used */ + + + if(ssfdc_sector_blank(pt_smcpart, sc)) { + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, blank, physical 0x%x\n", + zone, block, sc, sector, physical); + memcpy(&ssfdc_scratch[(sc * SECTOR_SIZE)], ptr+ptr_done, SECTOR_SIZE); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done), &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done + 256), &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), SECTOR_SIZE, &retlen, + ptr + ptr_done, ssfdc_buffer, &ssfdc_ffoob_info); + if(retlen != SECTOR_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write physical 0x%x, sector 0x%x, blank, retlen %d\n" + , physical, sc, retlen); + return -ENXIO; + } + + ptr_done += SECTOR_SIZE; + if(ptr_done >= size) break; + } + else { + new = ssfdc_allocate_new(pt_smcpart, zone); + /* erase the old block */ + *(pt_physical + ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE)) = 0xFFFFFFFF; + + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE), 0xFFFFFFFF); + do_erase = TRUE; + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, written, physical 0x%x, new 0x%x\n", + zone, block, sc, sector, physical, new); + break; + } + } + } + else { + ssfdc_cached = 0xFFFFFFFF; + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + new = ssfdc_allocate_new(pt_smcpart, zone); + PDEBUG("ssfdc_write : zone %d, block %d, lbn %d, physical 0x%x, unallocated, new 0x%x\n", + zone, block, sector, physical, new); + } + + + + if(new != -1) { + + + memcpy(&ssfdc_scratch[(offset * SECTOR_SIZE)], ptr, size); + PDEBUG("ssfdc_write : new 0x%x, offset 0x%x, size 0x%x, block 0x%x\n", new, offset, size, block); + for(sc=0; scmtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + memcpy(&ssfdc_oob_buf[sc * OOB_SIZE], ssfdc_buffer, OOB_SIZE); + } + + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, new, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write block, physical 0x%x, returned 0x%x\n", new, retlen); + return -ENXIO; + } + /* change the mapping table to reflect the new block placement */ + + *(pt_logical + block) = (new % ZONE_SIZE) / SMC_BLOCK_SIZE; + PDEBUG("ssfdc_write : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_logical, block, (new % ZONE_SIZE) / SMC_BLOCK_SIZE); + + *(pt_physical + ((new % ZONE_SIZE) / SMC_BLOCK_SIZE)) = block; + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((new % ZONE_SIZE) / SMC_BLOCK_SIZE), block); + + + ssfdc_cached = new; + } + + + ptr += size; + ptr_done = 0; + sectors_written += (size / SECTOR_SIZE); + if(do_erase) ssfdc_erase(pt_smcpart, physical); + + } + + + + + return(0); +} +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc) { +int b; + + for(b=0; blast_written[zone] + 1; + int * pt_physical; + int physical; + int block; + int retlen; + unsigned char oob[16]; + + + if(new >= BLOCKS_PER_ZONE) new = 0; + + + while (new != pt_smcpart->last_written[zone]) { + block = new % BLOCKS_PER_ZONE; + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024 + block; + physical = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + + PDEBUG("ssfdc_allocate_new : zone %d, block %d, address 0x%08x, data 0x%08x\n", + zone, block, (unsigned int)pt_physical, *pt_physical); + if(*pt_physical == 0xFFFFFFFF) { + PDEBUG("ssfdc_allocate_new : physical 0x%x = 0x%x\n", (unsigned int)pt_physical, *pt_physical); + memset(oob, 0, OOB_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical, OOB_SIZE, &retlen, oob); + if((oob[5] == 0xFF) && (retlen == OOB_SIZE)) { // If not a bad block + pt_smcpart->last_written[zone] = new; + return((new * SMC_BLOCK_SIZE) + (zone * ZONE_SIZE)); + } + else { + PDEBUG("ssfdc_allocate_new : new 0x%x, physical 0x%x, block status 0x%x, oob length 0x%x\n", new, physical, oob[5], retlen); + } + } + new++; + if(new >= BLOCKS_PER_ZONE) new = 0; + } + + panic("ssfdc_allocate_new : cant find free block\n"); + +} + + + +static int ssfdc_read(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_read = 0; + int physical; + int size; + int retlen; + int i; + int sc; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_read < nblocks) { + + zone = (sector + sectors_read) / SECTORS_PER_ZONE; + block = ((sector + sectors_read) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_read) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + + if(offset) { + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_read)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_read); + } + else { + size = (SECTORS_PER_BLOCK < (nblocks - sectors_read)) ? SECTORS_PER_BLOCK : nblocks - sectors_read; + } + size *= SECTOR_SIZE; + + PDEBUG("ssfdc_read : device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_read, size, (unsigned int)ptr); + + + physical = ssfdc_physical(pt_smcpart, zone, block); + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_read : failed to read physical\n"); + return -ENXIO; + } + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_read : failed to read physical oob\n"); + return -ENXIO; + } + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<3; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + for(i=3; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + /* Get the ecc bytes and check that they are ok */ + + + } + ssfdc_cached = physical; + + + } + else { + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + ssfdc_cached = 0xFFFFFFFF; + } + + + memcpy(ptr, &ssfdc_scratch[(offset * SECTOR_SIZE)], size); + ptr += size; + sectors_read += (size / SECTOR_SIZE); + } + + + + return(0); +} + +static void ssfdc_erase_callback(struct erase_info *erase) { + + PDEBUG("ssfdc_erase_callback : wake erase\n"); + up(&ssfdc_semaphore); + PDEBUG("ssfdc_erase_callback : woken erase\n"); +} + +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset) +{ + int ret = 0; + struct erase_info *erase; + unsigned char * junk; + unsigned char * oob; + int retlen; + int b, sc; + + + PDEBUG("ssfdc_erase : offset 0x%08x\n", offset); + + erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); + junk=kmalloc(pt_smcpart->mtd->erasesize + 16, GFP_KERNEL); + oob = junk + pt_smcpart->mtd->erasesize; + + if (!erase) + return -ENOMEM; + if (!junk) + return -ENOMEM; + + erase->addr = offset; + erase->len = pt_smcpart->mtd->erasesize; + erase->callback = ssfdc_erase_callback; + ret = pt_smcpart->mtd->erase(pt_smcpart->mtd, erase); + if(ret) { + printk(KERN_WARNING "ssfdc_erase : failed status 0x%x\n", ret); + goto end; + + } + + down(&ssfdc_semaphore); + + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, offset, SMC_BLOCK_SIZE, &retlen, junk, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read returned length %d\n", offset, retlen); + goto end; + } + + + for(sc=0; sc < SECTORS_PER_BLOCK; sc++) { + for(b=0; bmtd->read_oob(pt_smcpart->mtd, offset + (sc * SECTOR_SIZE), OOB_SIZE, &retlen, oob); + if(retlen != OOB_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read oob returned length %d\n", offset, retlen); + goto end; + } + for(b=0; bsmc_status); +*/ + memset(ssfdc_ffoob_buf, 0xFF, sizeof(ssfdc_ffoob_buf)); + + for (i = 0; i < MAX_DEVICES*MAX_PARTITIONS; i++) { + ssfdc_hd[i].nr_sects = 0; + ssfdc_hd[i].start_sect = 0; + ssfdc_blocksizes[i] = 4096; + } + blksize_size[SSFDC_MAJOR] = ssfdc_blocksizes; + ssfdc_gendisk.major = SSFDC_MAJOR; + + + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + + result = register_blkdev(ssfdc_major, "ssfdc", &ssfdc_fops); + if(result != 0) { + printk(KERN_WARNING "ssfdc : failed to get a major number\n"); + return(result); + } +// if(ssfdc_major == 0) ssfdc_major = result; + + blk_init_queue(BLK_DEFAULT_QUEUE(ssfdc_major), &do_ssfdc_request); + + add_gendisk(&ssfdc_gendisk); + + + + register_mtd_user(&ssfdc_notifier); + + + init_MUTEX_LOCKED(&ssfdc_semaphore); + + + + return 0; +} + +static void __exit cleanup_ssfdc(void) +{ + int i; + + for(i=0; i"); +MODULE_DESCRIPTION("SSFDC translation layer support for MTD"); + + + + --- linux-2.4.21/drivers/net/irda/pxa_ir.c~pxa-irda +++ linux-2.4.21/drivers/net/irda/pxa_ir.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -786,6 +787,7 @@ * Suspend the IrDA interface. */ +/* static int pxa250_irda_shutdown(struct pxa250_irda *si) { @@ -793,6 +795,7 @@ return 0; } +*/ static int pxa250_irda_suspend(struct net_device *dev, int state) @@ -1141,11 +1144,11 @@ /* allocate consistent buffers for dma access * buffers have to be aligned and situated in dma capable memory region; */ - si->rxbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA ,HPSIR_MAX_RXLEN , &si->rxbuf_dma); + si->rxbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA ,HPSIR_MAX_RXLEN , &si->rxbuf_dma, 0); if (! si->rxbuf_dma_virt ) goto err_rxbuf_dma; - si->txbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA, HPSIR_MAX_TXLEN, &si->txbuf_dma); + si->txbuf_dma_virt = consistent_alloc(GFP_KERNEL | GFP_DMA, HPSIR_MAX_TXLEN, &si->txbuf_dma, 0); if (! si->txbuf_dma_virt ) goto err_txbuf_dma; --- linux-2.4.21/drivers/net/smc91x.c~pxa-smc91x +++ linux-2.4.21/drivers/net/smc91x.c @@ -46,10 +46,13 @@ . 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support . 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races, . more bus abstraction, big cleanup, etc. + . 20/08/03 Holger Schurig add ethtool support ----------------------------------------------------------------------------*/ +#define DRV_NAME "smc91x" + static const char version[] = - "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre \n"; + DRV_NAME ": v1.1 Aug 20 2003 by Nicolas Pitre \n"; /* Debugging level */ #ifndef SMC_DEBUG @@ -67,6 +70,7 @@ #include #include #include +#include #include #include @@ -78,6 +82,7 @@ #include #include #include +#include #include "smc91x.h" @@ -105,7 +110,7 @@ static int irq = SMC_IRQ; #ifndef SMC_NOWAIT -# define SMC_NOWAIT 0 +# define SMC_NOWAIT 1 #endif static int nowait = SMC_NOWAIT; @@ -116,6 +121,11 @@ MODULE_PARM_DESC(irq, "IRQ number"); MODULE_PARM_DESC(nowait, "set to 1 for no wait state"); +static int +smc_read_phy_register(unsigned long ioaddr, int phyaddr, int phyreg); +static void +smc_write_phy_register( unsigned long ioaddr, int phyaddr, + int phyreg, int phydata ); /*------------------------------------------------------------------------ . @@ -143,7 +153,12 @@ . but to the expense of reduced TX throughput and increased IRQ overhead. . Note this is not a cure for a too slow data bus or too high IRQ latency. */ -#define THROTTLE_TX_PKTS 0 +#define THROTTLE_TX_PKTS 1 + +/* + . This defines if we want to compile ethtool support into the driver +*/ +#define WITH_ETHTOOL 1 /* store this information for the driver.. */ @@ -310,14 +325,14 @@ if (nowait) SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT ); -#ifdef POWER_DOWN +#if POWER_DOWN /* Release from possible power-down state */ /* Configuration register is not affected by Soft Reset */ SMC_SELECT_BANK( 1 ); SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_EPH_POWER_EN ); status = smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); status &= ~PHY_CNTL_PDN; - smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, status); #endif /* this should pause enough for the chip to be happy */ @@ -390,10 +405,10 @@ SMC_SET_RCR( RCR_CLEAR ); SMC_SET_TCR( TCR_CLEAR ); -#ifdef POWER_DOWN +#if POWER_DOWN status = smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); status |= PHY_CNTL_PDN; - smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG); + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, status); /* finally, shut the chip down */ SMC_SELECT_BANK( 1 ); @@ -1628,14 +1643,18 @@ // Setup the default Register Modes lp->tcr_cur_mode = TCR_DEFAULT; lp->rcr_cur_mode = RCR_DEFAULT; - lp->rpc_cur_mode = RPC_DEFAULT; /* Set default parameters */ #ifdef CONFIG_ARCH_RAMSES - lp->ctl_autoneg = 0; - lp->ctl_rfduplx = 0; + lp->rpc_cur_mode = (RPC_ANEG | (RPC_LED_10 << RPC_LSXA_SHFT) | (RPC_LED_TX_RX << RPC_LSXB_SHFT) | RPC_DPLX); + + // 10 MBit/S, auto-negotiation only for 10 MB/s + lp->ctl_autoneg = 1; + lp->ctl_rfduplx = 1; lp->ctl_rspeed = 10; #else + lp->rpc_cur_mode = RPC_DEFAULT; + lp->ctl_autoneg = 1; lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; @@ -1680,6 +1699,127 @@ return 0; } +/*---------------------------------------------------- + . smc_ioctl + . + . This ioctl is currently only used by ethtool(8) to + . access the serial EEPROM + -----------------------------------------------------*/ + +#if WITH_ETHTOOL + +#define SMC91x_EEPROM_SIZE (0x40*2) + +u16 smc_eeprom_read(long ioaddr, u16 location) +{ + u16 val; + u16 oldBank; + u16 oldPtr; + + cli(); + // Save chip settings + oldBank = SMC_CURRENT_BANK(); + SMC_SELECT_BANK( 2 ); + oldPtr = SMC_GET_PTR(); + + // Set location in EEPROM to be read + SMC_SET_PTR(location); + + // Set EEPROM_SELECT and RELOAD bits in control register + SMC_SELECT_BANK( 1 ); + val = SMC_GET_CTL(); + SMC_SET_CTL(val | CTL_EEPROM_SELECT | CTL_RELOAD); + + // Wait until RELEAD is finished + while (SMC_GET_CTL() & CTL_RELOAD) ; + + // Get EEPROM data + val = SMC_inw(ioaddr, GP_REG); + + // Restore chip settings + SMC_SELECT_BANK( 2 ); + SMC_SET_PTR(oldPtr); + SMC_SELECT_BANK( oldBank ); + sti(); + + return val; +} + +static int smc_get_eeprom(struct net_device *dev, u8 *buf) +{ + int i; + u16 *ebuf = (u16 *)buf; + + for (i = 0; i < SMC91x_EEPROM_SIZE/2; i++) { + ebuf[i] = smc_eeprom_read(dev->base_addr, i); + } + return 0; +} + +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + u32 etcmd; + int ret = -EINVAL; + + if (cmd != SIOCETHTOOL) + return -EOPNOTSUPP; + + if (get_user(etcmd, (u32 *)rq->ifr_data)) + return -EFAULT; + + switch (etcmd) { + + /* Get driver info */ + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo edrv; + + memset(&edrv, 0, sizeof(edrv)); + edrv.cmd = etcmd; + strcpy(edrv.driver, DRV_NAME); + sprintf(edrv.bus_info, "ISA:%8.8lx:%d", dev->base_addr, dev->irq); + edrv.eedump_len = SMC91x_EEPROM_SIZE; + ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv)) ? -EFAULT : 0; + break; + } + + /* Get EEPROM data */ + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom; + u8 eebuf[SMC91x_EEPROM_SIZE]; + int r; + + if (copy_from_user(&eeprom, rq->ifr_data, sizeof(eeprom))) + return -EFAULT; + + if (eeprom.offset > eeprom.offset+eeprom.len) + return -EINVAL; + + if ((eeprom.offset+eeprom.len) > SMC91x_EEPROM_SIZE) { + eeprom.len = SMC91x_EEPROM_SIZE-eeprom.offset; + } + eeprom.magic = 0; + if (copy_to_user(rq->ifr_data, &eeprom, sizeof(eeprom))) + return -EFAULT; + + rq->ifr_data += offsetof(struct ethtool_eeprom, data); + + r = smc_get_eeprom(dev, eebuf); + + if (r) + return r; + if (copy_to_user(rq->ifr_data, eebuf+eeprom.offset, eeprom.len)) + return -EFAULT; + return 0; + + } + } + + return ret; +} + +#endif + + /*------------------------------------------------------------ . Get the current statistics. . This may be called with the card open or closed. @@ -1925,6 +2065,9 @@ dev->watchdog_timeo = HZ/10; dev->get_stats = smc_query_statistics; dev->set_multicast_list = smc_set_multicast_list; +#if WITH_ETHTOOL + dev->do_ioctl = smc_ioctl; +#endif return 0; @@ -1961,12 +2104,17 @@ smc_shutdown(global_dev); break; case PM_RESUME: + udelay(5000); smc_reset(global_dev); smc_enable(global_dev); SMC_SELECT_BANK( 1 ); SMC_SET_MAC_ADDR(global_dev->dev_addr); - if (lp->version >= 0x70) - smc_phy_configure(global_dev); + if (global_dev->flags & IFF_UP) { + if (lp->version >= 0x70) + smc_phy_configure(global_dev); + } else { + smc_shutdown(global_dev); + } break; } return 0; @@ -2054,6 +2202,15 @@ int ioaddr = RAMSES_ETH_BASE + 0x300; global_dev->irq = SMC_IRQ; ret = smc_probe(global_dev, ioaddr); +#ifdef POWER_DOWN + smc_shutdown(global_dev); +#endif + } +#elif defined(CONFIG_ARCH_RAMSES) + { + int ioaddr = RAMSES_ETH_BASE + 0x300; + global_dev->irq = SMC_IRQ; + ret = smc_probe(global_dev, ioaddr); } #else if (global_dev->base_addr == -1) { @@ -2083,7 +2240,11 @@ #ifdef CONFIG_PM if (ret == 0) { struct smc_local *lp = (struct smc_local *)global_dev->priv; +#ifdef PM_DEBUG + lp->pm = pm_register(PM_SYS_UNKNOWN, 0x73393178, smc_pm_callback, "smc91x"); +#else lp->pm = pm_register(PM_SYS_UNKNOWN, 0x73393178, smc_pm_callback); +#endif } #endif --- linux-2.4.21/drivers/net/smc91x.h~ramses-smc91x +++ linux-2.4.21/drivers/net/smc91x.h @@ -79,6 +79,11 @@ #include #define SMC_IOADDR (RAMSES_ETH_PHYS + 0x300) #define SMC_IRQ ETHERNET_IRQ + +#elif CONFIG_ARCH_RAMSES +#include +#define SMC_IOADDR (RAMSES_ETH_PHYS + 0x300) +#define SMC_IRQ ETHERNET_IRQ #endif #define SMC_CAN_USE_8BIT 1 --- linux-2.4.21/drivers/net/wireless/hermes.c~orinoco013e +++ linux-2.4.21/drivers/net/wireless/hermes.c @@ -52,7 +52,6 @@ #include "hermes.h" -static char version[] __initdata = "hermes.c: 4 Dec 2002 David Gibson "; MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); MODULE_AUTHOR("David Gibson "); #ifdef MODULE_LICENSE @@ -226,7 +225,8 @@ * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware * * Callable from any context, but locking is your problem. */ -int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) +int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, + hermes_response_t *resp) { int err; int k; @@ -469,13 +469,17 @@ err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); if (err) - goto out; + return err; err = hermes_bap_seek(hw, bap, rid, 0); if (err) - goto out; + return err; rlength = hermes_read_reg(hw, dreg); + + if (! rlength) + return -ENOENT; + rtype = hermes_read_reg(hw, dreg); if (length) @@ -495,8 +499,7 @@ nwords = min((unsigned)rlength - 1, bufsize / 2); hermes_read_words(hw, dreg, buf, nwords); - out: - return err; + return 0; } int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, @@ -511,7 +514,7 @@ err = hermes_bap_seek(hw, bap, rid, 0); if (err) - goto out; + return err; hermes_write_reg(hw, dreg, length); hermes_write_reg(hw, dreg, rid); @@ -523,7 +526,6 @@ err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, rid, NULL); - out: return err; } @@ -539,9 +541,12 @@ static int __init init_hermes(void) { - printk(KERN_DEBUG "%s\n", version); - return 0; } +static void __exit exit_hermes(void) +{ +} + module_init(init_hermes); +module_exit(exit_hermes); --- linux-2.4.21/drivers/net/wireless/hermes.h~orinoco013e +++ linux-2.4.21/drivers/net/wireless/hermes.h @@ -250,7 +250,6 @@ u16 scanreason; /* ??? */ struct hermes_scan_apinfo aps[35]; /* Scan result */ } __attribute__ ((packed)); - #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) #define HERMES_LINKSTATUS_CONNECTED (0x0001) #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) @@ -278,7 +277,7 @@ /* Basic control structure */ typedef struct hermes { - ulong iobase; + unsigned long iobase; int io_space; /* 1 if we IO-mapped IO, 0 for memory-mapped IO? */ #define HERMES_IO 1 #define HERMES_MEM 0 @@ -368,7 +367,7 @@ if (hw->io_space) { insw(hw->iobase + off, buf, count); } else { - int i; + unsigned i; u16 *p; /* This needs to *not* byteswap (like insw()) but @@ -388,7 +387,7 @@ if (hw->io_space) { outsw(hw->iobase + off, buf, count); } else { - int i; + unsigned i; const u16 *p; /* This needs to *not* byteswap (like outsw()) but @@ -401,6 +400,21 @@ } } +static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count) +{ + unsigned i; + + off = off << hw->reg_spacing;; + + if (hw->io_space) { + for (i = 0; i < count; i++) + outw(0, hw->iobase + off); + } else { + for (i = 0; i < count; i++) + writew(0, hw->iobase + off); + } +} + #define HERMES_READ_RECORD(hw, bap, rid, buf) \ (hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf))) #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ --- linux-2.4.21/drivers/net/wireless/ieee802_11.h~orinoco013e +++ linux-2.4.21/drivers/net/wireless/ieee802_11.h @@ -9,6 +9,8 @@ bytes is allowed, which is a bit confusing, I suspect this represents the 2304 bytes of real data, plus a possible 8 bytes of WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + + #define IEEE802_11_HLEN 30 #define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN) --- linux-2.4.21/drivers/net/wireless/orinoco.c~orinoco013e +++ linux-2.4.21/drivers/net/wireless/orinoco.c @@ -1,4 +1,4 @@ -/* orinoco.c 0.13b - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* orinoco.c 0.13e - (formerly known as dldwd_cs.c and orinoco_cs.c) * * A driver for Hermes or Prism 2 chipset based PCMCIA wireless * adaptors, with Lucent/Agere, Intersil or Symbol firmware. @@ -117,7 +117,7 @@ * o Init of priv->tx_rate_ctrl in firmware specific section. * o Prism2/Symbol rate, upto should be 0xF and not 0x15. Doh ! * o Spectrum card always need cor_reset (for every reset) - * o Fix cor_reset to not loose bit 7 in the register + * o Fix cor_reset to not lose bit 7 in the register * o flush_stale_links to remove zombie Pcmcia instances * o Ack previous hermes event before reset * Me (with my little hands) @@ -289,7 +289,7 @@ * which are used as the dev->open, dev->stop, priv->reset * callbacks if none are specified when alloc_orinocodev() is * called. - * o Removed orinoco_plx_interupt() and orinoco_pci_interrupt(). + * o Removed orinoco_plx_interrupt() and orinoco_pci_interrupt(). * They didn't do anything. * * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson @@ -345,13 +345,54 @@ * we are connected (avoids cofusing the firmware), and only * give LINKSTATUS printk()s if the status has changed. * + * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson + * o Cleanup: use dev instead of priv in various places. + * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event + * if we're in the middle of a (driver initiated) hard reset. + * o Bug fix: ETH_ZLEN is supposed to include the header + * (Dionysus Blazakis & Manish Karir) + * o Convert to using workqueues instead of taskqueues (and + * backwards compatibility macros for pre 2.5.41 kernels). + * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in + * airport.c + * o New orinoco_tmd.c init module from Joerg Dorchain for + * TMD7160 based PCI to PCMCIA bridges (similar to + * orinoco_plx.c). + * + * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson + * o Make hw_unavailable a counter, rather than just a flag, this + * is necessary to avoid some races (such as a card being + * removed in the middle of orinoco_reset(). + * o Restore Release/RequestConfiguration in the PCMCIA event handler + * when dealing with a driver initiated hard reset. This is + * necessary to prevent hangs due to a spurious interrupt while + * the reset is in progress. + * o Clear the 802.11 header when transmitting, even though we + * don't use it. This fixes a long standing bug on some + * firmwares, which seem to get confused if that isn't done. + * o Be less eager to de-encapsulate SNAP frames, only do so if + * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old + * behaviour broke CDP (Cisco Discovery Protocol). + * o Use dev instead of priv for free_irq() as well as + * request_irq() (oops). + * o Attempt to reset rather than giving up if we get too many + * IRQs. + * o Changed semantics of __orinoco_down() so it can be called + * safely with hw_unavailable set. It also now clears the + * linkstatus (since we're going to have to reassociate). + * + * v0.13d -> v0.13e - 12 May 2003 - David Gibson + * o Support for post-2.5.68 return values from irq handler. + * o Fixed bug where underlength packets would be double counted + * in the rx_dropped statistics. + * o Provided a module parameter to suppress linkstatus messages. + * * TODO - * o New wireless extensions API (patch from Moustafa - * Youssef, updated by Jim Carter). - * o Fix PCMCIA hard resets with pcmcia-cs. + * Youssef, updated by Jim Carter and Pavel Roskin). * o Handle de-encapsulation within network layer, provide 802.11 * headers (patch from Thomas 'Dent' Mirlacher) + * o RF monitor mode support * o Fix possible races in SPY handling. * o Disconnect wireless extensions from fundamental configuration. * o (maybe) Software WEP support (patch from Stano Meduna). @@ -373,27 +414,27 @@ * flag after taking the lock, and if it is set, give up on whatever * they are doing and drop the lock again. The orinoco_lock() * function handles this (it unlocks and returns -EBUSY if - * hw_unavailable is true). */ + * hw_unavailable is non-zero). */ #include #include #include #include -#include #include #include #include #include #include -#include -#include -#include #include #include #include #include +#include +#include +#include + #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" @@ -416,6 +457,9 @@ EXPORT_SYMBOL(orinoco_debug); #endif +static int suppress_linkstatus; /* = 0 */ +MODULE_PARM(suppress_linkstatus, "i"); + /********************************************************************/ /* Compile time configuration and compatibility stuff */ /********************************************************************/ @@ -443,8 +487,10 @@ #define USER_BAP 0 #define IRQ_BAP 1 #define MAX_IRQLOOPS_PER_IRQ 10 -#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of how many events the - device could legitimately generate */ +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of + * how many events the + * device could + * legitimately generate */ #define SMALL_KEY_SIZE 5 #define LARGE_KEY_SIZE 13 #define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */ @@ -480,8 +526,8 @@ {10, 1, 1, 1}, {20, 0, 2, 2}, {20, 1, 6, 3}, - {55, 0, 4, 4}, - {55, 1, 7, 7}, + {55, 0, 4, 4}, + {55, 1, 7, 7}, {110, 0, 5, 8}, }; #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) @@ -522,7 +568,7 @@ /* Hardware control routines */ -static int __orinoco_program_rids(struct orinoco_private *priv); +static int __orinoco_program_rids(struct net_device *dev); static int __orinoco_hw_set_bitrate(struct orinoco_private *priv); static int __orinoco_hw_setup_wep(struct orinoco_private *priv); @@ -535,37 +581,17 @@ static void __orinoco_set_multicast_list(struct net_device *dev); /* Interrupt handling routines */ -static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); +static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw); /* ioctl() routines */ -static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); -static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq); -static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq); -static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq); -static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq); -static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq); -static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq); -static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq); -static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq); -static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq); -static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq); -static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq); -static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq); -static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *frq); -static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *frq); -static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq); -static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq); -static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq); -static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq); - -static int orinoco_debug_dump_recs(struct orinoco_private *priv); +static int orinoco_debug_dump_recs(struct net_device *dev); /********************************************************************/ /* Function prototypes */ @@ -577,7 +603,7 @@ struct hermes *hw = &priv->hw; int err; - err = __orinoco_program_rids(priv); + err = __orinoco_program_rids(dev); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", dev->name, err); @@ -606,14 +632,25 @@ netif_stop_queue(dev); - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_ERR "%s: Error %d disabling MAC port\n", - dev->name, err); - return err; + if (! priv->hw_unavailable) { + if (! priv->broken_disableport) { + err = hermes_disable_port(hw, 0); + if (err) { + /* Some firmwares (e.g. Intersil 1.3.x) seem + * to have problems disabling the port, oh + * well, too bad. */ + printk(KERN_WARNING "%s: Error %d disabling MAC port\n", + dev->name, err); + priv->broken_disableport = 1; + } + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); } - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); + + /* firmware will have to reassociate */ + priv->last_linkstatus = 0xffff; + priv->connected = 0; return 0; } @@ -656,38 +693,38 @@ if (err) return err; - priv->open = 1; - err = __orinoco_up(dev); + if (! err) + priv->open = 1; + orinoco_unlock(priv, &flags); return err; } -static int orinoco_stop(struct net_device *dev) +int orinoco_stop(struct net_device *dev) { struct orinoco_private *priv = dev->priv; int err = 0; /* We mustn't use orinoco_lock() here, because we need to be - able to close the interface, even if hw_unavailable is set + able to close the interface even if hw_unavailable is set (e.g. as we're released after a PC Card removal) */ spin_lock_irq(&priv->lock); priv->open = 0; - if (! priv->hw_unavailable) - err = __orinoco_down(dev); + err = __orinoco_down(dev); spin_unlock_irq(&priv->lock); return err; } -static int __orinoco_program_rids(struct orinoco_private *priv) +static int __orinoco_program_rids(struct net_device *dev) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; int err; struct hermes_idstring idbuf; @@ -873,51 +910,84 @@ } /* xyzzy */ -static int orinoco_reconfigure(struct orinoco_private *priv) +static int orinoco_reconfigure(struct net_device *dev) { + struct orinoco_private *priv = dev->priv; struct hermes *hw = &priv->hw; unsigned long flags; int err = 0; - orinoco_lock(priv, &flags); + if (priv->broken_disableport) { + schedule_work(&priv->reset_work); + return 0; + } + + err = orinoco_lock(priv, &flags); + if (err) + return err; + err = hermes_disable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", - priv->ndev->name); + printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", + dev->name); + priv->broken_disableport = 1; goto out; } - err = __orinoco_program_rids(priv); - if (err) + err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); goto out; + } err = hermes_enable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", - priv->ndev->name); + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); goto out; } out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); + schedule_work(&priv->reset_work); + err = 0; + } + orinoco_unlock(priv, &flags); return err; } /* This must be called from user context, without locks held - use - * schedule_task() */ + * schedule_work() */ static void orinoco_reset(struct net_device *dev) { struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; int err; unsigned long flags; err = orinoco_lock(priv, &flags); if (err) + /* When the hardware becomes available again, whatever + * detects that is responsible for re-initializing + * it. So no need for anything further*/ return; - priv->hw_unavailable = 1; + netif_stop_queue(dev); + + /* Shut off interrupts. Depending on what state the hardware + * is in, this might not work, but we'll try anyway */ + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); + + priv->hw_unavailable++; + priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ + priv->connected = 0; + orinoco_unlock(priv, &flags); if (priv->hard_reset) @@ -936,18 +1006,22 @@ return; } - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); /* This has to be called from user context */ - priv->hw_unavailable = 0; + priv->hw_unavailable--; - err = __orinoco_up(dev); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", - dev->name, err); - } else - dev->trans_start = jiffies; + /* priv->open or priv->hw_unavailable might have changed while + * we dropped the lock */ + if (priv->open && (! priv->hw_unavailable)) { + err = __orinoco_up(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", + dev->name, err); + } else + dev->trans_start = jiffies; + } - orinoco_unlock(priv, &flags); + spin_unlock_irq(&priv->lock); return; } @@ -979,10 +1053,18 @@ } } +/* Does the frame have a SNAP header indicating it should be + * de-encapsulated to Ethernet-II? */ static inline int -is_snap(struct header_struct *hdr) +is_ethersnap(struct header_struct *hdr) { - return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); + /* We de-encapsulate all packets which, a) have SNAP headers + * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header + * and where b) the OUI of the SNAP header is 00:00:00 or + * 00:00:f8 - we need both because different APs appear to use + * different OUIs for some reason */ + return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) + && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); } static void @@ -1140,7 +1222,8 @@ return 0; } -static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]) +static int orinoco_hw_get_bssid(struct orinoco_private *priv, + char buf[ETH_ALEN]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1159,7 +1242,7 @@ } static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]) + char buf[IW_ESSID_MAX_SIZE+1]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1236,9 +1319,8 @@ } if ( (channel < 1) || (channel > NUM_CHANNELS) ) { - struct net_device *dev = priv->ndev; - - printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel); + printk(KERN_WARNING "%s: Channel out of range (%d)!\n", + priv->ndev->name, channel); err = -EBUSY; goto out; @@ -1253,8 +1335,8 @@ return err ? err : freq; } -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max) +static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max) { hermes_t *hw = &priv->hw; struct hermes_idstring list; @@ -1287,9 +1369,6 @@ } #if 0 -#ifndef ORINOCO_DEBUG -static inline void show_rx_frame(struct orinoco_rxframe_hdr *frame) {} -#else static void show_rx_frame(struct orinoco_rxframe_hdr *frame) { printk(KERN_DEBUG "RX descriptor:\n"); @@ -1346,17 +1425,16 @@ frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype); } -#endif -#endif +#endif /* 0 */ /* * Interrupt handler */ -void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct orinoco_private *priv = (struct orinoco_private *) dev_id; + struct net_device *dev = (struct net_device *)dev_id; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; - struct net_device *dev = priv->ndev; int count = MAX_IRQLOOPS_PER_IRQ; u16 evstat, events; /* These are used to detect a runaway interrupt situation */ @@ -1367,12 +1445,17 @@ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { - /* If hw is unavailable */ - return; + /* If hw is unavailable - we don't know if the irq was + * for us or not */ + return IRQ_HANDLED; } evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; + if (! events) { + orinoco_unlock(priv, &flags); + return IRQ_NONE; + } if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; @@ -1380,11 +1463,11 @@ while (events && count--) { if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { - printk(KERN_CRIT "%s: IRQ handler is looping too \ -much! Shutting down.\n", - dev->name); - /* Perform an emergency shutdown */ + printk(KERN_WARNING "%s: IRQ handler is looping too " + "much! Resetting.\n", dev->name); + /* Disable interrupts for now */ hermes_set_irqmask(hw, 0); + schedule_work(&priv->reset_work); break; } @@ -1395,21 +1478,21 @@ } if (events & HERMES_EV_TICK) - __orinoco_ev_tick(priv, hw); + __orinoco_ev_tick(dev, hw); if (events & HERMES_EV_WTERR) - __orinoco_ev_wterr(priv, hw); + __orinoco_ev_wterr(dev, hw); if (events & HERMES_EV_INFDROP) - __orinoco_ev_infdrop(priv, hw); + __orinoco_ev_infdrop(dev, hw); if (events & HERMES_EV_INFO) - __orinoco_ev_info(priv, hw); + __orinoco_ev_info(dev, hw); if (events & HERMES_EV_RX) - __orinoco_ev_rx(priv, hw); + __orinoco_ev_rx(dev, hw); if (events & HERMES_EV_TXEXC) - __orinoco_ev_txexc(priv, hw); + __orinoco_ev_txexc(dev, hw); if (events & HERMES_EV_TX) - __orinoco_ev_tx(priv, hw); + __orinoco_ev_tx(dev, hw); if (events & HERMES_EV_ALLOC) - __orinoco_ev_alloc(priv, hw); + __orinoco_ev_alloc(dev, hw); hermes_write_regn(hw, EVACK, events); @@ -1418,30 +1501,34 @@ }; orinoco_unlock(priv, &flags); + return IRQ_HANDLED; } -static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) { - printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name); + printk(KERN_DEBUG "%s: TICK\n", dev->name); } -static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", - priv->ndev->name); + dev->name); } -static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) { - printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name); + printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); } static void print_linkstatus(struct net_device *dev, u16 status) { char * s; + if (suppress_linkstatus) + return; + switch (status) { case HERMES_LINKSTATUS_NOT_CONNECTED: s = "Not Connected"; @@ -1472,9 +1559,9 @@ dev->name, s, status); } -static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; u16 infofid; struct { u16 len; @@ -1573,9 +1660,9 @@ } } -static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; @@ -1664,14 +1751,13 @@ * So, check ourselves */ if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || - is_snap(&hdr)) { + is_ethersnap(&hdr)) { /* These indicate a SNAP within 802.2 LLC within 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ stats->rx_length_errors++; - stats->rx_dropped++; goto drop; } @@ -1726,9 +1812,9 @@ return; } -static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); struct hermes_tx_descriptor desc; @@ -1752,8 +1838,9 @@ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } -static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) { + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; stats->tx_packets++; @@ -1761,9 +1848,10 @@ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } -static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; + u16 fid = hermes_read_regn(hw, ALLOCFID); if (fid != priv->txfid) { @@ -1945,7 +2033,7 @@ TRACE_ENTER(dev->name); - /* No need to lock, the resetting flag is already set in + /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; @@ -2081,8 +2169,6 @@ priv->wep_on = 0; priv->tx_key = 0; - priv->hw_unavailable = 0; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO) { /* Try workaround for old Symbol firmware bug */ @@ -2102,6 +2188,12 @@ goto out; } + /* Make the hardware available, as long as it hasn't been + * removed elsewhere (e.g. by PCMCIA hot unplug) */ + spin_lock_irq(&priv->lock); + priv->hw_unavailable--; + spin_unlock_irq(&priv->lock); + printk(KERN_DEBUG "%s: ready\n", dev->name); out: @@ -2267,7 +2359,7 @@ /* Length of the packet body */ /* FIXME: what if the skb is smaller than this? */ - len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN); + len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN); eh = (struct ethhdr *)skb->data; @@ -2281,6 +2373,12 @@ goto fail; } + /* Clear the 802.11 header and data length fields - some + * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused + * if this isn't done. */ + hermes_clear_words(hw, HERMES_DATA0, + HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); + /* Encapsulate Ethernet-II frames */ if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */ struct header_struct hdr; @@ -2362,7 +2460,7 @@ stats->tx_errors++; - schedule_task(&priv->timeout_task); + schedule_work(&priv->reset_work); } static int @@ -2532,7 +2630,7 @@ } err = orinoco_hw_get_bitratelist(priv, &numrates, - range.bitrate, IW_MAX_BITRATES); + range.bitrate, IW_MAX_BITRATES); if (err) return err; range.num_bitrates = numrates; @@ -2799,7 +2897,7 @@ erq->flags = 1; erq->length = strlen(essidbuf) + 1; if (erq->pointer) - if ( copy_to_user(erq->pointer, essidbuf, erq->length) ) + if (copy_to_user(erq->pointer, essidbuf, erq->length)) return -EFAULT; TRACE_EXIT(dev->name); @@ -3128,7 +3226,7 @@ rrq->value = 5500000; else rrq->value = val * 1000000; - break; + break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ for (i = 0; i < BITRATE_TABLE_SIZE; i++) @@ -3754,7 +3852,7 @@ printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); - schedule_task(&priv->timeout_task); + schedule_work(&priv->reset_work); break; case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ @@ -3827,7 +3925,7 @@ break; case SIOCIWLASTPRIV: - err = orinoco_debug_dump_recs(priv); + err = orinoco_debug_dump_recs(dev); if (err) printk(KERN_ERR "%s: Unable to dump records (%d)\n", dev->name, err); @@ -3839,7 +3937,7 @@ } if (! err && changed && netif_running(dev)) { - err = orinoco_reconfigure(priv); + err = orinoco_reconfigure(dev); } TRACE_EXIT(dev->name); @@ -3924,7 +4022,7 @@ DEBUG_REC(PRIID,WORDS), DEBUG_REC(PRISUPRANGE,WORDS), DEBUG_REC(CFIACTRANGES,WORDS), - DEBUG_REC(NICSERNUM,WORDS), + DEBUG_REC(NICSERNUM,XSTRING), DEBUG_REC(NICID,WORDS), DEBUG_REC(MFISUPRANGE,WORDS), DEBUG_REC(CFISUPRANGE,WORDS), @@ -3961,8 +4059,9 @@ #define DEBUG_LTV_SIZE 128 -static int orinoco_debug_dump_recs(struct orinoco_private *priv) +static int orinoco_debug_dump_recs(struct net_device *dev) { + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; u8 *val8; u16 *val16; @@ -4051,6 +4150,7 @@ dev->do_ioctl = orinoco_ioctl; dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; + /* we use the default eth_mac_addr for setting the MAC addr */ /* Set up default callbacks */ dev->open = orinoco_open; @@ -4062,7 +4162,7 @@ priv->hw_unavailable = 1; /* orinoco_init() must clear this * before anything else touches the * hardware */ - INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev); + INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); priv->last_linkstatus = 0xffff; priv->connected = 0; @@ -4079,13 +4179,14 @@ EXPORT_SYMBOL(__orinoco_up); EXPORT_SYMBOL(__orinoco_down); +EXPORT_SYMBOL(orinoco_stop); EXPORT_SYMBOL(orinoco_reinit_firmware); EXPORT_SYMBOL(orinoco_interrupt); /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco.c 0.13b (David Gibson and others)"; +static char version[] __initdata = "orinoco.c 0.13e (David Gibson and others)"; static int __init init_orinoco(void) { --- linux-2.4.21/drivers/net/wireless/orinoco.h~orinoco013e +++ linux-2.4.21/drivers/net/wireless/orinoco.h @@ -11,9 +11,29 @@ #include #include #include -#include +#include #include "hermes.h" +/* Workqueue / task queue backwards compatibility stuff */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) +#include +#else +#include +#define work_struct tq_struct +#define INIT_WORK INIT_TQUEUE +#define schedule_work schedule_task +#endif + +/* Interrupt handler backwards compatibility stuff */ +#ifndef IRQ_NONE + +#define IRQ_NONE +#define IRQ_HANDLED +typedef void irqreturn_t; + +#endif + /* To enable debug messages */ //#define ORINOCO_DEBUG 3 @@ -36,13 +56,13 @@ struct orinoco_private { - void *card; /* Pointer to card dependant structure */ + void *card; /* Pointer to card dependent structure */ int (*hard_reset)(struct orinoco_private *); /* Synchronisation stuff */ spinlock_t lock; int hw_unavailable; - struct tq_struct timeout_task; + struct work_struct reset_work; /* driver state */ int open; @@ -72,6 +92,7 @@ int has_sensitivity; int nicbuf_size; u16 channel_mask; + int broken_disableport; /* Configuration paramaters */ u32 iw_mode; @@ -111,9 +132,9 @@ int (*hard_reset)(struct orinoco_private *)); extern int __orinoco_up(struct net_device *dev); extern int __orinoco_down(struct net_device *dev); -int orinoco_reinit_firmware(struct net_device *dev); - -extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); +extern int orinoco_stop(struct net_device *dev); +extern int orinoco_reinit_firmware(struct net_device *dev); +extern irqreturn_t orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); /********************************************************************/ /* Locking and synchronization functions */ --- linux-2.4.21/drivers/net/wireless/orinoco_cs.c~orinoco013e +++ linux-2.4.21/drivers/net/wireless/orinoco_cs.c @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.13b - (formerly known as dldwd_cs.c) +/* orinoco_cs.c 0.13e - (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -22,11 +22,7 @@ #include #include #include -#include #include -#include -#include -#include #include #include #include @@ -38,7 +34,10 @@ #include #include #include -#include + +#include +#include +#include #include "orinoco.h" @@ -62,7 +61,7 @@ /* Some D-Link cards have buggy CIS. They do work at 5v properly, but * don't have any CIS entry for it. This workaround it... */ -static int ignore_cis_vcc; /* = 0 */ +static int ignore_cis_vcc = 1; MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); @@ -145,8 +144,10 @@ /* PCMCIA stuff */ /********************************************************************/ +/* In 2.5 (as of 2.5.69 at least) there is a cs_error exported which + * does this, but it's not in 2.4 so we do our own for now. */ static void -cs_error(client_handle_t handle, int func, int ret) +orinoco_cs_error(client_handle_t handle, int func, int ret) { error_info_t err = { func, ret }; CardServices(ReportError, handle, &err); @@ -202,6 +203,7 @@ link->priv = dev; /* Initialize the dev_link_t structure */ + init_timer(&link->release); link->release.function = &orinoco_cs_release; link->release.data = (u_long) link; @@ -240,7 +242,7 @@ ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != CS_SUCCESS) { - cs_error(link->handle, RegisterClient, ret); + orinoco_cs_error(link->handle, RegisterClient, ret); orinoco_cs_detach(link); return NULL; } @@ -269,19 +271,12 @@ return; } - /* - If the device is currently configured and active, we won't - actually delete it yet. Instead, it is marked so that when - the release() function is called, that will trigger a proper - detach(). - */ if (link->state & DEV_CONFIG) { -#ifdef PCMCIA_DEBUG - printk(KERN_DEBUG "orinoco_cs: detach postponed, '%s' " - "still locked\n", link->dev->dev_name); -#endif - link->state |= DEV_STALE_LINK; - return; + orinoco_cs_release((u_long)link); + if (link->state & DEV_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } } /* Break the link with Card Services */ @@ -368,7 +363,7 @@ CS_CHECK(GetFirstTuple, handle, &tuple); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - cistpl_cftable_entry_t dflt = { index: 0 }; + cistpl_cftable_entry_t dflt = { .index = 0 }; CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); @@ -472,7 +467,7 @@ link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = orinoco_interrupt; - link->irq.Instance = priv; + link->irq.Instance = dev; CS_CHECK(RequestIRQ, link->handle, &link->irq); } @@ -532,7 +527,7 @@ return; cs_failed: - cs_error(link->handle, last_fn, last_ret); + orinoco_cs_error(link->handle, last_fn, last_ret); failed: orinoco_cs_release((u_long) link); @@ -549,18 +544,13 @@ dev_link_t *link = (dev_link_t *) arg; struct net_device *dev = link->priv; struct orinoco_private *priv = dev->priv; + unsigned long flags; - /* - If the device is currently in use, we won't release until it - is actually closed, because until then, we can't be sure that - no one will try to access the device or its data structures. - */ - if (priv->open) { - DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n", - link->dev->dev_name); - link->state |= DEV_STALE_CONFIG; - return; - } + /* We're committed to taking the device away now, so mark the + * hardware as unavailable */ + spin_lock_irqsave(&priv->lock, flags); + priv->hw_unavailable++; + spin_unlock_irqrestore(&priv->lock, flags); /* Don't bother checking to see if these succeed or not */ CardServices(ReleaseConfiguration, link->handle); @@ -593,14 +583,9 @@ orinoco_lock(priv, &flags); netif_device_detach(dev); - priv->hw_unavailable = 1; + priv->hw_unavailable++; orinoco_unlock(priv, &flags); - -/* if (link->open) */ -/* orinoco_cs_stop(dev); */ - - mod_timer(&link->release, jiffies + HZ / 20); } break; @@ -619,13 +604,8 @@ a better way, short of rewriting the PCMCIA layer to not suck :-( */ if (! test_bit(0, &card->hard_reset_in_progress)) { - err = orinoco_lock(priv, &flags); - if (err) { - printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n", - dev->name); - break; - } - + spin_lock_irqsave(&priv->lock, flags); + err = __orinoco_down(dev); if (err) printk(KERN_WARNING "%s: %s: Error %d downing interface\n", @@ -634,9 +614,9 @@ err); netif_device_detach(dev); - priv->hw_unavailable = 1; - - orinoco_unlock(priv, &flags); + priv->hw_unavailable++; + + spin_unlock_irqrestore(&priv->lock, flags); } CardServices(ReleaseConfiguration, link->handle); @@ -653,10 +633,6 @@ CardServices(RequestConfiguration, link->handle, &link->conf); - /* If we're only getting these events because - of the ResetCard in the hard reset, we - don't need to do anything - orinoco_reset() - will handle reinitialization. */ if (! test_bit(0, &card->hard_reset_in_progress)) { err = orinoco_reinit_firmware(dev); if (err) { @@ -668,9 +644,9 @@ spin_lock_irqsave(&priv->lock, flags); netif_device_attach(dev); - priv->hw_unavailable = 0; + priv->hw_unavailable--; - if (priv->open) { + if (priv->open && ! priv->hw_unavailable) { err = __orinoco_up(dev); if (err) printk(KERN_ERR "%s: Error %d restarting card\n", @@ -678,7 +654,7 @@ } - orinoco_unlock(priv, &flags); + spin_unlock_irqrestore(&priv->lock, flags); } } break; @@ -693,7 +669,7 @@ /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco_cs.c 0.13b (David Gibson and others)"; +static char version[] __initdata = "orinoco_cs.c 0.13e (David Gibson and others)"; static int __init init_orinoco_cs(void) @@ -722,7 +698,6 @@ if (dev_list) DEBUG(0, "orinoco_cs: Removing leftover devices.\n"); while (dev_list != NULL) { - del_timer(&dev_list->release); if (dev_list->state & DEV_CONFIG) orinoco_cs_release((u_long) dev_list); orinoco_cs_detach(dev_list); --- linux-2.4.21/drivers/pcmcia/pxa/Makefile~ramses-pcmcia +++ linux-2.4.21/drivers/pcmcia/pxa/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARCH_PXA_IDP) += pxa_idp.o obj-$(CONFIG_ARCH_TRIZEPS2) += trizeps2.o obj-$(CONFIG_ARCH_PXA_CERF) += ../sa1100_cerf.o +obj-$(CONFIG_ARCH_RAMSES) += ramses.o obj-m := $(O_TARGET) --- linux-2.4.21/drivers/pcmcia/pxa/pxa.c~pxa-pcmcia +++ linux-2.4.21/drivers/pcmcia/pxa/pxa.c @@ -187,7 +187,6 @@ struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; unsigned int i, clock; - unsigned long mecr; printk(KERN_INFO "Intel PXA250/210 PCMCIA (CS release %s)\n", CS_RELEASE); @@ -240,6 +239,8 @@ pcmcia_low_level=&pxa_idp_pcmcia_ops; } else if( machine_is_pxa_cerf()){ pcmcia_low_level=&cerf_pcmcia_ops; + } else if( machine_is_ramses()){ + pcmcia_low_level=&ramses_pcmcia_ops; } else if (machine_is_trizeps2()){ #ifdef CONFIG_ARCH_TRIZEPS2 pcmcia_low_level=&trizeps2_pcmcia_ops; @@ -835,7 +836,7 @@ static int pxa_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map){ unsigned int clock, speed; - unsigned long mecr, start; + unsigned long start; DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); @@ -941,7 +942,7 @@ static int pxa_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map){ unsigned int clock, speed; - unsigned long mecr, start; + unsigned long start; DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); @@ -1076,7 +1077,6 @@ char *p=buf; unsigned int sock=(unsigned int)data; unsigned int clock = get_lclk_frequency_10khz(); - unsigned long mecr = MECR; p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", pxa_pcmcia_socket[sock].k_state.detect?"detect ":"", --- linux-2.4.21/drivers/pcmcia/pxa/pxa.h~ramses-pcmcia +++ linux-2.4.21/drivers/pcmcia/pxa/pxa.h @@ -228,6 +228,7 @@ extern struct pcmcia_low_level lubbock_pcmcia_ops; extern struct pcmcia_low_level pxa_idp_pcmcia_ops; extern struct pcmcia_low_level cerf_pcmcia_ops; +extern struct pcmcia_low_level ramses_pcmcia_ops; extern struct pcmcia_low_level trizeps2_pcmcia_ops; #endif /* !defined(_PCMCIA_PXA_H) */ --- /dev/null +++ linux-2.4.21/drivers/pcmcia/pxa/ramses.c @@ -0,0 +1,223 @@ +/* + * linux/drivers/pcmcia/pxa/ramses.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Copyright (c) 2003 M&N Logistik-Lösungen Online GmbH + * + * Platform specific routines for the Ramses, based on those + * first done for the Lubbock and PXA IDP. + * + */ + +#include +#include + +#include + +#include +#include +#include +#include + +static int +ramses_pcmcia_init(struct pcmcia_init *init) +{ + int return_val = 0; + + /* Set PCMCIA Socket 0 power to standby mode. + * RAMSES has dedicated CPLD pins for all this stuff :-) + */ + + /* both slots disabled, reset NOT active */ + RAMSES_CPLD_PCCARD_EN = PCC0_ENABLE | PCC1_ENABLE; + + RAMSES_CPLD_PCCARD_PWR = 0; //all power to both slots off + //GPDR(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID)) &= ~GPIO_bit(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID)); + set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(CFCARD_CD_VALID), GPIO_BOTH_EDGES); + //GPDR(IRQ_TO_GPIO_2_80(CFCARD_RDYINT)) &= ~GPIO_bit(IRQ_TO_GPIO_2_80(CFCARD_RDYINT)); + set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(CFCARD_RDYINT), GPIO_FALLING_EDGE); + + return_val += + request_irq(CFCARD_CD_VALID, init->handler, SA_INTERRUPT, + "CF-Card CD", NULL); + + if (return_val < 0) { + return -1; + } + + return 2; +} + +static int +ramses_pcmcia_shutdown(void) +{ + + free_irq(CFCARD_CD_VALID, NULL); + + RAMSES_CPLD_PCCARD_EN = 0x03; //disable slots + udelay(200); + RAMSES_CPLD_PCCARD_PWR = 0; //shut off all power + + return 0; +} + +static int +ramses_pcmcia_socket_state(struct pcmcia_state_array *state_array) +{ + unsigned long status; + int return_val = 1; + int i; + volatile unsigned long *stat_regs[2] = { + &RAMSES_CPLD_PCCARD0_STATUS, + &RAMSES_CPLD_PCCARD1_STATUS + }; + + if (state_array->size < 2) + return -1; + + memset(state_array->state, 0, + (state_array->size) * sizeof (struct pcmcia_state)); + + for (i = 1; i < 2; i++) { + + status = *stat_regs[i]; + + /* this one is a gpio */ + state_array->state[i].detect = (PCC_DETECT(i)) ? 0 : 1; + + state_array->state[i].ready = ((status & _PCC_IRQ) == 0) ? 0 : 1; + state_array->state[i].bvd1 = (status & PCC_BVD1) ? 0 : 1; + state_array->state[i].bvd2 = (status & PCC_BVD2) ? 0 : 1; + state_array->state[i].wrprot = (status & _PCC_WRPROT) ? 1 : 0; + state_array->state[i].vs_3v = (status & PCC_VS1) ? 0 : 1; + state_array->state[i].vs_Xv = (status & PCC_VS2) ? 0 : 1; + } + + state_array->state[0].detect = 0; + state_array->state[0].ready = 0; + state_array->state[0].bvd1 = 0; + state_array->state[0].bvd2 = 0; + state_array->state[0].wrprot = 0; + state_array->state[0].vs_3v = 0; + state_array->state[0].vs_Xv = 0; + + return return_val; +} + +static int +ramses_pcmcia_get_irq_info(struct pcmcia_irq_info *info) +{ + switch (info->sock) { + case 0: + //info->irq = PCMCIA_S0_RDYINT; + //printk("//hs ramses_pcmcia_get_irq_info called for slot 0\n"); + break; + + case 1: + info->irq = CFCARD_RDYINT; + break; + + default: + return -1; + } + + return 0; +} + +static int +ramses_pcmcia_configure_socket(unsigned int sock, socket_state_t *state) +{ + /* The Ramses uses the Maxim MAX1602, with the following connections: + * + * Socket 0 (PCMCIA): + * MAX1602 PXA_IDP Register + * Pin Signal RAMSES_CPLD_PCCARD_PWR: + * ----- ------- ---------------------- + * A0VPP PCC0_PWR0 bit0 + * A1VPP PCC0_PWR1 bit1 + * A0VCC PCC0_PWR2 bit2 + * A1VCC PCC0_PWR3 bit3 + * VX VCC + * VY +3.3V + * 12IN +12V + * CODE +3.3V Cirrus Code, CODE = High (VY) + * + * Socket 1 (PCMCIA): + * MAX1602 PXA_IDP Register + * Pin Signal RAMSES_CPLD_PCCARD_PWR: + * ----- ------- ---------------------- + * A0VPP PCC1_PWR0 bit4 + * A1VPP PCC1_PWR1 bit5 + * A0VCC PCC1_PWR2 bit6 + * A1VCC PCC1_PWR3 bit7 + * VX VCC + * VY +3.3V + * 12IN +12V + * CODE +3.3V Cirrus Code, CODE = High (VY) + * + */ + + if (sock == 1) { + + switch (state->Vcc) { + case 0: + RAMSES_CPLD_PCCARD_EN |= PCC1_ENABLE; // disable socket + udelay(200); + RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); + break; + + case 33: + RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); + RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR3; + RAMSES_CPLD_PCCARD_EN &= ~PCC1_ENABLE; //turn it on + break; + + case 50: + RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3); + RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR2; + RAMSES_CPLD_PCCARD_EN &= ~PCC1_ENABLE; + break; + + default: + printk(KERN_ERR "%s(): unrecognized Vcc %u\n", + __FUNCTION__, state->Vcc); + return -1; + } + + switch (state->Vpp) { + case 0: + RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1); + break; + + case 120: + RAMSES_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1); + RAMSES_CPLD_PCCARD_PWR |= PCC1_PWR1; + break; + + default: + if (state->Vpp == state->Vcc) + RAMSES_CPLD_PCCARD_PWR = + (RAMSES_CPLD_PCCARD_PWR & + ~(PCC1_PWR0 | PCC1_PWR1)) | PCC1_PWR0; + else { + printk(KERN_ERR "%s(): unrecognized Vpp %u\n", + __FUNCTION__, state->Vpp); + return -1; + } + } + RAMSES_CPLD_PCCARD_EN = (state->flags & SS_RESET) ? (RAMSES_CPLD_PCCARD_EN | PCC1_RESET) + : (RAMSES_CPLD_PCCARD_EN & ~PCC1_RESET); + } + return 0; +} + +struct pcmcia_low_level ramses_pcmcia_ops = { + ramses_pcmcia_init, + ramses_pcmcia_shutdown, + ramses_pcmcia_socket_state, + ramses_pcmcia_get_irq_info, + ramses_pcmcia_configure_socket +}; --- linux-2.4.21/drivers/scsi/scsi.h~usb-sonycamera +++ linux-2.4.21/drivers/scsi/scsi.h @@ -610,6 +610,7 @@ unsigned remap:1; /* support remapping */ unsigned starved:1; /* unable to process commands because host busy */ + unsigned no_start_on_add:1; /* do not issue start on add */ // Flag to allow revalidate to succeed in sd_open int allow_revalidate; --- linux-2.4.21/drivers/scsi/scsi_scan.c~usb-sonycamera +++ linux-2.4.21/drivers/scsi/scsi_scan.c @@ -37,6 +37,8 @@ #define BLIST_ISDISK 0x100 /* Treat as (removable) disk */ #define BLIST_ISROM 0x200 /* Treat as (removable) CD-ROM */ #define BLIST_LARGELUN 0x400 /* LUNs larger than 7 despite reporting as SCSI 2 */ +#define BLIST_NOSTARTONADD 0x1000 /* do not do automatic start on add */ + static void print_inquiry(unsigned char *data); static int scan_scsis_single(unsigned int channel, unsigned int dev, @@ -110,9 +112,10 @@ {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ - {"HP", "A6188A", "*", BLIST_SPARSELUN}, /* HP Va7100 Array */ - {"HP", "A6189A", "*", BLIST_SPARSELUN}, /* HP Va7400 Array */ - {"HP", "A6189B", "*", BLIST_SPARSELUN}, /* HP Va7410 Array */ + {"HP", "A6188A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7100 Array */ + {"HP", "A6189A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7400 Array */ + {"HP", "A6189B", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7110 Array */ + {"HP", "A6218A", "*", BLIST_SPARSELUN | BLIST_LARGELUN},/* HP Va7410 Array */ {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 * extra reset */ @@ -145,7 +148,7 @@ {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN}, {"CANON", "IPUBJD", "*", BLIST_SPARSELUN}, {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, - {"DEC","HSG80","*", BLIST_FORCELUN}, + {"DEC","HSG80","*", BLIST_FORCELUN | BLIST_NOSTARTONADD}, {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, {"COMPAQ","CR3500","*", BLIST_FORCELUN}, {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -173,7 +176,11 @@ {"HP", "NetRAID-4M", "*", BLIST_FORCELUN}, {"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN}, {"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN}, - {"COMPAQ", "MSA1000", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"APPLE", "Xserve", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"COMPAQ", "MSA1000", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, + {"COMPAQ", "MSA1000 VOLUME", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, + {"COMPAQ", "HSV110", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, + {"HP", "HSV100", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, {"HP", "C1557A", "*", BLIST_FORCELUN}, {"IBM", "AuSaV1S2", "*", BLIST_FORCELUN}, {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, @@ -182,7 +189,8 @@ {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"HITACHI", "OPEN-", "*", BLIST_SPARSELUN}, /* HITACHI XP Arrays */ + {"HITACHI", "OPEN-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HITACHI XP Arrays */ + {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HITACHI 9960 */ {"WINSYS","FLASHDISK G6", "*", BLIST_SPARSELUN}, {"DotHill","SANnet RAID X300", "*", BLIST_SPARSELUN}, {"SUN", "T300", "*", BLIST_SPARSELUN}, @@ -194,6 +202,12 @@ {"SGI", "TP9400", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"SGI", "TP9500", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"MYLEX", "DACARMRB", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"PLATYPUS", "CX5", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"Raidtec", "FCR", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "C7200", "*", BLIST_SPARSELUN}, /* Medium Changer */ + {"SMSC", "USB 2 HS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"XYRATEX", "RS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"NEC", "iStorage", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, /* * Must be at end of list... @@ -209,10 +223,14 @@ static unsigned int max_scsi_luns = 1; #endif +static unsigned int scsi_allow_ghost_devices = 0; + #ifdef MODULE MODULE_PARM(max_scsi_luns, "i"); MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 2^32-1)"); +MODULE_PARM(scsi_allow_ghost_devices, "i"); +MODULE_PARM_DESC(scsi_allow_ghost_devices, "allow devices marked as being offline to be accessed anyway (0 = off, else allow ghosts on lun 0 through scsi_allow_ghost_devices - 1"); #else @@ -232,6 +250,21 @@ __setup("max_scsi_luns=", scsi_luns_setup); +static int __init scsi_allow_ghost_devices_setup(char *str) +{ + unsigned int tmp; + + if (get_option(&str, &tmp) == 1) { + scsi_allow_ghost_devices = tmp; + return 1; + } else { + printk("scsi_allow_ghost_devices_setup: usage scsi_allow_ghost_devices=n (0: off else\nallow ghost devices (ghost devices are devices that report themselves as\nbeing offline but which we allow access to anyway) on lun 0 through n - 1.\n"); + return 0; + } +} + +__setup("scsi_allow_ghost_devices=", scsi_allow_ghost_devices_setup); + #endif static void print_inquiry(unsigned char *data) @@ -608,6 +641,7 @@ } else { /* assume no peripheral if any other sort of error */ scsi_release_request(SRpnt); + scsi_release_commandblocks(SDpnt); return 0; } } @@ -618,6 +652,24 @@ */ /* + * If we are offline and we are on a LUN != 0, then skip this entry. + * If we are on a BLIST_FORCELUN device this will stop the scan at + * the first offline LUN (typically the correct thing to do). If + * we are on a BLIST_SPARSELUN device then this won't stop the scan, + * but it will keep us from having false entries in our device + * array. DL + * + * NOTE: Need to test this to make sure it doesn't cause problems + * with tape autoloaders, multidisc CD changers, and external + * RAID chassis that might use sparse luns or multiluns... DL + */ + if (lun != 0 && (scsi_result[0] >> 5) == 1) { + scsi_release_request(SRpnt); + scsi_release_commandblocks(SDpnt); + return 0; + } + + /* * Get any flags for this device. */ bflags = get_device_flags (scsi_result); @@ -655,8 +707,11 @@ SDpnt->removable = (0x80 & scsi_result[1]) >> 7; /* Use the peripheral qualifier field to determine online/offline */ - if (((scsi_result[0] >> 5) & 7) == 1) SDpnt->online = FALSE; - else SDpnt->online = TRUE; + if ((((scsi_result[0] >> 5) & 7) == 1) && + (lun >= scsi_allow_ghost_devices)) + SDpnt->online = FALSE; + else + SDpnt->online = TRUE; SDpnt->lockable = SDpnt->removable; SDpnt->changed = 0; SDpnt->access_count = 0; @@ -742,6 +797,13 @@ if ((bflags & BLIST_BORKEN) == 0) SDpnt->borken = 0; + /* + * Some devices may not want to have a start command automatically + * issued when a device is added. + */ + if (bflags & BLIST_NOSTARTONADD) + SDpnt->no_start_on_add = 1; + /* * If we want to only allow I/O to one of the luns attached to this device * at a time, then we set this flag. @@ -857,11 +919,26 @@ * I think we need REPORT LUNS in future to avoid scanning * of unused LUNs. But, that is another item. */ + /* if (*max_dev_lun < shpnt->max_lun) *max_dev_lun = shpnt->max_lun; else if ((max_scsi_luns >> 1) >= *max_dev_lun) *max_dev_lun += shpnt->max_lun; else *max_dev_lun = max_scsi_luns; + */ + /* + * Blech...the above code is broken. When you have a device + * that is present, and it is a FORCELUN device, then we + * need to scan *all* the luns on that device. Besides, + * skipping the scanning of LUNs is a false optimization. + * Scanning for a LUN on a present device is a very fast + * operation, it's scanning for devices that don't exist that + * is expensive and slow (although if you are truly scanning + * through MAX_SCSI_LUNS devices that would be bad, I hope + * all of the controllers out there set a reasonable value + * in shpnt->max_lun). DL + */ + *max_dev_lun = shpnt->max_lun; return 1; } /* --- linux-2.4.21/drivers/scsi/sd.c~usb-sonycamera +++ linux-2.4.21/drivers/scsi/sd.c @@ -775,7 +775,8 @@ char nbuff[6]; unsigned char *buffer; unsigned long spintime_value = 0; - int the_result, retries, spintime; + int retries, spintime; + unsigned int the_result; int sector_size; Scsi_Request *SRpnt; @@ -817,7 +818,7 @@ do { retries = 0; - while (retries < 3) { + do { cmd[0] = TEST_UNIT_READY; cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; @@ -832,10 +833,10 @@ the_result = SRpnt->sr_result; retries++; - if (the_result == 0 - || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION) - break; - } + } while (retries < 3 + && (the_result !=0 + || ((driver_byte(the_result) & DRIVER_SENSE) + && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION))); /* * If the drive has indicated to us that it doesn't have @@ -853,24 +854,47 @@ break; } + if ((driver_byte(the_result) & DRIVER_SENSE) == 0) { + /* no sense, TUR either succeeded or failed + * with a status error */ + if(!spintime && the_result != 0) + printk(KERN_NOTICE "%s: Unit Not Ready, error = 0x%x\n", nbuff, the_result); + break; + } + + /* + * The device does not want the automatic start to be issued. + */ + if (rscsi_disks[i].device->no_start_on_add) { + break; + } + + /* + * If manual intervention is required, or this is an + * absent USB storage device, a spinup is meaningless. + */ + if (SRpnt->sr_sense_buffer[2] == NOT_READY && + SRpnt->sr_sense_buffer[12] == 4 /* not ready */ && + SRpnt->sr_sense_buffer[13] == 3) { + break; /* manual intervention required */ /* Look for non-removable devices that return NOT_READY. * Issue command to spin up drive for these cases. */ - if (the_result && !rscsi_disks[i].device->removable && - SRpnt->sr_sense_buffer[2] == NOT_READY) { + } else if (the_result && !rscsi_disks[i].device->removable && + SRpnt->sr_sense_buffer[2] == NOT_READY) { unsigned long time1; if (!spintime) { printk("%s: Spinning up disk...", nbuff); cmd[0] = START_STOP; cmd[1] = (rscsi_disks[i].device->scsi_level <= SCSI_2) ? - ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; - cmd[1] |= 1; /* Return immediately */ + ((rscsi_disks[i].device->lun << 5) & 0xe0) : 0; + cmd[1] |= 1; /* Return immediately */ memset((void *) &cmd[2], 0, 8); - cmd[4] = 1; /* Start spin cycle */ + cmd[4] = 1; /* Start spin cycle */ SRpnt->sr_cmd_len = 0; SRpnt->sr_sense_buffer[0] = 0; SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; + SRpnt->sr_data_direction = SCSI_DATA_NONE; scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, 0/*512*/, SD_TIMEOUT, MAX_RETRIES); spintime_value = jiffies; @@ -883,6 +907,14 @@ time1 = schedule_timeout(time1); } while(time1); printk("."); + } else { + /* we don't understand the sense code, so it's + * probably pointless to loop */ + if(!spintime) { + printk(KERN_NOTICE "%s: Unit Not Ready, sense:\n", nbuff); + print_req_sense("", SRpnt); + } + break; } } while (the_result && spintime && time_after(spintime_value + 100 * HZ, jiffies)); --- linux-2.4.21/drivers/sound/ac97_codec.c~ucb1x00 +++ linux-2.4.21/drivers/sound/ac97_codec.c @@ -547,6 +547,12 @@ val = SOUND_CAP_EXCL_INPUT; break; + case SOUND_MIXER_AC97: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = codec->codec_read(codec, val); + return put_user(val, (int *)arg); + default: /* read a specific mixer */ i = _IOC_NR(cmd); @@ -575,6 +581,11 @@ codec->recmask_io(codec, 0, val); return 0; + + case SOUND_MIXER_AC97: + codec->codec_write(codec, val >> 16 & 0xffff, val & 0xffff); + return 0; + default: /* write a specific mixer */ i = _IOC_NR(cmd); --- linux-2.4.21/drivers/sound/pxa-ac97.c~pxa-ac97 +++ linux-2.4.21/drivers/sound/pxa-ac97.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -164,6 +165,11 @@ //pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); +#if CONFIG_ARCH_RAMSES + pxa_ac97_codec.supported_mixers = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; + pxa_ac97_codec.stereo_mixers = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; + pxa_ac97_codec.record_sources = SOUND_MASK_MIC | SOUND_MASK_LINE; +#endif } pxa_ac97_refcount++; @@ -198,7 +204,7 @@ static int mixer_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret, val; + int ret; ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); if (ret) @@ -282,6 +288,7 @@ /* fall through */ case SOUND_PCM_READ_RATE: + val = 0; if (file->f_mode & FMODE_READ) val = codec_adc_rate; if (file->f_mode & FMODE_WRITE) @@ -342,6 +349,44 @@ }; +#ifdef CONFIG_PM + +static int pxa_ac97_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + down(&pxa_ac97_mutex); + + switch (rqst) { + case PM_SUSPEND: + // TODO: set to low-power state? + GCR = GCR_ACLINK_OFF; + CKEN &= ~CKEN2_AC97; + break; + + case PM_RESUME: + CKEN |= CKEN2_AC97; + + GCR = 0; + udelay(10); + GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; + while (!(GSR & GSR_PCR)) { + schedule(); + } + + // need little hack for UCB1400 (should be moved elsewhere) + pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1); + pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); + pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); + break; + } + + up(&pxa_ac97_mutex); + + return 0; +} + +#endif + + static int __init pxa_ac97_init(void) { int ret; @@ -354,11 +399,18 @@ ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); +#ifdef PM_DEBUG + ac97_audio_state.pmdev = pm_register(PM_SYS_UNKNOWN, 0x71783937, pxa_ac97_pm_callback, "pxa-ac97"); +#else + ac97_audio_state.pmdev = pm_register(PM_SYS_UNKNOWN, 0x71783937, pxa_ac97_pm_callback); +#endif + return 0; } static void __exit pxa_ac97_exit(void) { + pm_unregister(ac97_audio_state.pmdev); unregister_sound_dsp(ac97_audio_state.dev_dsp); unregister_sound_mixer(pxa_ac97_codec.dev_mixer); pxa_ac97_put(); --- linux-2.4.21/drivers/sound/pxa-audio.h~pm +++ linux-2.4.21/drivers/sound/pxa-audio.h @@ -47,6 +47,9 @@ int wr_ref:1; /* open reference for playback */ int (*client_ioctl)(struct inode *, struct file *, uint, ulong); struct semaphore sem; /* prevent races in attach/release */ +#ifdef CONFIG_PM + struct pm_dev *pmdev; /* Power management */ +#endif } audio_state_t; extern int pxa_audio_attach(struct inode *inode, struct file *file, --- linux-2.4.21/drivers/usb/Config.in~pxa-usb +++ linux-2.4.21/drivers/usb/Config.in @@ -5,7 +5,7 @@ comment 'USB support' # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. -if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" ]; then +if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" -o "$CONFIG_ARCH_PXA" = "y" ]; then tristate 'Support for USB' CONFIG_USB else define_bool CONFIG_USB n --- linux-2.4.21/drivers/usb/Makefile~usb-sl811 +++ linux-2.4.21/drivers/usb/Makefile @@ -80,6 +80,9 @@ ifeq ($(CONFIG_USB_OHCI),y) obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o endif + +subdir-$(CONFIG_USB_SL811HS_ALT)+= host + subdir-$(CONFIG_USB_OHCI_AT91) += host ifeq ($(CONFIG_USB_OHCI_AT91),y) obj-y += host/usb-ohci.o --- linux-2.4.21/drivers/usb/hcd.c~ramses-usb +++ linux-2.4.21/drivers/usb/hcd.c @@ -662,7 +662,9 @@ pci_set_drvdata(dev, hcd); hcd->driver = driver; hcd->description = driver->description; +#ifdef TODO hcd->pdev = dev; +#endif printk (KERN_INFO "%s %s: %s\n", hcd->description, dev->slot_name, dev->name); @@ -1201,6 +1203,7 @@ return status; // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag +#ifdef TODO if (usb_pipecontrol (urb->pipe)) urb->setup_dma = pci_map_single ( #ifdef CONFIG_PCI @@ -1223,7 +1226,7 @@ usb_pipein (urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - +#endif if (urb->dev == hcd->bus->root_hub) status = rh_urb_enqueue (hcd, urb); else @@ -1488,6 +1491,7 @@ // hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev) // NOTE: 2.5 does this if !URB_NO_DMA_MAP transfer flag +#ifdef TODO if (usb_pipecontrol (urb->pipe)) pci_unmap_single ( #ifdef CONFIG_PCI @@ -1510,6 +1514,7 @@ usb_pipein (urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); +#endif /* pass ownership to the completion handler */ urb->complete (urb); --- linux-2.4.21/drivers/usb/hid-core.c~bluetooth +++ linux-2.4.21/drivers/usb/hid-core.c @@ -211,6 +211,8 @@ offset = report->size; report->size += parser->global.report_size * parser->global.report_count; + if (usages < parser->global.report_count) + usages = parser->global.report_count; if (usages == 0) return 0; /* ignore padding fields */ --- linux-2.4.21/drivers/usb/host/Config.in~usb-sl811 +++ linux-2.4.21/drivers/usb/host/Config.in @@ -13,6 +13,9 @@ fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB +if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" ]; then + dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL +fi if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then dep_tristate ' AT91RM9200 OHCI-compatible host interface support' CONFIG_USB_OHCI_AT91 $CONFIG_USB fi --- linux-2.4.21/drivers/usb/host/Makefile~usb-sl811 +++ linux-2.4.21/drivers/usb/host/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o +obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o obj-$(CONFIG_USB_OHCI_AT91) += usb-ohci.o # Extract lists of the multi-part drivers. --- /dev/null +++ linux-2.4.21/drivers/usb/host/sl811.c @@ -0,0 +1,2782 @@ +/* + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + * Adam Richter, Gregory P. Smith; + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua + * + * It's now support isochornous mode and more effective than hc_sl811.o + * Support x86 architecture now. + * + * 19.09.2003 (05.06.2003) HNE + * sl811_alloc_hc: Set "bus->bus_name" at init. + * sl811_reg_test (hc_reset,regTest): + * Stop output at first failed pattern. + * Down-Grade for Kernel 2.4.20 and from 2.4.22 + * Split hardware dependency into files sl811-x86.h and sl811-arm.h. + * + * 22.09.2003 HNE + * sl811_found_hc: First patterntest, than interrupt enable. + * Do nothing, if patterntest failed. Release IO if failed. + * Stop Interrupts first, than remove handle. (Old blocked Shared IRQ) + * Alternate IO-Base for second Controller (CF/USB1). + * + * 24.09.2003 HNE + * Remove all arm specific source (moved into include/asm/sl811-hw.h). + * + * 03.10.2003 HNE + * Low level only for port IO into hardware-include. + * + * To do: + * 1.Modify the timeout part, it's some messy + * 2.Use usb-a and usb-b set in Ping-Pong mode + * o Floppy do not work. + * o driver crash, if io region can't register + * o Only tested as module. Compiled-in version not tested! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../hcd.h" +#include "../hub.h" +#include "sl811.h" + +#define DRIVER_VERSION "v0.30" +#define MODNAME "SL811" +#define DRIVER_AUTHOR "Yin Aihua , Henry Nestler " +#define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver" + +static LIST_HEAD(sl811_hcd_list); + +/* + * 0: normal prompt and information + * 1: error should not occur in normal + * 2: error maybe occur in normal + * 3: useful and detail debug information + * 4: function level enter and level inforamtion + * 5: endless information will output because of timer function or interrupt + */ +static int debug = 0; +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"debug level"); + +#include /* Include hardware and board depens */ + +static void sl811_rh_int_timer_do(unsigned long ptr); +static void sl811_transfer_done(struct sl811_hc *hc, int sof); + +/* + * Read a byte of data from the SL811H/SL11H + */ +static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset) +{ + sl811_write_index (hc, offset); + return (sl811_read_data (hc)); +} + +/* + * Write a byte of data to the SL811H/SL11H + */ +static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) +{ + sl811_write_index_data (hc, offset, data); +} + +/* + * Read consecutive bytes of data from the SL811H/SL11H buffer + */ +static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +{ + sl811_write_index (hc, offset); + while (size--) { + *buf++ = sl811_read_data(hc); + } +} + +/* + * Write consecutive bytes of data to the SL811H/SL11H buffer + */ +static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +{ + sl811_write_index (hc, offset); + while (size--) { + sl811_write_data (hc, *buf); + buf++; + } +} + +/* + * This routine test the Read/Write functionality of SL811HS registers + */ +static int sl811_reg_test(struct sl811_hc *hc) +{ + int i, data, result = 0; + __u8 buf[256]; + + for (i = 0x10; i < 256; i++) { + /* save the original buffer */ + buf[i] = sl811_read(hc, i); + + /* Write the new data to the buffer */ + sl811_write(hc, i, ~i); + } + + /* compare the written data */ + for (i = 0x10; i < 256; i++) { + data = sl811_read(hc, i); + if (data != (__u8) ~i) { + PDEBUG(1, "reg %02x expected %02x got %02x", i, (__u8) ~i, data); + result = -1; + + /* If no Debug, show only first failed Address */ + if (!debug) + break; + } + } + + /* restore the data */ + for (i = 0x10; i < 256; i++) + sl811_write(hc, i, buf[i]); + + return result; +} + +/* + * Display all SL811HS register values + */ +#if 0 /* unused (hne) */ +static void sl811_reg_show(struct sl811_hc *hc) +{ + int i; + + for (i = 0; i < 256; i++) + PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i)); +} +#endif + +/* + * This function enables SL811HS interrupts + */ +static void sl811_enable_interrupt(struct sl811_hc *hc) +{ + PDEBUG(4, "enter"); + sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV); +} + +/* + * This function disables SL811HS interrupts + */ +static void sl811_disable_interrupt(struct sl811_hc *hc) +{ + PDEBUG(4, "enter"); + // Disable all other interrupt except for insert/remove. + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); +} + +/* + * SL811 Virtual Root Hub + */ + +/* Device descriptor */ +static __u8 sl811_rh_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 sl811_rh_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* root hub class descriptor*/ +static __u8 sl811_rh_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x01, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x50, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/* + * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT. + */ +static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len) +{ + __u8 data = 0; + + PDEBUG(5, "enter"); + + /* + * Right now, It is assume the power is good and no changes and only one port. + */ + if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) { + data = 1<<1; + *(__u8 *)rh_change = data; + return 1; + } else + return 0; +} + +/* + * This function creates a timer that act as interrupt pipe in the virtual hub. + * + * Note: The virtual root hub's interrupt pipe are polled by the timer + * every "interval" ms + */ +static void sl811_rh_init_int_timer(struct urb * urb) +{ + struct sl811_hc *hc = urb->dev->bus->hcpriv; + hc->rh.interval = urb->interval; + + init_timer(&hc->rh.rh_int_timer); + hc->rh.rh_int_timer.function = sl811_rh_int_timer_do; + hc->rh.rh_int_timer.data = (unsigned long)urb; + hc->rh.rh_int_timer.expires = jiffies + + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; + add_timer (&hc->rh.rh_int_timer); +} + +/* + * This function is called when the timer expires. It gets the the port + * change data and pass along to the upper protocol. + */ +static void sl811_rh_int_timer_do(unsigned long ptr) +{ + int len; + struct urb *urb = (struct urb *)ptr; + struct sl811_hc *hc = urb->dev->bus->hcpriv; + PDEBUG (5, "enter"); + + if(hc->rh.send) { + len = sl811_rh_send_irq(hc, urb->transfer_buffer, + urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete(urb); + } + } + +#ifdef SL811_TIMEOUT + +{ + struct list_head *head, *tmp; + struct sl811_urb_priv *urbp; + struct urb *u; + int i; + static int timeout_count = 0; + +// check time out every second + if (++timeout_count > 4) { + int max_scan = hc->active_urbs; + timeout_count = 0; + for (i = 0; i < 6; ++i) { + head = &hc->urb_list[i]; + tmp = head->next; + while (tmp != head && max_scan--) { + u = list_entry(tmp, struct urb, urb_list); + urbp = (struct sl811_urb_priv *)u->hcpriv; + tmp = tmp->next; + // Check if the URB timed out + if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) { + PDEBUG(3, "urb = %p time out, we kill it", urb); + u->transfer_flags |= USB_TIMEOUT_KILLED; + } + } + } + } +} + +#endif + // re-activate the timer + sl811_rh_init_int_timer(urb); +} + +/* helper macro */ +#define OK(x) len = (x); break + +/* + * This function handles all USB request to the the virtual root hub + */ +static int sl811_rh_submit_urb(struct urb *urb) +{ + struct usb_device *usb_dev = urb->dev; + struct sl811_hc *hc = usb_dev->bus->hcpriv; + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet; + void *data = urb->transfer_buffer; + int buf_len = urb->transfer_buffer_length; + unsigned int pipe = urb->pipe; + __u8 data_buf[16]; + __u8 *bufp = data_buf; + int len = 0; + int status = 0; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipeint(pipe)) { + hc->rh.urb = urb; + hc->rh.send = 1; + hc->rh.interval = urb->interval; + sl811_rh_init_int_timer(urb); + urb->status = 0; + + return 0; + } + + bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); + wValue = le16_to_cpu (cmd->wValue); + wIndex = le16_to_cpu (cmd->wIndex); + wLength = le16_to_cpu (cmd->wLength); + + PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength); + + /* Request Destination: + without flags: Device, + USB_RECIP_INTERFACE: interface, + USB_RECIP_ENDPOINT: endpoint, + USB_TYPE_CLASS means HUB here, + USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here + */ + switch (bmRType_bReq) { + case RH_GET_STATUS: + *(__u16 *)bufp = cpu_to_le16(1); + OK(2); + + case RH_GET_STATUS | USB_RECIP_INTERFACE: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_RECIP_ENDPOINT: + *(__u16 *)bufp = cpu_to_le16(0); + OK(2); + + case RH_GET_STATUS | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(0); + OK(4); + + case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: + *(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus); + OK(4); + + case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: + switch (wValue) { + case 1: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_TYPE_CLASS: + switch (wValue) { + case C_HUB_LOCAL_POWER: + OK(0); + + case C_HUB_OVER_CURRENT: + OK(0); + } + break; + + case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_SUSPEND: + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_POWER: + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_C_CONNECTION: + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + OK(0); + + case USB_PORT_FEAT_C_ENABLE: + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; + OK(0); + + case USB_PORT_FEAT_C_SUSPEND: + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; + OK(0); + + case USB_PORT_FEAT_C_OVER_CURRENT: + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; + OK(0); + + case USB_PORT_FEAT_C_RESET: + hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; + OK(0); + } + break; + + case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; + OK(0); + + case USB_PORT_FEAT_RESET: + hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET; + hc->rh_status.wPortChange = 0; + hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET; + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; + hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + + case USB_PORT_FEAT_POWER: + hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER; + OK(0); + + case USB_PORT_FEAT_ENABLE: + hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; + OK(0); + } + break; + + case RH_SET_ADDRESS: + hc->rh.devnum = wValue; + OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case USB_DT_DEVICE: + len = sizeof(sl811_rh_dev_des); + bufp = sl811_rh_dev_des; + OK(len); + + case USB_DT_CONFIG: + len = sizeof(sl811_rh_config_des); + bufp = sl811_rh_config_des; + OK(len); + + case USB_DT_STRING: + len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength); + if (len > 0) { + bufp = data; + OK(len); + } + + default: + status = -EPIPE; + } + break; + + case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: + len = sizeof(sl811_rh_hub_des); + bufp = sl811_rh_hub_des; + OK(len); + + case RH_GET_CONFIGURATION: + bufp[0] = 0x01; + OK(1); + + case RH_SET_CONFIGURATION: + OK(0); + + default: + PDEBUG(1, "unsupported root hub command"); + status = -EPIPE; + } + + len = min(len, buf_len); + if (data != bufp) + memcpy(data, bufp, len); + urb->actual_length = len; + urb->status = status; + + PDEBUG(5, "len = %d, status = %d", len, status); + + urb->hcpriv = NULL; + urb->dev = NULL; + if (urb->complete) + urb->complete(urb); + + return 0; +} + +/* + * This function unlinks the URB + */ +static int sl811_rh_unlink_urb(struct urb *urb) +{ + struct sl811_hc *hc = urb->dev->bus->hcpriv; + + PDEBUG(5, "enter"); + + if (hc->rh.urb == urb) { + hc->rh.send = 0; + del_timer(&hc->rh.rh_int_timer); + hc->rh.urb = NULL; + urb->hcpriv = NULL; + usb_dec_dev_use(urb->dev); + urb->dev = NULL; + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNRESET; + if (urb->complete) + urb->complete(urb); + } else + urb->status = -ENOENT; + } + + return 0; +} + +/* + * This function connect the virtual root hub to the USB stack + */ +static int sl811_connect_rh(struct sl811_hc * hc) +{ + struct usb_device *usb_dev; + + hc->rh.devnum = 0; + usb_dev = usb_alloc_dev(NULL, hc->bus); + if (!usb_dev) + return -ENOMEM; + + hc->bus->root_hub = usb_dev; + usb_connect(usb_dev); + + if (usb_new_device(usb_dev)) { + usb_free_dev(usb_dev); + return -ENODEV; + } + + PDEBUG(5, "leave success"); + + return 0; +} + +/* + * This function allocates private data space for the usb device + */ +static int sl811_alloc_dev_priv(struct usb_device *usb_dev) +{ + return 0; +} + +/* + * This function de-allocates private data space for the usb devic + */ +static int sl811_free_dev_priv (struct usb_device *usb_dev) +{ + return 0; +} + +/* + * This function allocates private data space for the urb + */ +static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb) +{ + struct sl811_urb_priv *urbp; + + urbp = kmalloc(sizeof(*urbp), GFP_KERNEL); + if (!urbp) + return NULL; + + memset(urbp, 0, sizeof(*urbp)); + + INIT_LIST_HEAD(&urbp->td_list); + + urbp->urb = urb; + urb->hcpriv = urbp; + + return urbp; +} + +/* + * This function free private data space for the urb + */ +static void sl811_free_urb_priv(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td; + struct list_head *head, *tmp; + + if (!urbp) + return ; + + head = &urbp->td_list; + tmp = head->next; + + while (tmp != head) { + td = list_entry(tmp, struct sl811_td, td_list); + tmp = tmp->next; + kfree(td); + } + + kfree(urbp); + urb->hcpriv = NULL; + + return ; +} + +/* + * This function calculate the bus time need by this td. + * Fix me! Can this use usb_calc_bus_time()? + */ +static void sl811_calc_td_time(struct sl811_td *td) +{ +#if 1 + int time; + int len = td->len; + struct sl811_hc *hc = td->urb->dev->bus->hcpriv; + + if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) + time = 8*8*len + 1024; + else { + if (td->ctrl & SL811_USB_CTRL_PREAMBLE) + time = 8*8*len + 2048; + else + time = 8*len + 256; + } + + time += 2*10 * len; + + td->bustime = time; + +#else + + unsigned long tmp; + int time; + int low_speed = usb_pipeslow(td->urb->pipe); + int input_dir = usb_pipein(td->urb->pipe); + int bytecount = td->len; + int isoc = usb_pipeisoc(td->urb->pipe); + + if (low_speed) { /* no isoc. here */ + if (input_dir) { + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; + time = (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } else { + tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; + time = (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + } else if (!isoc){ /* for full-speed: */ + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + time = (9107L + BW_HOST_DELAY + tmp); + } else { /* for isoc: */ + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + time = (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); + } + + td->bustime = time / 84; + +#endif +} + +/* + * This function calculate the remainder bus time in current frame. + */ +static inline int sl811_calc_bus_remainder(struct sl811_hc *hc) +{ + return (sl811_read(hc, SL811_SOFCNTDIV) * 64); +} + +/* + * This function allocates td for the urb + */ +static struct sl811_td* sl811_alloc_td(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td; + + td = kmalloc(sizeof (*td), GFP_KERNEL); + if (!td) + return NULL; + + memset(td, 0, sizeof(*td)); + + INIT_LIST_HEAD(&td->td_list); + + td->urb = urb; + list_add_tail(&td->td_list, &urbp->td_list); + + return td; +} + +/* + * Fill the td. + */ +static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, __u8 pidep, __u8 dev, __u8 *buf) +{ + td->ctrl = ctrl; + td->addr = addr; + td->len = len; + td->pidep = pidep; + td->dev = dev; + td->buf = buf; + td->left = len; + td->errcnt = 3; +} + +/* + * Fill the td. + */ +static inline void sl811_reset_td(struct sl811_td *td) +{ + td->status = 0; + td->left = td->len; + td->done = 0; + td->errcnt = 3; + td->nakcnt = 0; + td->td_status = 0; +} + +static void sl811_print_td(int level, struct sl811_td *td) +{ + PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n " + "dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n " + "buf = %p, bustime = %d, td_status = %d\n", + td, td->ctrl, td->addr, td->len, td->pidep, + td->dev, td->status, td->left, td->errcnt, td->done, + td->buf, td->bustime, td->td_status); +} + +/* + * Isochronous transfers + */ +static int sl811_submit_isochronous(struct urb *urb) +{ + __u8 dev = usb_pipedevice(urb->pipe); + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); + __u8 ctrl = 0; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + int i; + + PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp); + + /* Can't have low speed bulk transfers */ + if (usb_pipeslow(urb->pipe)) { + PDEBUG(1, "error, urb = %p, low speed device", urb); + return -EINVAL; + } + + if (usb_pipeout(urb->pipe)) + ctrl |= SL811_USB_CTRL_DIR_OUT; + + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO; + + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + sl811_fill_td(td, ctrl, SL811_DATA_START, + urb->iso_frame_desc[i].length, + pidep, dev, + urb->transfer_buffer + urb->iso_frame_desc[i].offset); + sl811_calc_td_time(td); + if (urbp->cur_td == NULL) + urbp->cur_td = urbp->first_td = td; + } + + urbp->last_td = td; + + PDEBUG(4, "leave success"); + +/* +// for debug + { + struct list_head *head, *tmp; + struct sl811_td *td; + int i = 0; + head = &urbp->td_list; + tmp = head->next; + + if (list_empty(&urbp->td_list)) { + PDEBUG(1, "bug!!! td list is empty!"); + return -ENODEV; + } + + while (tmp != head) { + ++i; + td = list_entry(tmp, struct sl811_td, td_list); + PDEBUG(2, "td = %p, i = %d", td, i); + tmp = tmp->next; + } + } +*/ + return 0; +} + +/* + * Reset isochronous transfers + */ +static void sl811_reset_isochronous(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + struct list_head *head, *tmp; + int i; + + PDEBUG(4, "enter, urb = %p", urb); + + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct sl811_td, td_list); + tmp = tmp->next; + sl811_reset_td(td); + } + + urbp->cur_td = urbp->first_td; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; +} + +/* + * Result the iso urb. + */ +static void sl811_result_isochronous(struct urb *urb) +{ + struct list_head *tmp, *head; + struct sl811_urb_priv *urbp = urb->hcpriv; + int status = 0; + struct sl811_td *td; + int i; + + PDEBUG(4, "enter, urb = %p", urb); + + urb->actual_length = 0; + + i = 0; + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct sl811_td, td_list); + tmp = tmp->next; + + if (!td->done) { + if (urbp->unlink) + urb->status = -ENOENT; + else { + PDEBUG(1, "we should not get here!"); + urb->status = -EXDEV; + } + return ; + } + if (td->td_status) { + status = td->td_status; + urb->error_count++; + PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status); + } + + urb->iso_frame_desc[i].actual_length = td->len - td->left; + urb->actual_length += td->len - td->left; + urb->iso_frame_desc[i].status = td->td_status; + ++i; + if (td->left) + PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, td->len, td->left); + } + + urb->status = status; +/* +// for debug + PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, urb->status); +*/ + PDEBUG(4, "leave success"); +} + +/* + * Interrupt transfers + */ +static int sl811_submit_interrupt(struct urb *urb) +{ + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + __u8 *data = urb->transfer_buffer; + __u8 dev = usb_pipedevice(urb->pipe); + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); + __u8 ctrl = 0; + struct sl811_hc *hc = urb->dev->bus->hcpriv; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + + PDEBUG(4, "enter, urb = %p", urb); + + if (len > maxsze) { + PDEBUG(1, "length is big than max packet size, len = %d, max packet = %d", len, maxsze); + return -EINVAL; + } + if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) + ctrl |= SL811_USB_CTRL_PREAMBLE; + + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data); + sl811_calc_td_time(td); + urbp->cur_td = urbp->first_td = urbp->last_td = td; + urbp->interval = 0; + + PDEBUG(4, "leave success"); + + return 0; +} + +/* + * Reset interrupt transfers + */ +static void sl811_reset_interrupt(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = urbp->cur_td; + + PDEBUG(4, "enter, interval = %d", urb->interval); + + td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) + td->ctrl |= SL811_USB_CTRL_TOGGLE_1; + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + sl811_reset_td(td); + + urbp->interval = urb->interval; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; +} + +/* + * Result the interrupt urb. + */ +static void sl811_result_interrupt(struct urb *urb) +{ + struct list_head *tmp; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td; + int toggle; + + PDEBUG(4, "enter, urb = %p", urb); + + urb->actual_length = 0; + + tmp = &urbp->td_list; + tmp = tmp->next; + td = list_entry(tmp, struct sl811_td, td_list); + + // success. + if (td->done && td->td_status == 0) { + urb->actual_length += td->len - td->left; + urb->status = 0; + return ; + } + // tranfer is done but fail, reset the toggle. + else if (td->done && td->td_status) { + urb->status = td->td_status; +reset_toggle: + toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); + PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); + return ; + } + // unlink, and not do transfer yet + else if (td->done == 0 && urbp->unlink && td->td_status == 0) { + urb->status = -ENOENT; + PDEBUG(3, "unlink and not transfer!"); + return ; + } + // unlink, and transfer not complete yet. + else if (td->done == 0 && urbp->unlink && td->td_status) { + urb->status = -ENOENT; + PDEBUG(3, "unlink and not complete!"); + goto reset_toggle; + } + // must be bug!!! + else {// (td->done == 0 && urbp->unlink == 0) + PDEBUG(1, "we should not get here!"); + urb->status = -EPIPE; + return ; + } +} + +/* + * Control transfers + */ +static int sl811_submit_control(struct urb *urb) +{ + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + __u8 *data = urb->transfer_buffer; + __u8 dev = usb_pipedevice(urb->pipe); + __u8 pidep = 0; + __u8 ctrl = 0; + struct sl811_hc *hc = urb->dev->bus->hcpriv; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + + PDEBUG(4, "enter, urb = %p", urb); + + if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)) + ctrl |= SL811_USB_CTRL_PREAMBLE; + + /* Build SETUP TD */ + pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe)); + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT; + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet); + sl811_calc_td_time(td); + + urbp->cur_td = urbp->first_td = td; + + /* + * If direction is "send", change the frame from SETUP (0x2D) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). + */ + pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); + if (usb_pipeout(urb->pipe)) + ctrl |= SL811_USB_CTRL_DIR_OUT; + else + ctrl &= ~SL811_USB_CTRL_DIR_OUT; + + /* Build the DATA TD's */ + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + /* Alternate Data0/1 (start with Data1) */ + ctrl ^= SL811_USB_CTRL_TOGGLE_1; + + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); + sl811_calc_td_time(td); + + data += pktsze; + len -= pktsze; + } + + /* Build the final TD for control status */ + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + /* It's IN if the pipe is an output pipe or we're not expecting data back */ + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) { + pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe)); + ctrl &= ~SL811_USB_CTRL_DIR_OUT; + } else { + pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe)); + ctrl |= SL811_USB_CTRL_DIR_OUT; + } + + /* End in Data1 */ + ctrl |= SL811_USB_CTRL_TOGGLE_1; + + sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); + sl811_calc_td_time(td); + urbp->last_td = td; +/* +// for debug + { + struct list_head *head, *tmp; + struct sl811_td *td; + int i = 0; + head = &urbp->td_list; + tmp = head->next; + + if (list_empty(&urbp->td_list)) { + PDEBUG(1, "bug!!! td list is empty!"); + return -ENODEV; + } + + while (tmp != head) { + ++i; + td = list_entry(tmp, struct sl811_td, td_list); + PDEBUG(3, "td = %p, i = %d", td, i); + tmp = tmp->next; + } + } +*/ + PDEBUG(4, "leave success"); + + return 0; +} + +/* + * Result the control urb. + */ +static void sl811_result_control(struct urb *urb) +{ + struct list_head *tmp, *head; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td; + + PDEBUG(4, "enter, urb = %p", urb); + + if (list_empty(&urbp->td_list)) { + PDEBUG(1, "td list is empty"); + return ; + } + + head = &urbp->td_list; + + tmp = head->next; + td = list_entry(tmp, struct sl811_td, td_list); + + /* The first TD is the SETUP phase, check the status, but skip the count */ + if (!td->done) { + PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done); + goto err_done; + } + if (td->td_status) { + PDEBUG(3, "setup phase error, td = %p, td status = %d", td, td->td_status); + goto err_status; + } + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + tmp = tmp->next; + while (tmp != head && tmp->next != head) { + td = list_entry(tmp, struct sl811_td, td_list); + tmp = tmp->next; + if (!td->done) { + PDEBUG(3, "data phase error, td = %p, done = %d", td, td->done); + goto err_done; + } + if (td->td_status) { + PDEBUG(3, "data phase error, td = %p, td status = %d", td, td->td_status); + goto err_status; + } + + urb->actual_length += td->len - td->left; + // short packet. + if (td->left) { + PDEBUG(3, "data phase short packet, td = %p, count = %d", td, td->len - td->left); + break; + } + } + + /* The last td is status phase */ + td = urbp->last_td; + if (!td->done) { + PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done); + goto err_done; + } + if (td->td_status) { + PDEBUG(3, "status phase error, td = %p, td status = %d", td, td->td_status); + goto err_status; + } + + PDEBUG(4, "leave success"); + + urb->status = 0; + return ; + +err_done: + if (urbp->unlink) + urb->status = -ENOENT; + else { + PDEBUG(1, "we should not get here! td = %p", td); + urb->status = -EPIPE; + } + return ; + +err_status: + urb->status = td->td_status; + return ; +} + +/* + * Bulk transfers + */ +static int sl811_submit_bulk(struct urb *urb) +{ + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + __u8 *data = urb->transfer_buffer; + __u8 dev = usb_pipedevice(urb->pipe); + __u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe)); + __u8 ctrl = 0; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + + PDEBUG(4, "enter, urb = %p", urb); + + if (len < 0) { + PDEBUG(1, "error, urb = %p, len = %d", urb, len); + return -EINVAL; + } + + /* Can't have low speed bulk transfers */ + if (usb_pipeslow(urb->pipe)) { + PDEBUG(1, "error, urb = %p, low speed device", urb); + return -EINVAL; + } + + if (usb_pipeout(urb->pipe)) + ctrl |= SL811_USB_CTRL_DIR_OUT; + + ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; + + /* Build the DATA TD's */ + do { /* Allow zero length packets */ + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + /* Alternate Data0/1 (start with Data1) */ + ctrl &= ~SL811_USB_CTRL_TOGGLE_1; + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data); + sl811_calc_td_time(td); + + if (urbp->cur_td == NULL) + urbp->cur_td = urbp->first_td = td; + + data += pktsze; + len -= maxsze; + } while (len > 0); + + /* + * USB_ZERO_PACKET means adding a 0-length packet, if + * direction is OUT and the transfer_length was an + * exact multiple of maxsze, hence + * (len = transfer_length - N * maxsze) == 0 + * however, if transfer_length == 0, the zero packet + * was already prepared above. + */ + if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) && + !len && urb->transfer_buffer_length) { + + td = sl811_alloc_td(urb); + if (!td) + return -ENOMEM; + + /* Alternate Data0/1 (start with Data1) */ + ctrl &= ~SL811_USB_CTRL_TOGGLE_1; + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) + ctrl |= SL811_USB_CTRL_TOGGLE_1; + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0); + sl811_calc_td_time(td); + } + + urbp->last_td = td; + + PDEBUG(4, "leave success"); + + return 0; +} + +/* + * Reset bulk transfers + */ +static int sl811_reset_bulk(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td; + struct list_head *head, *tmp; + + PDEBUG(4, "enter, urb = %p", urb); + + + head = &urbp->td_list; + tmp = head->next; + + while (tmp != head) { + td = list_entry(tmp, struct sl811_td, td_list); + + /* Alternate Data0/1 (start with Data1) */ + td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1; + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) + td->ctrl |= SL811_USB_CTRL_TOGGLE_1; + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + sl811_reset_td(td); + } + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urbp->cur_td = urbp->first_td; + + PDEBUG(4, "leave success"); + + return 0; +} + +/* + * Result the bulk urb. + */ +static void sl811_result_bulk(struct urb *urb) +{ + struct list_head *tmp, *head; + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = NULL; + int toggle; + + PDEBUG(4, "enter, urb = %p", urb); + + urb->actual_length = 0; + + head = &urbp->td_list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct sl811_td, td_list); + tmp = tmp->next; + + // success. + if (td->done && td->td_status == 0) { + urb->actual_length += td->len - td->left; + + // short packet + if (td->left) { + urb->status = 0; + PDEBUG(3, "short packet, td = %p, count = %d", td, td->len - td->left); + goto reset_toggle; + } + } + // tranfer is done but fail, reset the toggle. + else if (td->done && td->td_status) { + urb->status = td->td_status; + PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status); + goto reset_toggle; + } + // unlink, and not do transfer yet + else if (td->done == 0 && urbp->unlink && td->td_status == 0) { + urb->status = -ENOENT; + PDEBUG(3, "unlink and not transfer!"); + return ; + } + // unlink, and transfer not complete yet. + else if (td->done == 0 && urbp->unlink && td->td_status) { + PDEBUG(3, "unlink and not complete!"); + urb->status = -ENOENT; + goto reset_toggle; + } + // must be bug!!! + else {// (td->done == 0 && urbp->unlink == 0) + urb->status = -EPIPE; + PDEBUG(1, "we should not get here!"); + return ; + } + } + + PDEBUG(4, "leave success"); + urb->status = 0; + return ; + +reset_toggle: + toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); +} + +/* + * Find the first urb have the same dev and endpoint. + */ +static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb) +{ + struct list_head *tmp; + struct urb *u; + + if (!head || !urb) + return 0; + + tmp = head->next; + + while (tmp != head) { + u = list_entry(tmp, struct urb, urb_list); + if (u == urb) + return 1; + tmp = tmp->next; + } + + return 0; +} + +/* + * Find the first urb have the same dev and endpoint. + */ +static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb *urb) +{ + struct list_head *tmp; + struct urb *u; + + if (!head || !urb) + return NULL; + + tmp = head->next; + + while (tmp != head) { + u = list_entry(tmp, struct urb, urb_list); + if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe))) + return u; + tmp = tmp->next; + } + + return NULL; +} + +/* + * This function is called by the USB core API when an URB is available to + * process. + */ +static int sl811_submit_urb(struct urb *urb) +{ + struct sl811_hc *hc = urb->dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + struct list_head *head = NULL; + unsigned long flags; + int bustime; + int ret = 0; + + if (!urb) { + PDEBUG(1, "urb is null"); + return -EINVAL; + } + + if (urb->hcpriv) { + PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv); + return -EINVAL; + } + + if (!urb->dev || !urb->dev->bus || !hc) { + PDEBUG(1, "dev or bus or hc is null"); + return -ENODEV; + } + + if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + PDEBUG(2, "sl811_submit_urb: endpoint_halted"); + return -EPIPE; + } + + if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > SL811_DATA_LIMIT) { + printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", SL811_DATA_LIMIT); + return -EINVAL; + } + + /* a request to the virtual root hub */ + if (usb_pipedevice(pipe) == hc->rh.devnum) + return sl811_rh_submit_urb(urb); + + spin_lock_irqsave(&hc->hc_lock, flags); + spin_lock(&urb->lock); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + head = &hc->iso_list; + break; + case PIPE_INTERRUPT: + head = &hc->intr_list; + break; + case PIPE_CONTROL: + head = &hc->ctrl_list; + break; + case PIPE_BULK: + head = &hc->bulk_list; + break; + } + + if (sl811_find_same_devep(head, urb)) { + list_add(&urb->urb_list, &hc->wait_list); + PDEBUG(4, "add to wait list"); + goto out_unlock; + } + + if (!sl811_alloc_urb_priv(urb)) { + ret = -ENOMEM; + goto out_unlock; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + if (urb->number_of_packets <= 0) { + ret = -EINVAL; + break; + } + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + break; + } + if (!(ret = sl811_submit_isochronous(urb))) + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + break; + case PIPE_INTERRUPT: + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) + ret = bustime; + else if (!(ret = sl811_submit_interrupt(urb))) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + break; + case PIPE_CONTROL: + ret = sl811_submit_control(urb); + break; + case PIPE_BULK: + ret = sl811_submit_bulk(urb); + break; + } + + if (!ret) { + ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; + list_add(&urb->urb_list, head); + PDEBUG(4, "add to type list"); + urb->status = -EINPROGRESS; + if (++hc->active_urbs == 1) + sl811_enable_interrupt(hc); + goto out_unlock; + } else { + PDEBUG(2, "submit urb fail! error = %d", ret); + sl811_free_urb_priv(urb); + } + +out_unlock: + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&hc->hc_lock, flags); + + return ret; +} + +/* + * Submit the urb the wait list. + */ +static int sl811_submit_urb_with_lock(struct urb *urb) +{ + struct sl811_hc *hc = urb->dev->bus->hcpriv; + struct list_head *head = NULL; + int bustime; + int ret = 0; + + spin_lock(&urb->lock); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + head = &hc->iso_list; + break; + case PIPE_INTERRUPT: + head = &hc->intr_list; + break; + case PIPE_CONTROL: + head = &hc->ctrl_list; + break; + case PIPE_BULK: + head = &hc->bulk_list; + break; + } + + if (!sl811_alloc_urb_priv(urb)) { + ret = -ENOMEM; + goto out_unlock; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + if (urb->number_of_packets <= 0) { + ret = -EINVAL; + break; + } + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + break; + } + if (!(ret = sl811_submit_isochronous(urb))) + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + break; + case PIPE_INTERRUPT: + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) + ret = bustime; + else if (!(ret = sl811_submit_interrupt(urb))) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + break; + case PIPE_CONTROL: + ret = sl811_submit_control(urb); + break; + case PIPE_BULK: + ret = sl811_submit_bulk(urb); + break; + } + + if (ret == 0) { + ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; + list_add(&urb->urb_list, head); + PDEBUG(4, "add to type list"); + urb->status = -EINPROGRESS; + if (++hc->active_urbs == 1) + sl811_enable_interrupt(hc); + goto out_unlock; + } else { + PDEBUG(2, "submit urb fail! error = %d", ret); + sl811_free_urb_priv(urb); + } + +out_unlock: + spin_unlock(&urb->lock); + + return ret; +} + +/* + * Reset the urb + */ +static void sl811_reset_urb(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + sl811_reset_isochronous(urb); + break; + case PIPE_INTERRUPT: + sl811_reset_interrupt(urb); + break; + case PIPE_CONTROL: + return; + case PIPE_BULK: + sl811_reset_bulk(urb); + break; + } + urbp->inserttime = jiffies; +} + +/* + * Return the result of a transfer + */ +static void sl811_result_urb(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_hc *hc = urb->dev->bus->hcpriv; + struct list_head *head = NULL; + struct urb *u = NULL; + int reset = 0; + int ring = 0; + + if (urb->status != -EINPROGRESS) { + PDEBUG(1, "urb status is not EINPROGRESS!"); + return ; + } + + spin_lock(&urb->lock); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + head = &hc->iso_list; + sl811_result_isochronous(urb); + + // if the urb is not unlink and is in a urb "ring", we reset it + if (!urbp->unlink && urb->next) + ring = 1; + break; + case PIPE_INTERRUPT: + head = &hc->intr_list; + sl811_result_interrupt(urb); + + // if the urb is not unlink and not "once" query, we reset. + if (!urbp->unlink && urb->interval) + reset = 1; + break; + case PIPE_CONTROL: + head = &hc->ctrl_list; + sl811_result_control(urb); + break; + case PIPE_BULK: + head = &hc->bulk_list; + sl811_result_bulk(urb); + + // if the urb is not unlink and is in a urb "ring", we reset it + if (!urbp->unlink && urb->next) + ring = 1; + break; + } + + PDEBUG(4, "result urb status = %d", urb->status); + + if (ring && urb->next == urb) + reset = 1; + + if (!reset) { + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + usb_release_bandwidth(urb->dev, urb, 1); + break; + case PIPE_INTERRUPT: + usb_release_bandwidth(urb->dev, urb, 0); + break; + } + sl811_free_urb_priv(urb); + } + + spin_unlock(&urb->lock); + + if (urb->complete) + urb->complete(urb); + + if (reset) { + spin_lock(&urb->lock); + sl811_reset_urb(urb); + if (usb_pipeint(urb->pipe)) + list_add(&urb->urb_list, &hc->idle_intr_list); + else + list_add(&urb->urb_list, head); + spin_unlock(&urb->lock); + } else { + if (--hc->active_urbs <= 0) { + hc->active_urbs = 0; + sl811_disable_interrupt(hc); + } + + if (ring) + u = urb->next; + else + u = sl811_find_same_devep(&hc->wait_list, urb); + + if (u) { + if (!list_empty(&u->urb_list)) + list_del(&u->urb_list); + if (sl811_submit_urb_with_lock(u)) + list_add(&u->urb_list, &hc->wait_list); + } + } +} + + +#ifdef SL811_TIMEOUT + +/* + * Unlink the urb from the urb list + */ +static int sl811_unlink_urb(struct urb *urb) +{ + unsigned long flags; + struct sl811_hc *hc; + struct sl811_urb_priv *urbp; + int call = 0; + int schedule = 0; + int count = 0; + + if (!urb) { + PDEBUG(1, "urb is null"); + return -EINVAL; + } + + if (!urb->dev || !urb->dev->bus) { + PDEBUG(1, "dev or bus is null"); + return -ENODEV; + } + + hc = urb->dev->bus->hcpriv; + urbp = urb->hcpriv; + + /* a request to the virtual root hub */ + if (usb_pipedevice(urb->pipe) == hc->rh.devnum) + return sl811_rh_unlink_urb(urb); + + spin_lock_irqsave(&hc->hc_lock, flags); + spin_lock(&urb->lock); + + // in wait list + if (sl811_find_same_urb(&hc->wait_list, urb)) { + PDEBUG(4, "unlink urb in wait list"); + list_del_init(&urb->urb_list); + urb->status = -ENOENT; + call = 1; + goto out; + } + + // in intr idle list. + if (sl811_find_same_urb(&hc->idle_intr_list, urb)) { + PDEBUG(4, "unlink urb in idle intr list"); + list_del_init(&urb->urb_list); + urb->status = -ENOENT; + sl811_free_urb_priv(urb); + usb_release_bandwidth(urb->dev, urb, 0); + if (--hc->active_urbs <= 0) { + hc->active_urbs = 0; + sl811_disable_interrupt(hc); + } + call = 1; + goto out; + } + + if (urb->status == -EINPROGRESS) { + PDEBUG(3, "urb is still in progress"); + urbp->unlink = 1; + +re_unlink: + // Is it in progress? + urbp = urb->hcpriv; + if (urbp && hc->cur_td == urbp->cur_td) { + ++count; + if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { + PDEBUG(3, "unlink: cur td is still in progress! count = %d", count); +re_schedule: + schedule = 1; + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&hc->hc_lock, flags); + schedule_timeout(HZ/50); + spin_lock_irqsave(&hc->hc_lock, flags); + spin_lock(&urb->lock); + } else { + PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count); + spin_unlock(&urb->lock); + sl811_transfer_done(hc, 0); + spin_lock(&urb->lock); + } + goto re_unlink; + } + + if (list_empty(&urb->urb_list)) { + PDEBUG(3, "unlink: list empty!"); + goto out; + } + + if (urb->transfer_flags & USB_TIMEOUT_KILLED) { + PDEBUG(3, "unlink: time out killed"); + // it is timeout killed by us + goto result; + } else if (urb->transfer_flags & USB_ASYNC_UNLINK) { + // we do nothing, just let it be processing later + PDEBUG(3, "unlink async, do nothing"); + goto out; + } else { + // synchron without callback + PDEBUG(3, "unlink synchron, we wait the urb complete or timeout"); + if (schedule == 0) { + PDEBUG(3, "goto re_schedule"); + goto re_schedule; + } else { + PDEBUG(3, "already scheduled"); + goto result; + } + } + } else if (!list_empty(&urb->urb_list)) { + PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); + //list_del_init(&urb->urb_list); + //call = 1; + } + +out: + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&hc->hc_lock, flags); + + if (call && urb->complete) + urb->complete(urb); + + return 0; + +result: + spin_unlock(&urb->lock); + + list_del_init(&urb->urb_list); + sl811_result_urb(urb); + + spin_unlock_irqrestore(&hc->hc_lock, flags); + + return 0; +} + +#else + +/* + * Unlink the urb from the urb list + */ +static int sl811_unlink_urb(struct urb *urb) +{ + unsigned long flags; + struct sl811_hc *hc; + struct sl811_urb_priv *urbp; + int call = 0; + + if (!urb) { + PDEBUG(1, "urb is null"); + return -EINVAL; + } + + if (!urb->dev || !urb->dev->bus) { + PDEBUG(1, "dev or bus is null"); + return -ENODEV; + } + + hc = urb->dev->bus->hcpriv; + urbp = urb->hcpriv; + + /* a request to the virtual root hub */ + if (usb_pipedevice(urb->pipe) == hc->rh.devnum) + return sl811_rh_unlink_urb(urb); + + spin_lock_irqsave(&hc->hc_lock, flags); + spin_lock(&urb->lock); + + // in wait list + if (sl811_find_same_urb(&hc->wait_list, urb)) { + PDEBUG(2, "unlink urb in wait list"); + list_del_init(&urb->urb_list); + urb->status = -ENOENT; + call = 1; + goto out; + } + + if (urb->status == -EINPROGRESS) { + PDEBUG(2, "urb is still in progress"); + urbp->unlink = 1; + + // Is it in progress? + urbp = urb->hcpriv; + if (urbp && hc->cur_td == urbp->cur_td) { + // simple, let it out + PDEBUG(2, "unlink: cur td is still in progress!"); + hc->cur_td = NULL; + } + + goto result; + } else if (!list_empty(&urb->urb_list)) { + PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); + list_del_init(&urb->urb_list); + if (urbp) + goto result; + else + call = 1; + } + +out: + spin_unlock(&urb->lock); + spin_unlock_irqrestore(&hc->hc_lock, flags); + + if (call && urb->complete) + urb->complete(urb); + + return 0; + +result: + spin_unlock(&urb->lock); + + list_del_init(&urb->urb_list); + sl811_result_urb(urb); + + spin_unlock_irqrestore(&hc->hc_lock, flags); + + return 0; +} + +#endif + +static int sl811_get_current_frame_number(struct usb_device *usb_dev) +{ + return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number; +} + +static struct usb_operations sl811_device_operations = +{ + sl811_alloc_dev_priv, + sl811_free_dev_priv, + sl811_get_current_frame_number, + sl811_submit_urb, + sl811_unlink_urb +}; + +/* + * This functions transmit a td. + */ +static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td) +{ + sl811_print_td(4, td); + sl811_write_buf(hc, SL811_ADDR_A, &td->addr, 4); + if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT)) + sl811_write_buf(hc, td->addr, td->buf, td->len); + + sl811_write(hc, SL811_CTRL_A, td->ctrl); +} + + +/* + * This function checks the status of the transmitted or received packet + * and copy the data from the SL811HS register into a buffer. + */ +static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td) +{ + struct urb *urb = td->urb; +#ifdef SL811_DEBUG + int dev = usb_pipedevice(td->urb->pipe); + int ep = usb_pipeendpoint(td->urb->pipe); +#endif + + sl811_read_buf(hc, SL811_STS_A, &td->status, 2); + + if (td->status & SL811_USB_STS_ACK) { + td->done = 1; + +/* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) { + PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep); + td->td_status = -EILSEQ; + } +*/ + if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0) + sl811_read_buf(hc, td->addr, td->buf, td->len - td->left); + + if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) { + PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td); + td->td_status = -EREMOTEIO; + } else + td->td_status = 0; + } else if (td->status & SL811_USB_STS_STALL) { + PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td); + td->td_status = -EPIPE; + if (urb->dev) + usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe)); + td->done = 1; + } else if (td->status & SL811_USB_STS_OVERFLOW) { + PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT); + td->td_status = -EOVERFLOW; + td->done = 1; + } else if (td->status & SL811_USB_STS_TIMEOUT ) { + PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td); + td->td_status = -ETIMEDOUT; + if (--td->errcnt == 0) + td->done = 1; + } else if (td->status & SL811_USB_STS_ERROR) { + PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td); + td->td_status = -EILSEQ; + if (--td->errcnt == 0) + td->done = 1; + } else if (td->status & SL811_USB_STS_NAK) { + ++td->nakcnt; + PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt); + td->td_status = -EINPROGRESS; + if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) { + PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt); + td->td_status = -ETIMEDOUT; + td->done = 1; + } + } + + sl811_print_td(4, td); +} + +/* + * This function checks the status of current urb. + */ +static int sl811_parse_cur_urb(struct urb *urb) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + struct sl811_td *td = urbp->cur_td; + struct list_head *tmp; + + sl811_print_td(5, td); + + // this td not done yet. + if (!td->done) + return 0; + + // the last ld, so the urb is done. + if (td == urbp->last_td) { + PDEBUG(4, "urb = %p is done success", td->urb); + if (usb_pipeisoc(td->urb->pipe)) + PDEBUG(4, "ISO URB DONE, td = %p", td); + return 1; + } + + // iso transfer, we always advance to next td + if (usb_pipeisoc(td->urb->pipe)) { + tmp = &td->td_list; + tmp = tmp->next; + urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); + PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td); + return 0; + } + + // some error occur, so the urb is done. + if (td->td_status) { + PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status); + return 1; + } + + // short packet. + if (td->left) { + if (usb_pipecontrol(td->urb->pipe)) { + // control packet, we advance to the last td + PDEBUG(3, "ctrl short packet, advance to last td"); + urbp->cur_td = urbp->last_td; + return 0; + } else { + // interrut and bulk packet, urb is over. + PDEBUG(3, "bulk or intr short packet, urb is over"); + return 1; + } + } + + // we advance to next td. + tmp = &td->td_list; + tmp = tmp->next; + urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); +#ifdef SL811_DEBUG + PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td); + sl811_print_td(5, urbp->cur_td); + if (td == urbp->cur_td) + PDEBUG(1, "bug!!!"); +#endif + return 0; +} + +/* + * Find the next td to transfer. + */ +static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td) +{ + struct sl811_urb_priv *urbp = urb->hcpriv; + + PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td); + + // iso don't schedule the td in the same frame. + if (usb_pipeisoc(cur_td->urb->pipe)) + return NULL; + + // cur td is not complete + if (!cur_td->done) + return NULL; + + // here, urbp->cur_td is already the next td; + return urbp->cur_td; +} + +/* + * Scan the list to find a active urb + */ +static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next) +{ + struct urb *urb; + int i; + + if (list_empty(next)) + return NULL; + + if (next == hc->cur_list) + return NULL; + + for (i = 0; i < 4; ++i) + if (next == &hc->urb_list[i]) + return NULL; + + urb = list_entry(next, struct urb, urb_list); + PDEBUG(4, "next urb in list is at %p", urb); + + return urb; +} + +/* + * Find the next td to transfer. + */ +static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next) +{ + struct urb *urb = NULL; + int back_loop = 1; + struct list_head *old_list = hc->cur_list; + + // try to get next urb in the same list. + if (next) { + urb = sl811_get_list_next_urb(hc, next); + if (!urb) + ++hc->cur_list; + } + + // try other list. + if (!urb) { +re_loop: + // try all the list. + while (hc->cur_list < &hc->urb_list[4]) { + if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next))) + return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td; + ++hc->cur_list; + } + // the last list is try + if (back_loop && (old_list >= &hc->ctrl_list)) { + hc->cur_list = &hc->ctrl_list; + back_loop = 0; + goto re_loop; + } + } + + if (hc->cur_list > &hc->urb_list[3]) + hc->cur_list = &hc->ctrl_list; + + return NULL; +} + +/* + * This function process the transfer rusult. + */ +static void sl811_transfer_done(struct sl811_hc *hc, int sof) +{ + struct sl811_td *cur_td = hc->cur_td, *next_td = NULL; + struct urb *cur_urb = NULL; + struct list_head *next = NULL; + int done; + + PDEBUG(5, "enter"); + + if (cur_td == NULL) { + PDEBUG(1, "in done interrupt, but td is null, be already parsed?"); + return ; + } + + cur_urb = cur_td->urb; + hc->cur_td = NULL; + next = &cur_urb->urb_list; + next = next->next; + + spin_lock(&cur_urb->lock); + sl811_parse_cur_td(hc, cur_td); + done = sl811_parse_cur_urb(cur_urb); + spin_unlock(&cur_urb->lock); + + if (done) { + list_del_init(&cur_urb->urb_list); + cur_td = NULL; + sl811_result_urb(cur_urb); + } + + if (sof) + return ; + + if (!done) { + next_td = sl811_schedule_next_td(cur_urb, cur_td); + if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) { + hc->cur_td = next_td; + PDEBUG(5, "ADD TD"); + sl811_trans_cur_td(hc, next_td); + return ; + } + } + + while (1) { + next_td = sl811_schedule_next_urb(hc, next); + if (!next_td) + return; + if (next_td == cur_td) + return; + next = &next_td->urb->urb_list; + next = next->next; + if (sl811_calc_bus_remainder(hc) > next_td->bustime) { + hc->cur_td = next_td; + PDEBUG(5, "ADD TD"); + sl811_trans_cur_td(hc, next_td); + return ; + } + } +} + +/* + * + */ +static void inline sl811_dec_intr_interval(struct sl811_hc *hc) +{ + struct list_head *head, *tmp; + struct urb *urb; + struct sl811_urb_priv *urbp; + + if (list_empty(&hc->idle_intr_list)) + return ; + + head = &hc->idle_intr_list; + tmp = head->next; + + while (tmp != head) { + urb = list_entry(tmp, struct urb, urb_list); + tmp = tmp->next; + spin_lock(&urb->lock); + urbp = urb->hcpriv; + if (--urbp->interval == 0) { + list_del(&urb->urb_list); + list_add(&urb->urb_list, &hc->intr_list); + PDEBUG(4, "intr urb active"); + } + spin_unlock(&urb->lock); + } +} + +/* + * The sof interrupt is happen. + */ +static void sl811_start_sof(struct sl811_hc *hc) +{ + struct sl811_td *next_td; +#ifdef SL811_DEBUG + static struct sl811_td *repeat_td = NULL; + static int repeat_cnt = 1; +#endif + if (++hc->frame_number > 1024) + hc->frame_number = 0; + + if (hc->active_urbs == 0) + return ; + + sl811_dec_intr_interval(hc); + + if (hc->cur_td) { + if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { +#ifdef SL811_DEBUG + if (repeat_td == hc->cur_td) + ++repeat_cnt; + else { + if (repeat_cnt >= 2) + PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt); + repeat_cnt = 1; + repeat_td = hc->cur_td; + } +#endif + return ; + } else { + PDEBUG(2, "lost of interrupt in sof? do parse!"); + sl811_transfer_done(hc, 1); + + // let this frame idle + return; + } + } + + hc->cur_list = &hc->iso_list; + + if (hc->active_urbs == 0) + return ; + + next_td = sl811_schedule_next_urb(hc, NULL); + if (!next_td) { +#ifdef SL811_DEBUG + if (list_empty(&hc->idle_intr_list)) + PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs); +#endif + return; + } + if (sl811_calc_bus_remainder(hc) > next_td->bustime) { + hc->cur_td = next_td; + sl811_trans_cur_td(hc, next_td); + } else + PDEBUG(2, "bus time if not enough, why?"); +} + +/* + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Return: 0 = no device attached; 1 = USB device attached + */ +static int sl811_hc_reset(struct sl811_hc *hc) +{ + int status ; + + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); + + mdelay(20); + + // Disable hardware SOF generation, clear all irq status. + sl811_write(hc, SL811_CTRL1, 0); + mdelay(2); + sl811_write(hc, SL811_INTRSTS, 0xff); + status = sl811_read(hc, SL811_INTRSTS); + + if (status & SL811_INTR_NOTPRESENT) { + // Device is not present + PDEBUG(0, "Device not present"); + hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); + hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); + return 0; + } + + // Send SOF to address 0, endpoint 0. + sl811_write(hc, SL811_LEN_B, 0); + sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); + sl811_write(hc, SL811_DEV_B, 0x00); + sl811_write (hc, SL811_SOFLOW, SL811_12M_HI); + + if (status & SL811_INTR_SPEED_FULL) { + /* full speed device connect directly to root hub */ + PDEBUG (0, "Full speed Device attached"); + + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); + hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; + hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write (hc, SL811_INTRSTS, 0xff); + } else { + /* slow speed device connect directly to root-hub */ + PDEBUG(0, "Low speed Device attached"); + + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET); + mdelay(20); + sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); + sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); + + /* start the SOF or EOP */ + sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM); + hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; + mdelay(2); + sl811_write(hc, SL811_INTRSTS, 0xff); + } + + hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; + sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV); + + return 1; +} + +/* + * Interrupt service routine. + */ +static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r) +{ + __u8 status; + struct sl811_hc *hc = __hc; + + status = sl811_read(hc, SL811_INTRSTS); + if (status == 0) + return ; /* Not me */ + + sl811_write(hc, SL811_INTRSTS, 0xff); + + if (status & SL811_INTR_INSRMV) { + sl811_write(hc, SL811_INTR, 0); + sl811_write(hc, SL811_CTRL1, 0); + // wait for device stable + mdelay(100); + sl811_hc_reset(hc); + return ; + } + + spin_lock(&hc->hc_lock); + + if (status & SL811_INTR_DONE_A) { + if (status & SL811_INTR_SOF) { + sl811_transfer_done(hc, 1); + PDEBUG(4, "sof in done!"); + sl811_start_sof(hc); + } else + sl811_transfer_done(hc, 0); + } else if (status & SL811_INTR_SOF) + sl811_start_sof(hc); + + spin_unlock(&hc->hc_lock); + + return ; +} + +/* + * This function allocates all data structure and store in the + * private data structure. + * + * Return value : data structure for the host controller + */ +static struct sl811_hc* __devinit sl811_alloc_hc(void) +{ + struct sl811_hc *hc; + struct usb_bus *bus; + int i; + + PDEBUG(4, "enter"); + + hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL); + if (!hc) + return NULL; + + memset(hc, 0, sizeof(struct sl811_hc)); + + hc->rh_status.wPortStatus = USB_PORT_STAT_POWER; + hc->rh_status.wPortChange = 0; + + hc->active_urbs = 0; + INIT_LIST_HEAD(&hc->hc_hcd_list); + list_add(&hc->hc_hcd_list, &sl811_hcd_list); + + init_waitqueue_head(&hc->waitq); + + for (i = 0; i < 6; ++i) + INIT_LIST_HEAD(&hc->urb_list[i]); + + hc->cur_list = &hc->iso_list; + + bus = usb_alloc_bus(&sl811_device_operations); + if (!bus) { + kfree (hc); + return NULL; + } + + hc->bus = bus; + bus->bus_name = MODNAME; + bus->hcpriv = hc; + + return hc; +} + +/* + * This function De-allocate all resources + */ +static void sl811_release_hc(struct sl811_hc *hc) +{ + PDEBUG(4, "enter"); + + /* disconnect all devices */ + if (hc->bus->root_hub) + usb_disconnect(&hc->bus->root_hub); + + // Stop interrupt handle + if (hc->irq) + free_irq(hc->irq, hc); + hc->irq = 0; + + /* Stop interrupt for sharing */ + if (hc->addr_io) { + /* Disable Interrupts */ + sl811_write(hc, SL811_INTR, 0); + + /* Remove all Interrupt events */ + mdelay(2); + sl811_write(hc, SL811_INTRSTS, 0xff); + } + + /* free io regions */ + sl811_release_regions(hc); + + usb_deregister_bus(hc->bus); + usb_free_bus(hc->bus); + + list_del(&hc->hc_hcd_list); + INIT_LIST_HEAD(&hc->hc_hcd_list); + + kfree (hc); +} + +/* + * This function request IO memory regions, request IRQ, and + * allocate all other resources. + * + * Input: addr_io = first IO address + * data_io = second IO address + * irq = interrupt number + * + * Return: 0 = success or error condition + */ +static int __devinit sl811_found_hc(int addr_io, int data_io, int irq) +{ + struct sl811_hc *hc; + + PDEBUG(4, "enter"); + + hc = sl811_alloc_hc(); + if (!hc) + return -ENOMEM; + + if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { + PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); + sl811_release_hc(hc); + return -EBUSY; + } + + if (sl811_reg_test(hc)) { + PDEBUG(1, "SL811 register test failed!"); + sl811_release_hc(hc); + return -ENODEV; + } + +//#ifdef SL811_DEBUG_VERBOSE + { + __u8 u = sl811_read(hc, SL811_HWREV); + + // Show the hardware revision of chip + PDEBUG(1, "SL811 HW: %02Xh", u); + switch (u & 0xF0) { + case 0x00: PDEBUG(1, "SL11H"); break; + case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; + case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; + default: PDEBUG(1, "Revision unknown!"); + } + } +//#endif // SL811_DEBUG_VERBOSE + + sl811_init_irq(); + + usb_register_bus(hc->bus); + + if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) { + PDEBUG(1, "request interrupt %d failed", irq); + sl811_release_hc(hc); + return -EBUSY; + } + hc->irq = irq; + + printk(KERN_INFO __FILE__ ": USB SL811 at %08x,%08x, IRQ %d\n", + hc->addr_io, hc->data_io, irq); + + sl811_hc_reset(hc); + sl811_connect_rh(hc); + + return 0; +} + +/* + * This is an init function, and it is the first function being called + * + * Return: 0 = success or error condition + */ +static int __init sl811_hcd_init(void) +{ + int ret = -ENODEV; + + PDEBUG(4, "enter"); + + info(DRIVER_VERSION " : " DRIVER_DESC); + +#ifdef CONFIG_X86 + { + int count; + // registering some instance + for (count = 0; count < MAX_CONTROLERS; count++) { + if (io[count]) { + ret = sl811_found_hc(io[count], io[count]+OFFSET_DATA_REG, irq[count]); + if (ret) + return (ret); + } + } + } +#endif +#ifdef CONFIG_ARCH_RAMSES + ret = sl811_found_hc(0,0,SL811HS_IRQ); +#endif + + return ret; +} + +/* + * This is a cleanup function, and it is called when module is unloaded. + */ +static void __exit sl811_hcd_cleanup(void) +{ + struct list_head *list = sl811_hcd_list.next; + struct sl811_hc *hc; + + PDEBUG(4, "enter"); + + for (; list != &sl811_hcd_list; ) { + hc = list_entry(list, struct sl811_hc, hc_hcd_list); + list = list->next; + sl811_release_hc(hc); + } +} + +module_init(sl811_hcd_init); +module_exit(sl811_hcd_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); --- /dev/null +++ linux-2.4.21/drivers/usb/host/sl811.h @@ -0,0 +1,177 @@ +#ifndef __LINUX_SL811_H +#define __LINUX_SL811_H + +#define SL811_DEBUG + +#ifdef SL811_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) info("[%s:%d] " fmt, \ + __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +//#define SL811_TIMEOUT + +/* Sl811 host control register */ +#define SL811_CTRL_A 0x00 +#define SL811_ADDR_A 0x01 +#define SL811_LEN_A 0x02 +#define SL811_STS_A 0x03 /* read */ +#define SL811_PIDEP_A 0x03 /* write */ +#define SL811_CNT_A 0x04 /* read */ +#define SL811_DEV_A 0x04 /* write */ +#define SL811_CTRL1 0x05 +#define SL811_INTR 0x06 +#define SL811_CTRL_B 0x08 +#define SL811_ADDR_B 0x09 +#define SL811_LEN_B 0x0A +#define SL811_STS_B 0x0B /* read */ +#define SL811_PIDEP_B 0x0B /* write */ +#define SL811_CNT_B 0x0C /* read */ +#define SL811_DEV_B 0x0C /* write */ +#define SL811_INTRSTS 0x0D /* write clears bitwise */ +#define SL811_HWREV 0x0E /* read */ +#define SL811_SOFLOW 0x0E /* write */ +#define SL811_SOFCNTDIV 0x0F /* read */ +#define SL811_CTRL2 0x0F /* write */ + +/* USB control register bits (addr 0x00 and addr 0x08) */ +#define SL811_USB_CTRL_ARM 0x01 +#define SL811_USB_CTRL_ENABLE 0x02 +#define SL811_USB_CTRL_DIR_OUT 0x04 +#define SL811_USB_CTRL_ISO 0x10 +#define SL811_USB_CTRL_SOF 0x20 +#define SL811_USB_CTRL_TOGGLE_1 0x40 +#define SL811_USB_CTRL_PREAMBLE 0x80 + +/* USB status register bits (addr 0x03 and addr 0x0B) */ +#define SL811_USB_STS_ACK 0x01 +#define SL811_USB_STS_ERROR 0x02 +#define SL811_USB_STS_TIMEOUT 0x04 +#define SL811_USB_STS_TOGGLE_1 0x08 +#define SL811_USB_STS_SETUP 0x10 +#define SL811_USB_STS_OVERFLOW 0x20 +#define SL811_USB_STS_NAK 0x40 +#define SL811_USB_STS_STALL 0x80 + +/* Control register 1 bits (addr 0x05) */ +#define SL811_CTRL1_SOF 0x01 +#define SL811_CTRL1_RESET 0x08 +#define SL811_CTRL1_JKSTATE 0x10 +#define SL811_CTRL1_SPEED_LOW 0x20 +#define SL811_CTRL1_SUSPEND 0x40 + +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ +#define SL811_INTR_DONE_A 0x01 +#define SL811_INTR_DONE_B 0x02 +#define SL811_INTR_SOF 0x10 +#define SL811_INTR_INSRMV 0x20 +#define SL811_INTR_DETECT 0x40 +#define SL811_INTR_NOTPRESENT 0x40 +#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0x0E) */ +#define SL811_HWR_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0x0F) */ +#define SL811_CTL2_SOFHI 0x3F +#define SL811_CTL2_DSWAP 0x40 +#define SL811_CTL2_HOST 0x80 + +/* Set up for 1-ms SOF time. */ +#define SL811_12M_LOW 0xE0 +#define SL811_12M_HI 0x2E + +#define SL811_DATA_START 0x10 +#define SL811_DATA_LIMIT 240 + + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 + + +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; /* interrupt URB of root hub */ + int send; /* active flag */ + int interval; /* intervall of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ +}; + +struct sl811_td { + /* hardware */ + __u8 ctrl; /* control register */ + + /* write */ + __u8 addr; /* base adrress register */ + __u8 len; /* base length register */ + __u8 pidep; /* PId and endpoint register */ + __u8 dev; /* device address register */ + + /* read */ + __u8 status; /* status register */ + __u8 left; /* transfer count register */ + + /* software */ + __u8 errcnt; /* error count, begin with 3 */ + __u8 done; /* is this td tranfer done */ + __u8 *buf; /* point to data buffer for tranfer */ + int bustime; /* the bus time need by this td */ + int td_status; /* the status of this td */ + int nakcnt; /* number of naks */ + struct urb *urb; /* the urb this td belongs to */ + struct list_head td_list; /* link to a list of the urb */ +}; + +struct sl811_urb_priv { + struct urb *urb; /* the urb this priv beloings to */ + struct list_head td_list; /* list of all the td of this urb */ + struct sl811_td *cur_td; /* current td is in processing or it will be */ + struct sl811_td *first_td; /* the first td of this urb */ + struct sl811_td *last_td; /* the last td of this urb */ + int interval; /* the query time value for intr urb */ + int unlink; /* is the this urb unlinked */ + unsigned long inserttime; /* the time when insert to list */ +}; + +struct sl811_hc { + spinlock_t hc_lock; /* Lock for this structure */ + + int irq; /* IRQ number this hc use */ + int addr_io; /* I/O address line address */ + int data_io; /* I/O data line address */ + struct virt_root_hub rh; /* root hub */ + struct usb_port_status rh_status;/* root hub port status */ + struct list_head urb_list[6]; /* set of urbs, the order is iso,intr,ctrl,bulk,inactive intr, wait */ + struct list_head *cur_list; /* the current list is in process */ + wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ + struct sl811_td *cur_td; /* point to the td is in process */ + struct list_head hc_hcd_list; /* list of all hci_hcd */ + struct usb_bus *bus; /* our bus */ + int active_urbs; /* total number of active usbs */ + int frame_number; /* the current frame number, we do't use it, any one need it? */ +}; + +#define iso_list urb_list[0] /* set of isoc urbs */ +#define intr_list urb_list[1] /* ordered (tree) set of int urbs */ +#define ctrl_list urb_list[2] /* set of ctrl urbs */ +#define bulk_list urb_list[3] /* set of bulk urbs */ +#define idle_intr_list urb_list[4] /* set of intr urbs in its idle time*/ +#define wait_list urb_list[5] /* set of wait urbs */ + +#endif --- linux-2.4.21/drivers/usb/storage/transport.h~usb-sonycamera +++ linux-2.4.21/drivers/usb/storage/transport.h @@ -75,6 +75,8 @@ #define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ #endif +#define US_PR_DEVICE 0xff /* Use device's value */ + /* * Bulk only data structures */ --- linux-2.4.21/drivers/usb/storage/unusual_devs.h~usb-sonycamera +++ linux-2.4.21/drivers/usb/storage/unusual_devs.h @@ -223,10 +223,10 @@ US_FL_FIX_INQUIRY | US_FL_START_STOP ), /* This entry is needed because the device reports Sub=ff */ -UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0440, +UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", - "DSC-S30/S70/S75/505V/F505/F707/F717", - US_SC_SCSI, US_PR_CB, NULL, + "DSC-S30/S70/S75/505V/F505/F707/F717/P8", + US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ), /* Reported by wim@geeks.nl */ --- linux-2.4.21/drivers/usb/storage/usb.c~usb-sonycamera +++ linux-2.4.21/drivers/usb/storage/usb.c @@ -622,7 +622,9 @@ /* Determine subclass and protocol, or copy from the interface */ subclass = unusual_dev->useProtocol; - protocol = unusual_dev->useTransport; + protocol = (unusual_dev->useTransport == US_PR_DEVICE) ? + altsetting->bInterfaceProtocol : + unusual_dev->useTransport; flags = unusual_dev->flags; /* --- linux-2.4.21/drivers/video/fbcon-cfb16.c~fb-turn180 +++ linux-2.4.21/drivers/video/fbcon-cfb16.c @@ -34,6 +34,41 @@ #endif }; +static u8 mirrortab_cfb16[] = { + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0, + 0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8, + 0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4, + 0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC, + 0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2, + 0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA, + 0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6, + 0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE, + 0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1, + 0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9, + 0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5, + 0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED, + 0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3, + 0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB, + 0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7, + 0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF, + 0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF +}; + void fbcon_cfb16_setup(struct display *p) { p->next_line = p->line_length ? p->line_length : p->var.xres_virtual<<1; @@ -46,6 +81,53 @@ int bytes = p->next_line, linesize = bytes * fontheight(p), rows; u8 *src, *dst; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + char *scrn_end = p->screen_base + p->var.xres*p->var.yres * 2; +/* + printk("---@paul@-------------------------\n"\ + "fbcon_cfb16_bmove() %d %d %d %d %d %d\n", + sx,sy,dx,dy,height,width + ); +*/ + if (sx == 0 && dx == 0 && width * fontwidth(p) * 2 == bytes) + { + fb_memmove( + scrn_end - dy * linesize, + scrn_end - sy * linesize, + height * linesize + ); + return; + } + if (fontwidthlog(p)) { + sx <<= fontwidthlog(p)+1; + dx <<= fontwidthlog(p)+1; + width <<= fontwidthlog(p)+1; + } else { + sx *= fontwidth(p)*2; + dx *= fontwidth(p)*2; + width *= fontwidth(p)*2; + } + if (dy < sy || (dy == sy && dx < sx)) { + src = scrn_end + sy * linesize + sx; + dst = scrn_end + dy * linesize + dx; + for (rows = height * fontheight(p); rows--;) + { + fb_memmove(dst, src, width); + src += bytes; + dst += bytes; + } + } else { + src = scrn_end + (sy+height) * linesize + sx - bytes; + dst = scrn_end + (dy+height) * linesize + dx - bytes; + for (rows = height * fontheight(p); rows--;) + { + fb_memmove(dst, src, width); + src -= bytes; + dst -= bytes; + } + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { if (sx == 0 && dx == 0 && width * fontwidth(p) * 2 == bytes) { fb_memmove(p->screen_base + dy * linesize, p->screen_base + sy * linesize, @@ -78,6 +160,8 @@ dst -= bytes; } } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } static inline void rectfill(u8 *dest, int width, int height, u32 data, @@ -108,10 +192,16 @@ int bytes = p->next_line, lines = height * fontheight(p); u32 bgx; - dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p) * 2; - - bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; - + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest = p->screen_base + + p->var.xres*p->var.yres * 2 + - (sy+height) * fontheight(p) * bytes + + sx * fontwidth(p) * 2; + bgx = 1; + } else { + dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p) * 2; + bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; + } width *= fontwidth(p)/4; if (width * 8 == bytes) rectfill(dest, lines * width * 4, 1, bgx, bytes); @@ -126,14 +216,69 @@ int bytes = p->next_line, rows; u32 eorx, fgx, bgx; - dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; - fgx = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)]; bgx = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)]; fgx |= (fgx << 16); bgx |= (bgx << 16); eorx = fgx ^ bgx; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest = p->screen_base + + p->var.xres*p->var.yres * 2 + - yy * fontheight(p) * bytes + - xx * fontwidth(p) * 2; + + switch (fontwidth(p)) { + case 4: + cdat = p->fontdata + (c & p->charmask) * fontheight(p); + for (rows = fontheight(p); rows--; dest += bytes) + { + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); + } + case 8: + cdat = p->fontdata + (c & p->charmask) * fontheight(p); + for (rows = fontheight(p); rows--; dest += bytes) + { + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); + } + break; + case 12: + cdat = p->fontdata + ((c & p->charmask) * fontheight(p) << 1); + for (rows = fontheight(p); rows--; dest += bytes) { + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-24); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-20); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-12); + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); + } + case 16: + cdat = p->fontdata + ((c & p->charmask) * fontheight(p) << 1); + for (rows = fontheight(p); rows--; dest += bytes) { + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-32); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-28); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-24); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-20); + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); + } + break; + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { + dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; switch (fontwidth(p)) { case 4: case 8: @@ -167,6 +312,8 @@ } break; } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, @@ -177,7 +324,6 @@ int rows, bytes = p->next_line; u32 eorx, fgx, bgx; - dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; c = scr_readw(s); fgx = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)]; bgx = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)]; @@ -185,6 +331,81 @@ bgx |= (bgx << 16); eorx = fgx ^ bgx; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest0 = p->screen_base + + p->var.xres * p->var.yres * 2 + - yy * fontheight(p) * bytes + - xx * fontwidth(p) * 2; + + switch (fontwidth(p)) { + case 4: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + c * fontheight(p); + for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) + { + u8 bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p)*2; + } + case 8: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + c * fontheight(p); + for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) + { + u8 bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p)*2; + } + break; + case 12: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + (c * fontheight(p) << 1); + for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) + { + u8 bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-24); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-20); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-12); + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p)*2; + } + case 16: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + (c * fontheight(p) << 1); + for (rows = fontheight(p), dest = dest0; rows--; dest -= bytes) + { + u8 bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-32); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-28); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-24); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-20); + bits = mirrortab_cfb16[*cdat++]; + fb_writel((tab_cfb16[bits >> 6] & eorx) ^ bgx, dest-16); + fb_writel((tab_cfb16[bits >> 4 & 3] & eorx) ^ bgx, dest-12); + fb_writel((tab_cfb16[bits >> 2 & 3] & eorx) ^ bgx, dest-8); + fb_writel((tab_cfb16[bits & 3] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p)*2; + } + break; + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { + dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 2; switch (fontwidth(p)) { case 4: case 8: @@ -226,6 +447,8 @@ } break; } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb16_revc(struct display *p, int xx, int yy) @@ -233,6 +456,32 @@ u8 *dest; int bytes = p->next_line, rows; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest = p->screen_base + + p->var.xres*p->var.yres * 2 + - yy * fontheight(p) * bytes + - xx * fontwidth(p) * 2; + for (rows = fontheight(p); rows--; dest -= bytes) { + switch (fontwidth(p)) { + case 16: + fb_writel(fb_readl(dest-32) ^ 0xffffffff, dest-32); + fb_writel(fb_readl(dest-28) ^ 0xffffffff, dest-28); + /* FALL THROUGH */ + case 12: + fb_writel(fb_readl(dest-24) ^ 0xffffffff, dest-24); + fb_writel(fb_readl(dest-20) ^ 0xffffffff, dest-20); + /* FALL THROUGH */ + case 8: + fb_writel(fb_readl(dest-16) ^ 0xffffffff, dest-16); + fb_writel(fb_readl(dest-12) ^ 0xffffffff, dest-12); + /* FALL THROUGH */ + case 4: + fb_writel(fb_readl(dest-8) ^ 0xffffffff, dest-8); + fb_writel(fb_readl(dest-4) ^ 0xffffffff, dest-4); + } + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2; for (rows = fontheight(p); rows--; dest += bytes) { switch (fontwidth(p)) { @@ -253,6 +502,8 @@ fb_writel(fb_readl(dest+4) ^ 0xffffffff, dest+4); } } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb16_clear_margins(struct vc_data *conp, struct display *p, @@ -268,6 +519,9 @@ bgx = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; if (!bottom_only && (right_width = p->var.xres-right_start)) + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + printk("---@paul@------------------------- fbcon-cfb16 clear margins\n"); + } rectfill(p->screen_base+right_start*2, right_width, p->var.yres_virtual, bgx, bytes); if ((bottom_width = p->var.yres-bottom_start)) --- linux-2.4.21/drivers/video/fbcon-cfb8.c~fb-turn180 +++ linux-2.4.21/drivers/video/fbcon-cfb8.c @@ -39,6 +39,41 @@ #endif }; +static u8 mirrortab_cfb8[] = { + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0, + 0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8, + 0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4, + 0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC, + 0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2, + 0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA, + 0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6, + 0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE, + 0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1, + 0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9, + 0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5, + 0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED, + 0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3, + 0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB, + 0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7, + 0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF, + 0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF +}; + void fbcon_cfb8_setup(struct display *p) { p->next_line = p->line_length ? p->line_length : p->var.xres_virtual; @@ -51,10 +86,57 @@ int bytes = p->next_line, linesize = bytes * fontheight(p), rows; u8 *src,*dst; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { +/* + printk("---@paul@-------------------------\n"\ + "fbcon_cfb8_bmove() %d %d %d %d %d %d\n", + sx,sy,dx,dy,height,width + ); +*/ + if (sx == 0 && dx == 0 && width * fontwidth(p) == bytes) + { + fb_memmove( + p->screen_base + p->var.xres*p->var.yres - dy * linesize, + p->screen_base + p->var.xres*p->var.yres - sy * linesize, + height * linesize); + return; + } + if (fontwidthlog(p)) { + sx <<= fontwidthlog(p); dx <<= fontwidthlog(p); width <<= fontwidthlog(p); + } else { + sx *= fontwidth(p); dx *= fontwidth(p); width *= fontwidth(p); + } + if (dy < sy || (dy == sy && dx < sx)) + { + src = p->screen_base + p->var.xres*p->var.yres + - sy * linesize - sx; + dst = p->screen_base + p->var.xres*p->var.yres + - dy * linesize - dx; + for (rows = height * fontheight(p) ; rows-- ;) + { + fb_memmove(dst, src, width); + src += bytes; + dst += bytes; + } + } else + { + src = p->screen_base + p->var.xres*p->var.yres + - (sy+height) * linesize - sx + bytes; + dst = p->screen_base + p->var.xres*p->var.yres + - (dy+height) * linesize - dx + bytes; + for (rows = height * fontheight(p) ; rows-- ;) + { + fb_memmove(dst, src, width); + src -= bytes; + dst -= bytes; + } + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { if (sx == 0 && dx == 0 && width * fontwidth(p) == bytes) { - fb_memmove(p->screen_base + dy * linesize, - p->screen_base + sy * linesize, - height * linesize); + fb_memmove(p->screen_base + dy * linesize, + p->screen_base + sy * linesize, + height * linesize); return; } if (fontwidthlog(p)) { @@ -79,6 +161,7 @@ dst -= bytes; } } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } static inline void rectfill(u8 *dest, int width, int height, u8 data, @@ -97,11 +180,17 @@ int bytes=p->next_line,lines=height * fontheight(p); u8 bgx; - dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p); - - bgx=attr_bgcol_ec(p,conp); - - width *= fontwidth(p); + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + bgx=attr_bgcol_ec(p,conp); + width *= fontwidth(p); + dest = p->screen_base + p->var.xres*p->var.yres + - (sy+height) * fontheight(p) * bytes + + sx * fontwidth(p); + } else { + dest = p->screen_base + sy * fontheight(p) * bytes + sx * fontwidth(p); + bgx=attr_bgcol_ec(p,conp); + width *= fontwidth(p); + } if (width == bytes) rectfill(dest, lines * width, 1, bgx, bytes); else @@ -114,8 +203,8 @@ u8 *dest,*cdat; int bytes=p->next_line,rows; u32 eorx,fgx,bgx; + u8 chrrow; - dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); if (fontwidth(p) <= 8) cdat = p->fontdata + (c & p->charmask) * fontheight(p); else @@ -129,6 +218,53 @@ bgx |= (bgx << 16); eorx = fgx ^ bgx; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest = p->screen_base + + p->var.xres*p->var.yres + - yy * fontheight(p) * bytes + - xx * fontwidth(p); + + switch (fontwidth(p)) { + case 4: + for (rows = fontheight(p) ; rows-- ; dest += bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); + } + break; + case 8: + for (rows = fontheight(p) ; rows-- ; dest += bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); + } + break; + case 12: + for (rows = fontheight(p) ; rows-- ; dest += bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-12); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-8); + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); + } + break; + case 16: + for (rows = fontheight(p) ; rows-- ; dest += bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-16); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-12); + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); + } + break; + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { + dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); switch (fontwidth(p)) { case 4: for (rows = fontheight(p) ; rows-- ; dest += bytes) @@ -152,6 +288,8 @@ } break; } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, @@ -161,8 +299,8 @@ u16 c; int rows,bytes=p->next_line; u32 eorx, fgx, bgx; + u8 chrrow; - dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); c = scr_readw(s); fgx = attr_fgcol(p, c); bgx = attr_bgcol(p, c); @@ -171,6 +309,76 @@ bgx |= (bgx << 8); bgx |= (bgx << 16); eorx = fgx ^ bgx; + + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest0 = p->screen_base + + p->var.xres*p->var.yres + - yy * fontheight(p) * bytes + - xx * fontwidth(p); + switch (fontwidth(p)) { + case 4: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + c * fontheight(p); + + for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); + } + dest0 -= 4; + } + break; + case 8: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + c * fontheight(p); + for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); + } + dest0 -= 8; + } + break; + case 12: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + (c * fontheight(p) << 1); + + for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-12); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-8); + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p); + } + break; + case 16: + while (count--) { + c = scr_readw(s++) & p->charmask; + cdat = p->fontdata + (c * fontheight(p) << 1); + + for (rows = fontheight(p), dest = dest0; rows-- ; dest -= bytes) + { + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-16); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-12); + chrrow = mirrortab_cfb8[*cdat++]; + fb_writel((nibbletab_cfb8[chrrow >> 4] & eorx) ^ bgx, dest-8); + fb_writel((nibbletab_cfb8[chrrow & 0xf] & eorx) ^ bgx, dest-4); + } + dest0 -= fontwidth(p); + } + break; + } /* switch (fontwidth(p)) */ +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { + dest0 = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); switch (fontwidth(p)) { case 4: while (count--) { @@ -212,6 +420,8 @@ } break; } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb8_revc(struct display *p, int xx, int yy) @@ -219,6 +429,21 @@ u8 *dest; int bytes=p->next_line, rows; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dest = p->screen_base + p->var.xres*p->var.yres + - yy * fontheight(p) * bytes + - xx * fontwidth(p); + for (rows = fontheight(p) ; rows-- ; dest -= bytes) { + switch (fontwidth(p)) { + case 16: fb_writel(fb_readl(dest-16) ^ 0x0f0f0f0f, dest-16); /* fall thru */ + case 12: fb_writel(fb_readl(dest-12) ^ 0x0f0f0f0f, dest-12); /* fall thru */ + case 8: fb_writel(fb_readl(dest-8) ^ 0x0f0f0f0f, dest-8); /* fall thru */ + case 4: fb_writel(fb_readl(dest-4) ^ 0x0f0f0f0f, dest-4); /* fall thru */ + default: break; + } + } +/* if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ + } else { dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p); for (rows = fontheight(p) ; rows-- ; dest += bytes) { switch (fontwidth(p)) { @@ -229,6 +454,8 @@ default: break; } } + } +/* elseif ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) */ } void fbcon_cfb8_clear_margins(struct vc_data *conp, struct display *p, @@ -244,6 +471,9 @@ bgx=attr_bgcol_ec(p,conp); if (!bottom_only && (right_width = p->var.xres-right_start)) + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + printk("---@paul@------------------------- fbcon-cfb8 clear margins\n"); + } rectfill(p->screen_base+right_start, right_width, p->var.yres_virtual, bgx, bytes); if ((bottom_width = p->var.yres-bottom_start)) --- linux-2.4.21/drivers/video/fbcon.c~fb-turn180 +++ linux-2.4.21/drivers/video/fbcon.c @@ -1558,6 +1558,7 @@ update_region(fg_console, conp->vc_origin + conp->vc_size_row * conp->vc_top, conp->vc_size_row * (conp->vc_bottom - conp->vc_top) / 2); + conp->vc_top = 0; return 0; } return 1; @@ -2209,7 +2210,16 @@ src = logo; bdepth = depth/8; for( y1 = 0; y1 < LOGO_H; y1++ ) { - dst = fb + y1*line + x*bdepth; + + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { +/* + Das ist NICHT die richtige Stelle für den Ramses 16 BPP Modus + aber dafür die weiter unten. +*/ + dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; + } else { + dst = fb + y1*line + x*bdepth; + } for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { val = (*src << redshift) | (*src << greenshift) | @@ -2217,18 +2227,32 @@ if (bdepth == 4 && !((long)dst & 3)) { /* Some cards require 32bit access */ fb_writel (val, dst); - dst += 4; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst -= 4; + } else { + dst += 4; + } } else if (bdepth == 2 && !((long)dst & 1)) { /* others require 16bit access */ fb_writew (val,dst); - dst +=2; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst -= 2; + } else { + dst +=2; + } } else { + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + for( i = bdepth-1; i >= 0; --i ) + fb_writeb (val >> (i*8), dst--); + + } else { #ifdef __LITTLE_ENDIAN - for( i = 0; i < bdepth; ++i ) + for( i = 0; i < bdepth; ++i ) #else - for( i = bdepth-1; i >= 0; --i ) + for( i = bdepth-1; i >= 0; --i ) #endif - fb_writeb (val >> (i*8), dst++); + fb_writeb (val >> (i*8), dst++); + } } } } @@ -2239,28 +2263,42 @@ src = linux_logo16; bdepth = (depth+7)/8; for( y1 = 0; y1 < LOGO_H; y1++ ) { - dst = fb + y1*line + x*bdepth; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; + } else { + dst = fb + y1*line + x*bdepth; + } for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) { pix = *src >> 4; /* upper nibble */ val = (pix << redshift) | (pix << greenshift) | (pix << blueshift); + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + for( i = bdepth-1; i >= 0; --i ) + fb_writeb (val >> (i*8), dst--); + } else { #ifdef __LITTLE_ENDIAN - for( i = 0; i < bdepth; ++i ) + for( i = 0; i < bdepth; ++i ) #else - for( i = bdepth-1; i >= 0; --i ) + for( i = bdepth-1; i >= 0; --i ) #endif - fb_writeb (val >> (i*8), dst++); + fb_writeb (val >> (i*8), dst++); + } pix = *src & 0x0f; /* lower nibble */ val = (pix << redshift) | (pix << greenshift) | (pix << blueshift); + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + for( i = bdepth-1; i >= 0; --i ) + fb_writeb (val >> (i*8), dst--); + } else { #ifdef __LITTLE_ENDIAN - for( i = 0; i < bdepth; ++i ) + for( i = 0; i < bdepth; ++i ) #else - for( i = bdepth-1; i >= 0; --i ) + for( i = bdepth-1; i >= 0; --i ) #endif - fb_writeb (val >> (i*8), dst++); + fb_writeb (val >> (i*8), dst++); + } } } } @@ -2287,7 +2325,11 @@ src = logo; for( y1 = 0; y1 < LOGO_H; y1++ ) { - dst = fb + y1*line + x*bdepth; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst = fb + p->var.xres*p->var.yres*bdepth -1 - y1*line - x*bdepth; + } else { + dst = fb + y1*line + x*bdepth; + } for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { val = safe_shift((linux_logo_red[*src-32] & redmask), redshift) | safe_shift((linux_logo_green[*src-32] & greenmask), greenshift) | @@ -2295,18 +2337,31 @@ if (bdepth == 4 && !((long)dst & 3)) { /* Some cards require 32bit access */ fb_writel (val, dst); - dst += 4; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst -= 4; + } else { + dst += 4; + } } else if (bdepth == 2 && !((long)dst & 1)) { /* others require 16bit access */ fb_writew (val,dst); - dst +=2; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + dst -= 2; + } else { + dst +=2; + } } else { + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + for( i = bdepth-1; i >= 0; --i ) + fb_writeb (val >> (i*8), dst--); + } else { #ifdef __LITTLE_ENDIAN - for( i = 0; i < bdepth; ++i ) + for( i = 0; i < bdepth; ++i ) #else - for( i = bdepth-1; i >= 0; --i ) + for( i = bdepth-1; i >= 0; --i ) #endif - fb_writeb (val >> (i*8), dst++); + fb_writeb (val >> (i*8), dst++); + } } } } @@ -2331,13 +2386,24 @@ if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) { /* depth 8 or more, packed, with color registers */ - src = logo; - for( y1 = 0; y1 < LOGO_H; y1++ ) { - dst = fb + y1*line + x; - for( x1 = 0; x1 < LOGO_W; x1++ ) - fb_writeb (*src++, dst++); - } - done = 1; + if ( ramses_flags & RAMSES_FLAGS_LCD_FBTURN) { + src = logo; + for( y1 = 0; y1 < LOGO_H; y1++ ) + { + dst = fb + p->var.xres*p->var.yres -1 - y1*line - x; + for( x1 = 0; x1 < LOGO_W; x1++ ) + fb_writeb (*src++, dst--); + } + done = 1; + } else { + src = logo; + for( y1 = 0; y1 < LOGO_H; y1++ ) { + dst = fb + y1*line + x; + for( x1 = 0; x1 < LOGO_W; x1++ ) + fb_writeb (*src++, dst++); + } + done = 1; + } } #endif #if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \ --- linux-2.4.21/drivers/video/fbmem.c~fb-buffered +++ linux-2.4.21/drivers/video/fbmem.c @@ -302,7 +302,7 @@ { "sa1100", sa1100fb_init, NULL }, #endif #ifdef CONFIG_FB_PXA - { "pxa", pxafb_init, NULL }, + { "pxa", pxafb_init, NULL }, #endif #ifdef CONFIG_FB_SUN3 { "sun3", sun3fb_init, sun3fb_setup }, @@ -672,7 +672,11 @@ #elif defined(__hppa__) pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; #elif defined(__ia64__) || defined(__arm__) +#ifdef CONFIG_PXA + vma->vm_page_prot = pgprot_noncached_buffered(vma->vm_page_prot); +#else vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); +#endif #elif defined(__hppa__) pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; #else --- linux-2.4.21/drivers/video/pxafb.c~ramses-lcd +++ linux-2.4.21/drivers/video/pxafb.c @@ -45,8 +45,6 @@ #include