D: SRM environment driver (for Alpha systems)
P: 1024D/8399E1BB 250D 3BCF 7127 0D8C A444 A961 1DBD 5E75 8399 E1BB
+N: Thomas Gleixner
+E: tglx@linutronix.de
+D: NAND flash hardware support, JFFS2 on NAND flash
+
N: Richard E. Gooch
E: rgooch@atnf.csiro.au
D: parent process death signal to children
jmp $31, do_sys_ptrace
.end sys_ptrace
+ .align 4
+ .globl sys_execve
+ .ent sys_execve
+sys_execve:
+ .prologue 0
+ mov $sp, $19
+ jmp $31, do_sys_execve
+.end sys_execve
+
+ .align 4
+ .globl osf_sigprocmask
+ .ent osf_sigprocmask
+osf_sigprocmask:
+ .prologue 0
+ mov $sp, $18
+ jmp $31, do_osf_sigprocmask
+.end osf_sigprocmask
+
.align 4
.globl alpha_ni_syscall
.ent alpha_ni_syscall
/*
* sys_execve() executes a new program.
- *
- * This works due to the alpha calling sequence: the first 6 args
- * are gotten from registers, while the rest is on the stack, so
- * we get a0-a5 for free, and then magically find "struct pt_regs"
- * on the stack for us..
- *
- * Don't do this at home.
*/
asmlinkage int
-sys_execve(char __user *ufilename, char __user * __user *argv,
- char __user * __user *envp,
- unsigned long a3, unsigned long a4, unsigned long a5,
- struct pt_regs regs)
+do_sys_execve(char __user *ufilename, char __user * __user *argv,
+ char __user * __user *envp, struct pt_regs *regs)
{
int error;
char *filename;
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
- error = do_execve(filename, argv, envp, ®s);
+ error = do_execve(filename, argv, envp, regs);
putname(filename);
out:
return error;
* operation, as all of this is local to this thread.
*/
asmlinkage unsigned long
-osf_sigprocmask(int how, unsigned long newmask, long a2, long a3,
- long a4, long a5, struct pt_regs regs)
+do_osf_sigprocmask(int how, unsigned long newmask, struct pt_regs *regs)
{
unsigned long oldmask = -EINVAL;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- (®s)->r0 = 0; /* special no error return */
+ regs->r0 = 0; /* special no error return */
}
return oldmask;
}
More information is available at:
<http://linux-apus.sourceforge.net/>.
+config KATANA
+ bool "Artesyn-Katana"
+
+config DMV182
+ bool "Dy-4 SVME/DMV-182"
+
config WILLOW
bool "Cogent-Willow"
bool "Force-PowerPMC250"
config EV64260
- bool "Galileo-EV-64260-BP"
+ bool "Marvell-EV64260BP"
+ help
+ Select EV64260 if configuring of a Marvell (formerly Galileo)
+ EV64260BP Evaluation platofm.
config SPRUCE
bool "IBM-Spruce"
depends on PPC_PMAC || PPC_CHRP
default y
+menu "Set bridge options"
+ depends on MV64X60
+
+config MV64X60_BASE
+ hex "Set bridge base used by firmware"
+ default "0xf1000000"
+ help
+ A firmware can leave the base address of the bridge's registers at
+ a non-standard location. If so, set this value to reflect the
+ address of that non-standard location.
+
+config MV64X60_NEW_BASE
+ hex "Set bridge base used by kernel"
+ default "0xf1000000"
+ help
+ If the current base address of the bridge's registers is not where
+ you want it, set this value to the address that you want it moved to.
+
+endmenu
+
config PPC_GEN550
bool
- depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800
+ depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800 || (EV64260 && !MV64X60_MPSC) || DMV182
default y
config FORCE
depends on EV64260
default y
+config MV64360
+ bool
+ depends on KATANA || DMV182
+ default y
+
+config MV64X60
+ bool
+ depends on (GT64260 || MV64360)
+ default y
+
config NONMONARCH_SUPPORT
bool "Enable Non-Monarch Support"
depends on PRPMC800
depends on 8xx
default y
-config SERIAL_CONSOLE_BAUD
- int
- depends on EV64260
- default "115200"
-
config PPCBUG_NVRAM
bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC
default y if PPC_PREP
config SERIAL_TEXT_DEBUG
bool "Support for early boot texts over serial port"
- depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550
+ depends on 4xx || LOPEC || MV64X60 || PPLUS || PRPMC800 || PPC_GEN550
config PPC_OCP
bool
- depends on IBM_OCP || FSL_OCP
+ depends on IBM_OCP || FSL_OCP || MV64X60
default y
endmenu
void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap);
unsigned char *ISA_io = NULL;
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \
+ defined(CONFIG_SERIAL_MPSC_CONSOLE)
extern unsigned long com_port;
extern int serial_tstc(unsigned long com_port);
int tstc(void)
{
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \
+ defined(CONFIG_SERIAL_MPSC_CONSOLE)
if(keyb_present)
return (CRT_tstc() || serial_tstc(com_port));
else
int getc(void)
{
while (1) {
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \
+ defined(CONFIG_SERIAL_MPSC_CONSOLE)
if (serial_tstc(com_port))
return (serial_getc(com_port));
#endif /* serial console */
{
int x,y;
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \
+ defined(CONFIG_SERIAL_MPSC_CONSOLE)
serial_putc(com_port, c);
if ( c == '\n' )
serial_putc(com_port, '\r');
y = orig_y;
while ( ( c = *s++ ) != '\0' ) {
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \
+ defined(CONFIG_SERIAL_MPSC_CONSOLE)
serial_putc(com_port, c);
if ( c == '\n' ) serial_putc(com_port, '\r');
#endif /* serial console */
end-$(CONFIG_OCOTEA) := ocotea
entrypoint-$(CONFIG_OCOTEA) := 0x01000000
- extra.o-$(CONFIG_EV64260) := direct.o misc-ev64260.o
+ extra.o-$(CONFIG_EV64260) := misc-ev64260.o
end-$(CONFIG_EV64260) := ev64260
cacheflag-$(CONFIG_EV64260) := -include $(clear_L2_L3)
boot-$(CONFIG_8260) += embed_config.o
boot-$(CONFIG_BSEIP) += iic.o
boot-$(CONFIG_MBX) += iic.o pci.o qspan_pci.o
+boot-$(CONFIG_MV64X60) += misc-mv64x60.o
+boot-$(CONFIG_DMV182) += mv64x60_stub.o
boot-$(CONFIG_RPXCLASSIC) += iic.o pci.o qspan_pci.o
boot-$(CONFIG_RPXLITE) += iic.o
# Different boards need different serial implementations.
ifeq ($(CONFIG_SERIAL_CONSOLE),y)
boot-$(CONFIG_8xx) += m8xx_tty.o
boot-$(CONFIG_8260) += m8260_tty.o
-boot-$(CONFIG_GT64260_CONSOLE) += gt64260_tty.o
endif
+boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o
LIBS := $(common)/lib.a $(bootlib)/lib.a
ifeq ($(CONFIG_PPC_PREP),y)
*/
#endif
-#ifdef CONFIG_EV64260
- /* Move 64260's base regs & CS window for external UART */
- bl ev64260_init
+#ifdef CONFIG_MV64X60
+ /* mv64x60 specific hook to do things like moving register base, etc. */
+ bl mv64x60_init
#endif
/* Get the load address.
/*
* arch/ppc/boot/simple/misc-ev64260.S
- *
+ *
* Host bridge init code for the Marvell/Galileo EV-64260-BP evaluation board
* with a GT64260 onboard.
*
* Author: Mark Greer <mgreer@mvista.com>
*
- * 2001 (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.
+ * Copyright 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
*/
#include <asm/ppc_asm.h>
+#include <asm/processor.h>
#include <asm/cache.h>
-#include <asm/gt64260_defs.h>
-
+#include <asm/mv64x60_defs.h>
#include <platforms/ev64260.h>
- .globl ev64260_init
-ev64260_init:
- li r20,0
+ .globl mv64x60_board_init
+mv64x60_board_init:
+ /* DINK doesn't enable 745x timebase, so enable here (Adrian Cox) */
+ mfspr r25,PVR
+ srwi r25,r25,16
+ cmplwi r25,(PVR_7450 >> 16)
+ bne 1f
+ mfspr r25,HID0
+ oris r25,r25,(HID0_TBEN >> 16)
+ mtspr HID0,r25
+1:
+#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE)
li r23,20
- /* Relocate galileo's regs */
- addis r25,0,GT64260_INTERNAL_SPACE_DEFAULT_ADDR@h
- ori r25,r25,GT64260_INTERNAL_SPACE_DECODE
- lwbrx r26,0,(r25)
- lis r24,0xffff
- and r26,r26,r24
- addis r24,0,EV64260_BRIDGE_REG_BASE@h
- srw r24,r24,r23
- or r26,r26,r24
- stwbrx r26,0,(r25)
- sync
-
- /* Wait for write to take effect */
- addis r25,0,EV64260_BRIDGE_REG_BASE@h
- ori r25,r25,GT64260_INTERNAL_SPACE_DECODE
-1: lwbrx r24,0,(r25)
- cmpw r24,r26
- bne 1b
-
- /* Change CS2 (UARTS on device module) window */
- addis r25,0,EV64260_BRIDGE_REG_BASE@h
- ori r25,r25,GT64260_CPU_CS_DECODE_2_BOT
+ /*
+ * Change the CS2 window for the UART so that the bootloader
+ * can do I/O thru the UARTs.
+ */
+ addis r25,0,CONFIG_MV64X60_NEW_BASE@h
+ ori r25,r25,MV64x60_CPU2DEV_2_BASE
addis r26,0,EV64260_UART_BASE@h
srw r26,r26,r23
stwbrx r26,0,(r25)
sync
- addis r25,0,EV64260_BRIDGE_REG_BASE@h
- ori r25,r25,GT64260_CPU_CS_DECODE_2_TOP
+ addis r25,0,CONFIG_MV64X60_NEW_BASE@h
+ ori r25,r25,MV64x60_CPU2DEV_2_SIZE
addis r26,0,EV64260_UART_END@h
srw r26,r26,r23
stwbrx r26,0,(r25)
sync
-
+#endif
blr
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+.data
+ .globl mv64x60_console_baud
+mv64x60_console_baud:
+.long EV64260_DEFAULT_BAUD
+
+ .globl mv64x60_mpsc_clk_src
+mv64x60_mpsc_clk_src:
+.long EV64260_MPSC_CLK_SRC
+
+ .globl mv64x60_mpsc_clk_freq
+mv64x60_mpsc_clk_freq:
+.long EV64260_MPSC_CLK_FREQ
+#endif
* user to edit the cmdline or not.
*/
#if (defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_VGA_CONSOLE)) \
- && !defined(CONFIG_GEMINI)
+ && !defined(CONFIG_GEMINI) || defined(CONFIG_SERIAL_MPSC_CONSOLE)
#define INTERACTIVE_CONSOLE 1
#endif
unsigned long initrd_loc, TotalMemory = 0;
serial_fixups();
-#ifdef CONFIG_SERIAL_8250_CONSOLE
+#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_MPSC_CONSOLE)
com_port = serial_init(0, NULL);
#endif
CONFIG_MMU=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_STANDALONE=y
+CONFIG_BROKEN_ON_SMP=y
#
# General setup
#
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+# CONFIG_IKCONFIG is not set
# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
#
# Loadable module support
CONFIG_KMOD=y
#
-# Platform support
+# Processor
#
-CONFIG_PPC=y
-CONFIG_PPC32=y
CONFIG_6xx=y
# CONFIG_40x is not set
+# CONFIG_44x is not set
# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
# CONFIG_8xx is not set
+CONFIG_ALTIVEC=y
+# CONFIG_TAU is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_STD_MMU=y
#
-# IBM 4xx options
+# Platform options
#
-# CONFIG_8260 is not set
-CONFIG_GENERIC_ISA_DMA=y
-CONFIG_PPC_STD_MMU=y
# CONFIG_PPC_MULTIPLATFORM is not set
# CONFIG_APUS is not set
-# CONFIG_WILLOW_2 is not set
+# CONFIG_KATANA is not set
+# CONFIG_WILLOW is not set
# CONFIG_PCORE is not set
# CONFIG_POWERPMC250 is not set
CONFIG_EV64260=y
# CONFIG_K2 is not set
# CONFIG_PAL4 is not set
# CONFIG_GEMINI is not set
+# CONFIG_EST8260 is not set
+# CONFIG_SBS8260 is not set
+# CONFIG_RPX6 is not set
+# CONFIG_TQM8260 is not set
+
+#
+# Set bridge base address
+#
+CONFIG_MV64X60_BASE=0xf1000000
+CONFIG_MV64X60_NEW_BASE=0xfbe00000
+CONFIG_PPC_GEN550=y
CONFIG_GT64260=y
+CONFIG_MV64X60=y
CONFIG_SERIAL_CONSOLE_BAUD=115200
# CONFIG_SMP is not set
# CONFIG_PREEMPT is not set
-CONFIG_ALTIVEC=y
-CONFIG_TAU=y
-# CONFIG_TAU_INT is not set
-# CONFIG_TAU_AVERAGE is not set
-# CONFIG_CPU_FREQ is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_KERNEL_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0,115200 ip=on"
#
-# General setup
+# Bus options
#
-# CONFIG_HIGHMEM is not set
+CONFIG_GENERIC_ISA_DMA=y
CONFIG_PCI=y
CONFIG_PCI_DOMAINS=y
-CONFIG_KCORE_ELF=y
-CONFIG_BINFMT_ELF=y
-CONFIG_KERNEL_ELF=y
-CONFIG_BINFMT_MISC=y
CONFIG_PCI_LEGACY_PROC=y
CONFIG_PCI_NAMES=y
-# CONFIG_HOTPLUG is not set
-
-#
-# Parallel port support
-#
-# CONFIG_PARPORT is not set
-# CONFIG_PPC601_SYNC_FIX is not set
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="console=ttyS0,115200 ip=on"
#
# Advanced setup
CONFIG_TASK_SIZE=0x80000000
CONFIG_BOOT_LOAD=0x00800000
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+
#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
#
# Plug and Play support
#
-# CONFIG_PNP is not set
#
# Block devices
# CONFIG_BLK_DEV_DAC960 is not set
# CONFIG_BLK_DEV_UMEM is not set
CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_CARMEL is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
#
-# Multi-device support (RAID and LVM)
+# ATA/ATAPI/MFM/RLL support
#
-# CONFIG_MD is not set
+# CONFIG_IDE is not set
#
-# ATA/IDE/MFM/RLL support
+# SCSI device support
#
-# CONFIG_IDE is not set
+# CONFIG_SCSI is not set
#
-# SCSI support
+# Multi-device support (RAID and LVM)
#
-# CONFIG_SCSI is not set
+# CONFIG_MD is not set
#
# Fusion MPT device support
#
#
-# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+# IEEE 1394 (FireWire) support
#
# CONFIG_IEEE1394 is not set
#
# CONFIG_I2O is not set
+#
+# Macintosh device drivers
+#
+
#
# Networking support
#
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
# CONFIG_NETLINK_DEV is not set
-CONFIG_NETFILTER=y
-# CONFIG_NETFILTER_DEBUG is not set
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_ARPD is not set
-# CONFIG_INET_ECN is not set
CONFIG_SYN_COOKIES=y
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
-
-#
-# IP: Netfilter Configuration
-#
-# CONFIG_IP_NF_CONNTRACK is not set
-# CONFIG_IP_NF_QUEUE is not set
-# CONFIG_IP_NF_IPTABLES is not set
-# CONFIG_IP_NF_ARPTABLES is not set
-# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
-# CONFIG_IP_NF_COMPAT_IPFWADM is not set
# CONFIG_IPV6 is not set
-# CONFIG_XFRM_USER is not set
+# CONFIG_NETFILTER is not set
#
# SCTP Configuration (EXPERIMENTAL)
#
-CONFIG_IPV6_SCTP__=y
# CONFIG_IP_SCTP is not set
# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
-# CONFIG_LLC is not set
# CONFIG_DECNET is not set
-# CONFIG_BRIDGE is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_NET_DIVERT is not set
# Network testing
#
# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
#
# 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_MII is not set
+CONFIG_MII=y
# CONFIG_OAKNET is not set
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNGEM is not set
#
# Tulip family network device support
#
-# CONFIG_NET_TULIP is not set
+CONFIG_NET_TULIP=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 is not set
# CONFIG_HP100 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
# CONFIG_DGRS is not set
CONFIG_EEPRO100=y
# CONFIG_EEPRO100_PIO is not set
# Ethernet (10000 Mbit)
#
# CONFIG_IXGB is not set
-# CONFIG_FDDI is not set
-# CONFIG_HIPPI is not set
-# CONFIG_PPP is not set
-# CONFIG_SLIP is not set
+# CONFIG_S2IO is not set
#
-# Wireless LAN (non-hamradio)
+# Token Ring devices
#
-# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
#
-# Token Ring devices (depends on LLC=y)
+# Wireless LAN (non-hamradio)
#
-# CONFIG_RCPCI is not set
-# CONFIG_SHAPER is not set
+# CONFIG_NET_RADIO is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
-
-#
-# Amateur Radio support
-#
-# CONFIG_HAMRADIO is not set
-
-#
-# IrDA (infrared) support
-#
-# CONFIG_IRDA is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
#
# ISDN subsystem
#
-# CONFIG_ISDN_BOOL is not set
-
-#
-# Graphics support
-#
-# CONFIG_FB is not set
+# CONFIG_ISDN is not set
#
-# Old CD-ROM drivers (not SCSI, not IDE)
+# Telephony Support
#
-# CONFIG_CD_NO_IDESCSI is not set
+# CONFIG_PHONE is not set
#
# Input device support
#
-# CONFIG_INPUT is not set
+CONFIG_INPUT=y
#
# Userland interfaces
#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
#
# Input I/O drivers
# CONFIG_GAMEPORT is not set
CONFIG_SOUND_GAMEPORT=y
# CONFIG_SERIO is not set
+# CONFIG_SERIO_I8042 is not set
#
# Input Device Drivers
#
-
-#
-# Macintosh device drivers
-#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
#
# Character devices
#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
# CONFIG_SERIAL_NONSTANDARD is not set
#
#
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
# CONFIG_SERIAL_8250_EXTENDED is not set
#
# Non-8250 serial port support
#
+# CONFIG_SERIAL_MPSC is not set
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
-CONFIG_UNIX98_PTY_COUNT=256
-
-#
-# I2C support
-#
-CONFIG_I2C=m
-# CONFIG_I2C_ALGOBIT is not set
-# CONFIG_I2C_ALGOPCF is not set
-CONFIG_I2C_CHARDEV=m
-
-#
-# I2C Hardware Sensors Mainboard support
-#
-# CONFIG_I2C_ALI15X3 is not set
-# CONFIG_I2C_AMD756 is not set
-# CONFIG_I2C_AMD8111 is not set
-# CONFIG_I2C_I801 is not set
-# CONFIG_I2C_PIIX4 is not set
-# CONFIG_I2C_SIS96X is not set
-# CONFIG_I2C_VIAPRO is not set
-
-#
-# I2C Hardware Sensors Chip support
-#
-# CONFIG_SENSORS_ADM1021 is not set
-# CONFIG_SENSORS_IT87 is not set
-# CONFIG_SENSORS_LM75 is not set
-# CONFIG_SENSORS_LM85 is not set
-# CONFIG_SENSORS_VIA686A is not set
-# CONFIG_SENSORS_W83781D is not set
-# CONFIG_I2C_SENSOR is not set
-
-#
-# Mice
-#
-# CONFIG_BUSMOUSE is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_QIC02_TAPE is not set
#
# CONFIG_AGP is not set
# CONFIG_DRM is not set
# CONFIG_RAW_DRIVER is not set
-# CONFIG_HANGCHECK_TIMER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Misc devices
+#
#
# Multimedia devices
#
# CONFIG_DVB is not set
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
#
# File systems
#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
CONFIG_DEVFS_FS=y
# CONFIG_DEVFS_MOUNT is not set
# CONFIG_DEVFS_DEBUG is not set
-CONFIG_DEVPTS_FS=y
# CONFIG_DEVPTS_FS_XATTR is not set
CONFIG_TMPFS=y
+# CONFIG_HUGETLB_PAGE is not set
CONFIG_RAMFS=y
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
# CONFIG_NFSD is not set
CONFIG_ROOT_NFS=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
# CONFIG_EXPORTFS is not set
CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_GSS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
-# CONFIG_INTERMEZZO_FS is not set
# CONFIG_AFS_FS is not set
#
CONFIG_MSDOS_PARTITION=y
#
-# Sound
+# Native Language Support
#
-# CONFIG_SOUND is not set
-
-#
-# USB support
-#
-# CONFIG_USB is not set
-# CONFIG_USB_GADGET is not set
-
-#
-# Bluetooth support
-#
-# CONFIG_BT is not set
+# CONFIG_NLS is not set
#
# Library routines
#
-# CONFIG_CRC32 is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
#
# Kernel hacking
#
# CONFIG_DEBUG_KERNEL is not set
-# CONFIG_KALLSYMS is not set
# CONFIG_SERIAL_TEXT_DEBUG is not set
+CONFIG_PPC_OCP=y
#
# Security options
#include <linux/tty.h> /* for linux/serial_core.h */
#include <linux/serial_core.h>
#include <linux/module.h>
+#include <linux/initrd.h>
#include <asm/system.h>
#include <asm/pgtable.h>
uart_req.iotype = SERIAL_IO_MEM;
uart_req.mapbase = UARTA_ADDR;
uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE);
+ uart_req.type = PORT_16650;
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
gen550_init(0, &uart_req);
#define BASE_BAUD ( 1843200 / 16 )
#ifdef CONFIG_SERIAL_DETECT_IRQ
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
+#define STD_COM_FLAGS (ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
#else
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
+#define STD_COM_FLAGS (ASYNC_SKIP_TEST)
#endif
-
+
#define STD_SERIAL_PORT_DFNS \
{ 0, BASE_BAUD, UARTA_ADDR, MPC85xx_IRQ_EXT9, STD_COM_FLAGS, /* ttyS0 */ \
iomem_base: (u8 *)UARTA_ADDR, \
/* Internal interrupts are all Level Sensitive, and Positive Polarity */
static u_char sbc8560_openpic_initsenses[] __initdata = {
- (IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
- (IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
- (IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
- (IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
- (IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
- (IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
- (IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
- (IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
- (IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
- (IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
- (IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
- (IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
- (IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
- (IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
- (IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
- (IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
- (IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
- (IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
- (IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
- (IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
- (IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
- (IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
0x0, /* External 0: */
0x0, /* External 1: */
#if defined(CONFIG_PCI)
obj-$(CONFIG_EST8260) += est8260_setup.o
obj-$(CONFIG_PQ2ADS) += pq2ads_setup.o
obj-$(CONFIG_TQM8260) += tqm8260_setup.o
-obj-$(CONFIG_EV64260) += ev64260_setup.o
+obj-$(CONFIG_EV64260) += ev64260.o
+obj-$(CONFIG_DMV182) += dmv182.o
obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o
obj-$(CONFIG_K2) += k2.o
obj-$(CONFIG_LOPEC) += lopec_setup.o lopec_pci.o
/*
* arch/ppc/platforms/ev64260.h
- *
+ *
* Definitions for Marvell/Galileo EV-64260-BP Evaluation Board.
*
* Author: Mark A. Greer <mgreer@mvista.com>
*
- * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * 2001-2002 (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.
*/
/*
- * The GT64260 has 2 PCI buses each with 1 window from the CPU bus to
+ * The MV64x60 has 2 PCI buses each with 1 window from the CPU bus to
* PCI I/O space and 4 windows from the CPU bus to PCI MEM space.
* We'll only use one PCI MEM window on each PCI bus.
+ *
+ * This is the CPU physical memory map (windows must be at least 1MB and start
+ * on a boundary that is a multiple of the window size):
+ *
+ * 0xfc000000-0xffffffff - External FLASH on device module
+ * 0xfbf00000-0xfbffffff - Embedded (on board) FLASH
+ * 0xfbe00000-0xfbefffff - GT64260 Registers (preferably)
+ * but really a config option
+ * 0xfbd00000-0xfbdfffff - External SRAM on device module
+ * 0xfbc00000-0xfbcfffff - TODC chip on device module
+ * 0xfbb00000-0xfbbfffff - External UART on device module
+ * 0xa2000000-0xfbafffff - <hole>
+ * 0xa1000000-0xa1ffffff - PCI 1 I/O (defined in gt64260.h)
+ * 0xa0000000-0xa0ffffff - PCI 0 I/O (defined in gt64260.h)
+ * 0x90000000-0x9fffffff - PCI 1 MEM (defined in gt64260.h)
+ * 0x80000000-0x8fffffff - PCI 0 MEM (defined in gt64260.h)
*/
#ifndef __PPC_PLATFORMS_EV64260_H
#define __PPC_PLATFORMS_EV64260_H
-#define EV64260_BRIDGE_REG_BASE 0xf8000000
-#define EV64260_BRIDGE_REG_BASE_TO_TOP 0x08000000U
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/*
+ * CPU Physical Memory Map setup.
+ */
+#define EV64260_EXT_FLASH_BASE 0xfc000000
+#define EV64260_EMB_FLASH_BASE 0xfbf00000
+#define EV64260_EXT_SRAM_BASE 0xfbd00000
+#define EV64260_TODC_BASE 0xfbc00000
+#define EV64260_UART_BASE 0xfbb00000
-#define EV64260_TODC_BASE 0xfc800000
-#define EV64260_TODC_LEN 0x00800000
-#define EV64260_TODC_END (EV64260_TODC_BASE + \
- EV64260_TODC_LEN - 1)
+#define EV64260_EXT_FLASH_SIZE_ACTUAL 0x04000000 /* <= 64MB Extern FLASH */
+#define EV64260_EMB_FLASH_SIZE_ACTUAL 0x00080000 /* 512KB of Embed FLASH */
+#define EV64260_EXT_SRAM_SIZE_ACTUAL 0x00100000 /* 1MB SDRAM */
+#define EV64260_TODC_SIZE_ACTUAL 0x00000020 /* 32 bytes for TODC */
+#define EV64260_UART_SIZE_ACTUAL 0x00000040 /* 64 bytes for DUART */
+
+#define EV64260_EXT_FLASH_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \
+ EV64260_EXT_FLASH_SIZE_ACTUAL)
+#define EV64260_EMB_FLASH_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \
+ EV64260_EMB_FLASH_SIZE_ACTUAL)
+#define EV64260_EXT_SRAM_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \
+ EV64260_EXT_SRAM_SIZE_ACTUAL)
+#define EV64260_TODC_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \
+ EV64260_TODC_SIZE_ACTUAL)
+#if 0 /* XXXX blows up assembler in bootloader */
+#define EV64260_UART_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \
+ EV64260_UART_SIZE_ACTUAL)
+#else
+#define EV64260_UART_SIZE GT64260_WINDOW_SIZE_MIN
+#endif
+#define EV64260_UART_END ((EV64260_UART_BASE + \
+ EV64260_UART_SIZE - 1) & 0xfff00000)
+
+/*
+ * Board-specific IRQ info
+ */
+#define EV64260_UART_0_IRQ 85
+#define EV64260_UART_1_IRQ 86
+#define EV64260_PCI_0_IRQ 91
+#define EV64260_PCI_1_IRQ 93
-#define EV64260_UART_BASE 0xfd000000
-#define EV64260_UART_LEN 0x00800000
-#define EV64260_UART_END (EV64260_UART_BASE + \
- EV64260_UART_LEN - 1)
-/* Serial driver setup. */
+/*
+ * Serial port setup.
+ */
+#define EV64260_DEFAULT_BAUD 115200
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+#define SERIAL_PORT_DFNS
+
+#define EV64260_MPSC_CLK_SRC 8 /* TCLK */
+#define EV64260_MPSC_CLK_FREQ 100000000 /* 100MHz clk */
+#else
#define EV64260_SERIAL_0 (EV64260_UART_BASE + 0x20)
#define EV64260_SERIAL_1 EV64260_UART_BASE
-#define BASE_BAUD ( 3686400 / 16 )
+#define BASE_BAUD (EV64260_DEFAULT_BAUD * 2)
#ifdef CONFIG_SERIAL_MANY_PORTS
#define RS_TABLE_SIZE 64
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
#endif
-#if !defined(CONFIG_GT64260_CONSOLE)
/* Required for bootloader's ns16550.c code */
#define STD_SERIAL_PORT_DFNS \
- { 0, BASE_BAUD, EV64260_SERIAL_0, 85, STD_COM_FLAGS, /* ttyS0 */\
- iomem_base: (u8 *)EV64260_SERIAL_0, \
+ { 0, BASE_BAUD, EV64260_SERIAL_0, EV64260_UART_0_IRQ, STD_COM_FLAGS, \
+ iomem_base: (u8 *)EV64260_SERIAL_0, /* ttyS0 */ \
+ iomem_reg_shift: 2, \
+ io_type: SERIAL_IO_MEM },
+
+#if 0
+ { 1, BASE_BAUD, EV64260_SERIAL_1, EV64260_UART_1_IRQ, STD_COM_FLAGS, \
+ iomem_base: (u8 *)EV64260_SERIAL_1, /* ttyS1 */ \
iomem_reg_shift: 2, \
io_type: SERIAL_IO_MEM },
+#endif
#define SERIAL_PORT_DFNS \
STD_SERIAL_PORT_DFNS
-#else
-#define SERIAL_PORT_DFNS
#endif
-
#endif /* __PPC_PLATFORMS_EV64260_H */
obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \
todc_time.o
obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o
-obj-$(CONFIG_EV64260) += gt64260_common.o gt64260_pic.o \
- indirect_pci.o todc_time.o pci_auto.o
+obj-$(CONFIG_EV64260) += indirect_pci.o todc_time.o pci_auto.o
+obj-$(CONFIG_DMV182) += indirect_pci.o todc_time.o pci_auto.o
obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o
+obj-$(CONFIG_GT64260) += gt64260_pic.o
obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \
pci_auto.o
obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o
open_pic.o i8259.o hawk_common.o
obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \
pci_auto.o indirect_pci.o
+obj-$(CONFIG_MV64360) += mv64360_pic.o
+obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_ocp.o
obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \
i8259.o pci_auto.o hawk_common.o
obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o
*
* Interrupt controller support for Galileo's GT64260.
*
- * Author: Chris Zankel <chris@mvista.com>
+ * Author: Chris Zankel <source@mvista.com>
* Modified by: Mark A. Greer <mgreer@mvista.com>
*
* Based on sources from Rabeeh Khoury / Galileo Technology
#include <asm/io.h>
#include <asm/system.h>
#include <asm/irq.h>
-#include <asm/gt64260.h>
+#include <asm/ocp.h>
+#include <asm/mv64x60.h>
/* ========================== forward declaration ========================== */
u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */
+static mv64x60_handle_t base_bh;
+static mv64x60_handle_t ic_bh;
+
/* gt64260_init_irq()
*
* This function initializes the interrupt controller. It assigns
__init void
gt64260_init_irq(void)
{
+ struct ocp_def *def;
int i;
+/* XXXX extract reg base, irq base from ocp */
+/* XXXX rewrite read/write macros to not use 'bh'?? */
+/* XXXX Have to use ocp b/c can pass arg to this routine */
+
if ( ppc_md.progress ) ppc_md.progress("gt64260_init_irq: enter", 0x0);
+ if ((def = ocp_get_one_device(OCP_VENDOR_MARVELL, OCP_FUNC_HB,
+ OCP_ANY_INDEX)) == NULL) {
+ /* XXXX SCREAM */
+ return;
+ }
+ base_bh.v_base = (u32)ioremap(def->paddr, 0x10000); /* XXXX */
+
+ if ((def = ocp_get_one_device(OCP_VENDOR_MARVELL, OCP_FUNC_PIC,
+ OCP_ANY_INDEX)) == NULL) {
+ /* XXXX SCREAM */
+ return;
+ }
+ ic_bh.v_base = (u32)ioremap(def->paddr, 0x1000); /* XXXX */
+
ppc_cached_irq_mask[0] = 0;
ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */
ppc_cached_irq_mask[2] = 0;
/* disable all interrupts and clear current interrupts */
- gt_write(GT64260_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
- gt_write(GT64260_GPP_INTR_CAUSE,0);
- gt_write(GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]);
- gt_write(GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]);
+ mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
+ mv64x60_write(&base_bh, MV64x60_GPP_INTR_CAUSE,0);
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]);
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]);
/* use the gt64260 for all (possible) interrupt sources */
for( i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++ ) {
}
-/* gt64260_get_irq()
+/*
+ * gt64260_get_irq()
*
* This function returns the lowest interrupt number of all interrupts that
* are currently asserted.
int irq;
int irq_gpp;
- irq = gt_read(GT64260_IC_MAIN_CAUSE_LO);
+ irq = mv64x60_read(&ic_bh, GT64260_IC_MAIN_CAUSE_LO);
irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
if (irq == -1) {
- irq = gt_read(GT64260_IC_MAIN_CAUSE_HI);
+ irq = mv64x60_read(&ic_bh, GT64260_IC_MAIN_CAUSE_HI);
irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]);
if (irq == -1) {
irq = -2; /* bogus interrupt, should never happen */
} else {
if (irq >= 24) {
- irq_gpp = gt_read(GT64260_GPP_INTR_CAUSE);
+ irq_gpp = mv64x60_read(&base_bh, MV64x60_GPP_INTR_CAUSE);
irq_gpp = __ilog2(irq_gpp &
ppc_cached_irq_mask[2]);
irq = -2;
} else {
irq = irq_gpp + 64;
- gt_write(GT64260_GPP_INTR_CAUSE, ~(1<<(irq-64)));
+ mv64x60_write(&base_bh, MV64x60_GPP_INTR_CAUSE, ~(1<<(irq-64)));
}
} else {
irq += 32;
static void
gt64260_unmask_irq(unsigned int irq)
{
+ /* XXXX
+ printk("XXXX: *** unmask irq: %d\n", irq);
+ */
irq -= gt64260_irq_base;
if (irq > 31) {
if (irq > 63) {
/* unmask GPP irq */
- gt_write(GT64260_GPP_INTR_MASK,
+ mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] |= (1<<(irq-64)));
} else {
/* mask high interrupt register */
- gt_write(GT64260_IC_CPU_INTR_MASK_HI,
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI,
ppc_cached_irq_mask[1] |= (1<<(irq-32)));
}
} else {
/* mask low interrupt register */
- gt_write(GT64260_IC_CPU_INTR_MASK_LO,
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO,
ppc_cached_irq_mask[0] |= (1<<irq));
}
}
static void
gt64260_mask_irq(unsigned int irq)
{
+ /* XXXX
+ printk("XXXX: *** mask irq: %d\n", irq);
+ */
irq -= gt64260_irq_base;
if (irq > 31) {
if (irq > 63) {
/* mask GPP irq */
- gt_write(GT64260_GPP_INTR_MASK,
+ mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK,
ppc_cached_irq_mask[2] &= ~(1<<(irq-64)));
} else {
/* mask high interrupt register */
- gt_write(GT64260_IC_CPU_INTR_MASK_HI,
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI,
ppc_cached_irq_mask[1] &= ~(1<<(irq-32)));
}
} else {
/* mask low interrupt register */
- gt_write(GT64260_IC_CPU_INTR_MASK_LO,
+ mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO,
ppc_cached_irq_mask[0] &= ~(1<<irq));
}
/* Timer Interrupts */
static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
-static void openpic_maptimer(u_int timer, u_int cpumask);
+static void openpic_maptimer(u_int timer, cpumask_t cpumask);
/* Interrupt Sources */
static void openpic_enable_irq(u_int irq);
static void openpic_disable_irq(u_int irq);
static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
int is_level);
-static void openpic_mapirq(u_int irq, u_int cpumask, u_int keepmask);
+static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask);
/*
* These functions are not used but the code is kept here
*/
static void openpic_end_irq(unsigned int irq_nr);
static void openpic_ack_irq(unsigned int irq_nr);
-static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
+static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask);
struct hw_interrupt_type open_pic = {
- " OpenPIC ",
- NULL,
- NULL,
- openpic_enable_irq,
- openpic_disable_irq,
- openpic_ack_irq,
- openpic_end_irq,
- openpic_set_affinity
+ .typename = " OpenPIC ",
+ .enable = openpic_enable_irq,
+ .disable = openpic_disable_irq,
+ .ack = openpic_ack_irq,
+ .end = openpic_end_irq,
+ .set_affinity = openpic_set_affinity,
};
#ifdef CONFIG_SMP
static void openpic_disable_ipi(unsigned int irq_nr);
struct hw_interrupt_type open_pic_ipi = {
- " OpenPIC ",
- NULL,
- NULL,
- openpic_enable_ipi,
- openpic_disable_ipi,
- openpic_ack_ipi,
- openpic_end_ipi,
- 0
+ .typename = " OpenPIC ",
+ .enable = openpic_enable_ipi,
+ .disable = openpic_disable_ipi,
+ .ack = openpic_ack_ipi,
+ .end = openpic_end_ipi,
};
#endif /* CONFIG_SMP */
/* Disabled, Priority 0 */
openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset);
/* No processor */
- openpic_maptimer(i, 0);
+ openpic_maptimer(i, CPU_MASK_NONE);
}
#ifdef CONFIG_SMP
openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK),
(sense & IRQ_SENSE_MASK));
/* Processor 0 */
- openpic_mapirq(i, 1<<0, 0);
+ openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE);
}
/* Init descriptors */
/*
* Convert a cpu mask from logical to physical cpu numbers.
*/
-static inline u32 physmask(u32 cpumask)
+static inline cpumask_t physmask(cpumask_t cpumask)
{
int i;
- u32 mask = 0;
+ cpumask_t mask = CPU_MASK_NONE;
+
+ cpus_and(cpumask, cpu_online_map, cpumask);
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (cpu_isset(i, cpumask))
+ cpu_set(smp_hw_index[i], mask);
- for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
- if (cpu_online(i))
- mask |= (cpumask & 1) << smp_hw_index[i];
return mask;
}
#else
{
#ifdef CONFIG_IRQ_ALL_CPUS
int i;
- u32 msk;
+ cpumask_t msk = CPU_MASK_NONE;
#endif
spin_lock(&openpic_setup_lock);
#ifdef CONFIG_IRQ_ALL_CPUS
- msk = 1 << smp_hw_index[smp_processor_id()];
+ cpu_set(smp_hw_index[smp_processor_id()], mask);
/* let the openpic know we want intrs. default affinity
* is 0xffffffff until changed via /proc
* in irq.c.
*/
for (i = 0; i < NumSources; i++)
- openpic_mapirq(i, msk, ~0U);
+ openpic_mapirq(i, msk, CPU_MASK_ALL);
#endif /* CONFIG_IRQ_ALL_CPUS */
openpic_set_priority(0);
/*
* Map a timer interrupt to one or more CPUs
*/
-static void __init openpic_maptimer(u_int timer, u_int cpumask)
+static void __init openpic_maptimer(u_int timer, cpumask_t cpumask)
{
+ cpumask_t phys = physmask(cpumask);
check_arg_timer(timer);
openpic_write(&OpenPIC->Global.Timer[timer].Destination,
- physmask(cpumask));
+ cpus_addr(phys)[0]);
}
/*
/*
* Map an interrupt source to one or more CPUs
*/
-static void openpic_mapirq(u_int irq, u_int physmask, u_int keepmask)
+static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask)
{
if (ISR[irq] == 0)
return;
- if (keepmask != 0)
- physmask |= openpic_read(&ISR[irq]->Destination) & keepmask;
- openpic_write(&ISR[irq]->Destination, physmask);
+ if (!cpus_empty(keepmask)) {
+ cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) };
+ cpus_and(irqdest, irqdest, keepmask);
+ cpus_or(physmask, physmask, irqdest);
+ }
+ openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]);
}
#ifdef notused
#endif
}
-static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
+static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask)
{
- openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), 0);
+ openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE);
}
#ifdef CONFIG_SMP
CONFIG_MTD_PARTITIONS=m
CONFIG_MTD_CONCAT=m
CONFIG_MTD_REDBOOT_PARTS=m
-CONFIG_MTD_CMDLINE_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
#
# User Modules And Translation Layers
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_GEN_PROBE=m
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
CONFIG_MTD_RAM=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_ELAN_104NC=m
CONFIG_MTD_SCx200_DOCFLASH=m
CONFIG_MTD_AMD76XROM=m
-CONFIG_MTD_ICH2ROM=m
+# CONFIG_MTD_ICHXROM is not set
CONFIG_MTD_SCB2_FLASH=m
# CONFIG_MTD_NETtel is not set
# CONFIG_MTD_DILNETPC is not set
# CONFIG_MTD_PMC551_BUGFIX is not set
# CONFIG_MTD_PMC551_DEBUG is not set
# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
CONFIG_MTD_MTDRAM=m
CONFIG_MTDRAM_TOTAL_SIZE=4096
CONFIG_MTDRAM_ERASE_SIZE=128
# CONFIG_MTD_DOC2001 is not set
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCPROBE=m
+CONFIG_MTD_DOCECC=m
# CONFIG_MTD_DOCPROBE_ADVANCED is not set
CONFIG_MTD_DOCPROBE_ADDRESS=0
CONFIG_MTD_NAND=m
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
#
# Parallel port support
CONFIG_JFFS2_FS=m
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_PROC is not set
CONFIG_CRAMFS=m
CONFIG_VXFS_FS=m
# CONFIG_HPFS_FS is not set
CONFIG_MTD_PARTITIONS=m
CONFIG_MTD_CONCAT=m
CONFIG_MTD_REDBOOT_PARTS=m
-CONFIG_MTD_CMDLINE_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
#
# User Modules And Translation Layers
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_GEN_PROBE=m
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
CONFIG_MTD_RAM=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_ELAN_104NC=m
CONFIG_MTD_SCx200_DOCFLASH=m
CONFIG_MTD_AMD76XROM=m
-CONFIG_MTD_ICH2ROM=m
+# CONFIG_MTD_ICHXROM is not set
CONFIG_MTD_SCB2_FLASH=m
# CONFIG_MTD_NETtel is not set
# CONFIG_MTD_DILNETPC is not set
# CONFIG_MTD_PMC551_BUGFIX is not set
# CONFIG_MTD_PMC551_DEBUG is not set
# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
CONFIG_MTD_MTDRAM=m
CONFIG_MTDRAM_TOTAL_SIZE=4096
CONFIG_MTDRAM_ERASE_SIZE=128
# CONFIG_MTD_DOC2001 is not set
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCPROBE=m
+CONFIG_MTD_DOCECC=m
# CONFIG_MTD_DOCPROBE_ADVANCED is not set
CONFIG_MTD_DOCPROBE_ADDRESS=0
CONFIG_MTD_NAND=m
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
#
# Parallel port support
CONFIG_JFFS2_FS=m
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_PROC is not set
CONFIG_CRAMFS=m
CONFIG_VXFS_FS=m
# CONFIG_HPFS_FS is not set
CONFIG_MTD_PARTITIONS=m
CONFIG_MTD_CONCAT=m
CONFIG_MTD_REDBOOT_PARTS=m
-CONFIG_MTD_CMDLINE_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
#
# User Modules And Translation Layers
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_GEN_PROBE=m
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
CONFIG_MTD_RAM=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_ELAN_104NC=m
CONFIG_MTD_SCx200_DOCFLASH=m
CONFIG_MTD_AMD76XROM=m
-CONFIG_MTD_ICH2ROM=m
+# CONFIG_MTD_ICHXROM is not set
CONFIG_MTD_SCB2_FLASH=m
# CONFIG_MTD_NETtel is not set
# CONFIG_MTD_DILNETPC is not set
# CONFIG_MTD_PMC551_BUGFIX is not set
# CONFIG_MTD_PMC551_DEBUG is not set
# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
CONFIG_MTD_MTDRAM=m
CONFIG_MTDRAM_TOTAL_SIZE=4096
CONFIG_MTDRAM_ERASE_SIZE=128
# CONFIG_MTD_DOC2001 is not set
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCPROBE=m
+CONFIG_MTD_DOCECC=m
# CONFIG_MTD_DOCPROBE_ADVANCED is not set
CONFIG_MTD_DOCPROBE_ADDRESS=0
CONFIG_MTD_NAND=m
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
#
# Parallel port support
CONFIG_JFFS2_FS=m
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_PROC is not set
CONFIG_CRAMFS=m
CONFIG_VXFS_FS=m
# CONFIG_HPFS_FS is not set
CONFIG_MTD_PARTITIONS=m
CONFIG_MTD_CONCAT=m
CONFIG_MTD_REDBOOT_PARTS=m
-CONFIG_MTD_CMDLINE_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
#
# User Modules And Translation Layers
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_GEN_PROBE=m
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_AMDSTD_RETRY=3
CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_CFI_UTIL=m
CONFIG_MTD_RAM=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_ELAN_104NC=m
CONFIG_MTD_SCx200_DOCFLASH=m
CONFIG_MTD_AMD76XROM=m
-CONFIG_MTD_ICH2ROM=m
+# CONFIG_MTD_ICHXROM is not set
CONFIG_MTD_SCB2_FLASH=m
# CONFIG_MTD_NETtel is not set
# CONFIG_MTD_DILNETPC is not set
# CONFIG_MTD_PMC551_BUGFIX is not set
# CONFIG_MTD_PMC551_DEBUG is not set
# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
CONFIG_MTD_MTDRAM=m
CONFIG_MTDRAM_TOTAL_SIZE=4096
CONFIG_MTDRAM_ERASE_SIZE=128
# CONFIG_MTD_DOC2001 is not set
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCPROBE=m
+CONFIG_MTD_DOCECC=m
# CONFIG_MTD_DOCPROBE_ADVANCED is not set
CONFIG_MTD_DOCPROBE_ADDRESS=0
CONFIG_MTD_NAND=m
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
#
# Parallel port support
CONFIG_JFFS2_FS=m
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_NAND=y
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_PROC is not set
CONFIG_CRAMFS=m
CONFIG_VXFS_FS=m
# CONFIG_HPFS_FS is not set
-# $Id: Kconfig,v 1.3 2003/05/28 11:02:23 dwmw2 Exp $
+# $Id: Kconfig,v 1.5 2004/06/04 15:59:32 gleixner Exp $
menu "Memory Technology Devices (MTD)"
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example.
+config MTD_REDBOOT_PARTS_UNALLOCATED
+ bool " Include unallocated flash regions"
+ depends on MTD_REDBOOT_PARTS
+ help
+ If you need to register each unallocated flash region as a MTD
+ 'partition', enable this option.
+
+config MTD_REDBOOT_PARTS_READONLY
+ bool " Force read-only for RedBoot system images"
+ depends on MTD_REDBOOT_PARTS
+ help
+ If you need to force read-only for 'RedBoot', 'RedBoot Config' and
+ 'FIS directory' images, enable this option.
+
config MTD_CMDLINE_PARTS
- tristate "Command line partition table parsing"
- depends on MTD_PARTITIONS
+ bool "Command line partition table parsing"
+ depends on MTD_PARTITIONS = "y"
---help---
Allow generic configuration of the MTD paritition tables via the kernel
command line. Multiple flash resources are supported for hardware where
#
# Makefile for the memory technology device drivers.
#
-# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $
-
-# *** BIG UGLY NOTE ***
-#
-# The shiny new inter_module_xxx has introduced yet another ugly link
-# order dependency, which I'd previously taken great care to avoid.
-# We now have to ensure that the chip drivers are initialised before the
-# map drivers, and that the doc200[01] drivers are initialised before
-# docprobe.
-#
-# We'll hopefully merge the doc200[01] drivers and docprobe back into
-# a single driver some time soon, but the CFI drivers are going to have
-# to stay like that.
-#
-# Urgh.
-#
-# dwmw2 21/11/0
+# $Id: Makefile.common,v 1.3 2004/07/12 16:07:30 dwmw2 Exp $
# Core functionality.
obj-$(CONFIG_MTD) += mtdcore.o
This is access code for flashes using ARM's flash partitioning
standards.
- $Id: afs.c,v 1.12 2003/06/13 15:31:06 rmk Exp $
+ $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $
======================================================================*/
# drivers/mtd/chips/Kconfig
-# $Id: Kconfig,v 1.3 2003/05/28 15:13:24 dwmw2 Exp $
+# $Id: Kconfig,v 1.8 2004/07/13 22:32:02 dwmw2 Exp $
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
arrangements of CFI chips. If unsure, say 'N' and all options
which are supported by the current code will be enabled.
-config MTD_CFI_B1
- bool "Support 8-bit buswidth"
- depends on MTD_CFI_GEOMETRY
+config MTD_MAP_BANK_WIDTH_1
+ bool "Support 8-bit buswidth" if MTD_CFI_GEOMETRY
+ default y
help
If you wish to support CFI devices on a physical bus which is
8 bits wide, say 'Y'.
-config MTD_CFI_B2
- bool "Support 16-bit buswidth"
- depends on MTD_CFI_GEOMETRY
+config MTD_MAP_BANK_WIDTH_2
+ bool "Support 16-bit buswidth" if MTD_CFI_GEOMETRY
+ default y
help
If you wish to support CFI devices on a physical bus which is
16 bits wide, say 'Y'.
-config MTD_CFI_B4
- bool "Support 32-bit buswidth"
- depends on MTD_CFI_GEOMETRY
+config MTD_MAP_BANK_WIDTH_4
+ bool "Support 32-bit buswidth" if MTD_CFI_GEOMETRY
+ default y
help
If you wish to support CFI devices on a physical bus which is
32 bits wide, say 'Y'.
-config MTD_CFI_B8
- bool "Support 64-bit buswidth"
- depends on MTD_CFI_GEOMETRY
+config MTD_MAP_BANK_WIDTH_8
+ bool "Support 64-bit buswidth" if MTD_CFI_GEOMETRY
+ default n
help
If you wish to support CFI devices on a physical bus which is
64 bits wide, say 'Y'.
+config MTD_MAP_BANK_WIDTH_16
+ bool "Support 128-bit buswidth" if MTD_CFI_GEOMETRY
+ default n
+ help
+ If you wish to support CFI devices on a physical bus which is
+ 128 bits wide, say 'Y'.
+
+config MTD_MAP_BANK_WIDTH_32
+ bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY
+ default n
+ help
+ If you wish to support CFI devices on a physical bus which is
+ 256 bits wide, say 'Y'.
+
config MTD_CFI_I1
- bool "Support 1-chip flash interleave" if !MTD_CFI_B1
- depends on MTD_CFI_GEOMETRY
- default y if MTD_CFI_B1
+ bool "Support 1-chip flash interleave" if MTD_CFI_GEOMETRY
+ default y
help
If your flash chips are not interleaved - i.e. you only have one
flash chip addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I2
- bool "Support 2-chip flash interleave"
- depends on MTD_CFI_GEOMETRY
+ bool "Support 2-chip flash interleave" if MTD_CFI_GEOMETRY
+ default y
help
If your flash chips are interleaved in pairs - i.e. you have two
flash chips addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I4
- bool "Support 4-chip flash interleave"
- depends on MTD_CFI_GEOMETRY
+ bool "Support 4-chip flash interleave" if MTD_CFI_GEOMETRY
+ default n
help
If your flash chips are interleaved in fours - i.e. you have four
flash chips addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I8
- bool "Support 8-chip flash interleave"
- depends on MTD_CFI_GEOMETRY
+ bool "Support 8-chip flash interleave" if MTD_CFI_GEOMETRY
+ default n
help
If your flash chips are interleaved in eights - i.e. you have eight
flash chips addressed by each bus cycle, then say 'Y'.
provides support for one of those command sets, used on chips
including the AMD Am29LV320.
+config MTD_CFI_AMDSTD_RETRY
+ int "Retry failed commands (erase/program)"
+ depends on MTD_CFI_AMDSTD
+ default "0"
+ help
+ Some chips, when attached to a shared bus, don't properly filter
+ bus traffic that is destined to other devices. This broken
+ behavior causes erase and program sequences to be aborted when
+ the sequences are mixed with traffic for other devices.
+
+ SST49LF040 (and related) chips are know to be broken.
+
+config MTD_CFI_AMDSTD_RETRY_MAX
+ int "Max retries of failed commands (erase/program)"
+ depends on MTD_CFI_AMDSTD_RETRY
+ default "0"
+ help
+ If you have an SST49LF040 (or related chip) then this value should
+ be set to at least 1. This can also be adjusted at driver load
+ time with the retry_cmd_max module parameter.
+
config MTD_CFI_STAA
tristate "Support for ST (Advanced Architecture) flash chips"
depends on MTD_GEN_PROBE
sets which a CFI-compliant chip may claim to implement. This code
provides support for one of those command sets.
+config MTD_CFI_UTIL
+ tristate
+ default y if MTD_CFI_INTELEXT=y || MTD_CFI_AMDSTD=y || MTD_CFI_STAA=y
+ default m if MTD_CFI_INTELEXT=m || MTD_CFI_AMDSTD=m || MTD_CFI_STAA=m
+
config MTD_RAM
tristate "Support for RAM chips in bus mapping"
depends on MTD
#
# linux/drivers/chips/Makefile
#
-# $Id: Makefile.common,v 1.1 2003/05/21 15:00:01 dwmw2 Exp $
+# $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 cfi_probe.o
+# 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
*
* Author: Jonas Holmberg <jonas.holmberg@axis.com>
*
- * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $
+ * $Id: amd_flash.c,v 1.24 2004/07/12 13:34:30 dwmw2 Exp $
*
* Copyright (c) 2001 Axis Communications AB
*
*
* (C) 2000 Red Hat. GPL'd
*
- * $Id: cfi_cmdset_0001.c,v 1.126 2003/06/23 07:45:48 dwmw2 Exp $
+ * $Id: cfi_cmdset_0001.c,v 1.153 2004/07/12 21:52:20 dwmw2 Exp $
*
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/cfi.h>
+/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
+
// debugging, turns off buffer write mode if set to 1
#define FORCE_WORD_WRITE 0
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_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 *);
struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
static struct mtd_info *cfi_intelext_setup (struct map_info *);
+static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **);
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char **mtdbuf);
static void cfi_tell_features(struct cfi_pri_intelext *extp)
{
int i;
- printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport);
- printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported");
- printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported");
- printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported");
- printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported");
- printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported");
- printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported");
- printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported");
- printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported");
- printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported");
- for (i=9; i<32; i++) {
+ printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport);
+ printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported");
+ printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported");
+ printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported");
+ printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported");
+ printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported");
+ printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported");
+ printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported");
+ printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported");
+ printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported");
+ printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported");
+ for (i=10; i<32; i++) {
if (extp->FeatureSupport & (1<<i))
printk(" - Unknown Bit %X: supported\n", i);
}
}
printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
- extp->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 map_info *map, void* param)
+{
+ 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
+
+static void fixup_st_m28w320ct(struct map_info *map, void* param)
+{
+ 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 map_info *map, void* param)
+{
+ 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 struct cfi_fixup fixup_table[] = {
+#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+ {
+ CFI_MFR_ANY, CFI_ID_ANY,
+ fixup_intel_strataflash, NULL
+ },
#endif
+ {
+ 0x0020, /* STMicroelectronics */
+ 0x00ba, /* M28W320CT */
+ fixup_st_m28w320ct, NULL
+ }, {
+ 0x0020, /* STMicroelectronics */
+ 0x00bb, /* M28W320CB */
+ fixup_st_m28w320cb, NULL
+ }, {
+ 0, 0, NULL, NULL
+ }
+};
/* This routine is made available to other mtd code via
* inter_module_register. It must only be accessed through
{
struct cfi_private *cfi = map->fldrv_priv;
int i;
- __u32 base = cfi->chips[0].start;
if (cfi->cfi_mode == CFI_MODE_CFI) {
/*
*/
__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)
+ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
+ if (!extp)
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; i<sizeof(*extp); i++) {
- ((unsigned char *)extp)[i] =
- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
- }
-
- if (extp->MajorVersion != '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;
- }
/* 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);
+
+ /* Install our own private info structure */
+ cfi->cmdset_priv = extp;
+
+ cfi_fixup(map, fixup_table);
#ifdef DEBUG_CFI_FEATURES
/* Tell the user about it in lots of lovely detail */
#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;
}
for (i=0; i< cfi->numchips; i++) {
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);
}
printk(KERN_INFO "Using word write method\n" );
mtd->write = cfi_intelext_write_words;
}
+#if 0
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
+#endif
mtd->sync = cfi_intelext_sync;
mtd->lock = cfi_intelext_lock;
mtd->unlock = cfi_intelext_unlock;
mtd->flags = MTD_CAP_NORFLASH;
map->fldrv = &cfi_intelext_chipdrv;
mtd->name = map->name;
+
+ /* This function has the potential to distort the reality
+ a bit and therefore should be called last. */
+ if (cfi_intelext_partition_fixup(map, &cfi) != 0)
+ goto setup_err;
+
__module_get(THIS_MODULE);
return mtd;
kfree(mtd);
}
kfree(cfi->cmdset_priv);
- kfree(cfi->cfiq);
return NULL;
}
+static int cfi_intelext_partition_fixup(struct map_info *map,
+ struct cfi_private **pcfi)
+{
+ struct cfi_private *cfi = *pcfi;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+
+ /*
+ * Probing of multi-partition flash ships.
+ *
+ * This is extremely crude at the moment and should probably be
+ * extracted entirely from the Intel extended query data instead.
+ * Right now a L18 flash is assumed if multiple operations is
+ * detected.
+ *
+ * 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->FeatureSupport & (1 << 9)) {
+ struct cfi_private *newcfi;
+ struct flchip *chip;
+ struct flchip_shared *shared;
+ int numparts, partshift, numvirtchips, i, j;
+
+ /*
+ * The L18 flash memory array is divided
+ * into multiple 8-Mbit partitions.
+ */
+ numparts = 1 << (cfi->cfiq->DevSize - 20);
+ partshift = 20 + __ffs(cfi->interleave);
+ 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 sets of %d interleaved chips "
+ "--> %d partitions of %#x bytes\n",
+ map->name, cfi->numchips, cfi->interleave,
+ newcfi->numchips, 1<<newcfi->chipshift);
+
+ map->fldrv_priv = newcfi;
+ *pcfi = newcfi;
+ kfree(cfi);
+ }
+
+ return 0;
+}
+
/*
* *********** CHIP ACCESS FUNCTIONS ***********
*/
{
DECLARE_WAITQUEUE(wait, current);
struct cfi_private *cfi = map->fldrv_priv;
- cfi_word status, status_OK = CMD(0x80);
+ map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01);
unsigned long timeo;
- struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
+ struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
resettime:
timeo = jiffies + HZ;
retry:
+ if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) {
+ /*
+ * 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);
+ }
+
switch (chip->state) {
case FL_STATUS:
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;
+
+ /* 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 %llx\n",
- (long long)status);
- spin_unlock(chip->mutex);
+ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %lx\n",
+ status.x[0]);
return -EIO;
}
spin_unlock(chip->mutex);
/* Erase suspend */
- cfi_write(map, CMD(0xB0), adr);
+ 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
*/
- cfi_write(map, CMD(0x70), adr);
+ map_write(map, CMD(0x70), adr);
chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1;
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;
if (time_after(jiffies, timeo)) {
/* Urgh. Resume and pretend we weren't here. */
- cfi_write(map, CMD(0xd0), adr);
+ map_write(map, CMD(0xd0), adr);
/* Make sure we're in 'read status' mode if it had finished */
- cfi_write(map, CMD(0x70), adr);
+ 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%llx\n", status);
+ "suspended: status = 0x%lx\n", status.x[0]);
return -EIO;
}
{
struct cfi_private *cfi = map->fldrv_priv;
+ if (chip->priv) {
+ struct flchip_shared *shared = chip->priv;
+ spin_lock(&shared->lock);
+ if (shared->writing == chip) {
+ /* 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);
+ } else {
+ if (chip->oldstate != FL_ERASING) {
+ shared->erasing = NULL;
+ if (chip->oldstate != FL_WRITING)
+ shared->writing = NULL;
+ }
+ spin_unlock(&shared->lock);
+ }
+ }
+ }
+
switch(chip->oldstate) {
case FL_ERASING:
chip->state = chip->oldstate;
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);
+ map_write(map, CMD(0xd0), adr);
+ map_write(map, CMD(0x70), adr);
chip->oldstate = FL_READY;
chip->state = FL_ERASING;
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;
adr += chip->start;
/* Ensure cmd read/writes are aligned. */
- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+ cmd_addr = adr & ~(map_bankwidth(map)-1);
spin_lock(chip->mutex);
if (!ret) {
if (chip->state != FL_POINT && chip->state != FL_READY)
- cfi_write(map, CMD(0xff), cmd_addr);
+ map_write(map, CMD(0xff), cmd_addr);
chip->state = FL_POINT;
chip->ref_point_counter++;
int chipnum;
int ret = 0;
- if (from + len > mtd->size)
+ if (!map->virt || (from + len > mtd->size))
return -EINVAL;
*mtdbuf = (void *)map->virt + from;
- if(*mtdbuf == NULL)
- return -EINVAL; /* can not point this region */
*retlen = 0;
/* Now lock the chip(s) to POINT state */
adr += chip->start;
/* Ensure cmd read/writes are aligned. */
- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+ cmd_addr = adr & ~(map_bankwidth(map)-1);
spin_lock(chip->mutex);
ret = get_chip(map, chip, cmd_addr, FL_READY);
}
if (chip->state != FL_POINT && chip->state != FL_READY) {
- cfi_write(map, CMD(0xff), cmd_addr);
+ map_write(map, CMD(0xff), cmd_addr);
chip->state = FL_READY;
}
}
return ret;
}
-
+#if 0
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;
}
if (chip->state != FL_JEDEC_QUERY) {
- cfi_write(map, CMD(0x90), chip->start);
+ map_write(map, CMD(0x90), chip->start);
chip->state = FL_JEDEC_QUERY;
}
return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
}
+#endif
-
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
{
struct cfi_private *cfi = map->fldrv_priv;
- cfi_word status, status_OK;
+ map_word status, status_OK;
unsigned long timeo;
int z, ret=0;
}
ENABLE_VPP(map);
- cfi_write(map, CMD(0x40), adr);
- cfi_write(map, datum, adr);
+ map_write(map, CMD(0x40), adr);
+ map_write(map, datum, adr);
chip->state = FL_WRITING;
spin_unlock(chip->mutex);
+ INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
cfi_udelay(chip->word_write_time);
spin_lock(chip->mutex);
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 */
/* 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;
}
out:
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;
-
- 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 */
- }
+ int n;
+ map_word datum;
+
+ 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);
if (ret)
return ret;
-
+
+ len -= n;
ofs += n;
buf += n;
(*retlen) += n;
}
}
- 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);
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 ++;
}
}
- 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 (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 */
- }
+ if (len & (map_bankwidth(map)-1)) {
+ map_word datum;
+
+ 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);
if (ret)
return ret;
- (*retlen) += n;
+ (*retlen) += len;
}
return 0;
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
- cfi_word status, status_OK;
+ map_word status, status_OK;
unsigned long cmd_adr, timeo;
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);
return ret;
}
- if (chip->state != FL_STATUS)
- cfi_write(map, CMD(0x70), cmd_adr);
-
- status = cfi_read(map, cmd_adr);
-
/* §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 %llx). 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))) {
+ printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
+ 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);
if (++z > 20) {
/* Argh. Not ready for write to buffer */
- cfi_write(map, CMD(0x70), cmd_adr);
+ map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
- printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
+ printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
+ status.x[0], map_read(map, cmd_adr).x[0]);
/* 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);
ret = -EIO;
goto out;
}
}
/* Write length of data to come */
- bytes = len & (CFIDEV_BUSWIDTH-1);
- words = len / CFIDEV_BUSWIDTH;
- cfi_write(map, CMD(words - !bytes), cmd_adr );
+ bytes = len & (map_bankwidth(map)-1);
+ words = len / map_bankwidth(map);
+ map_write(map, CMD(words - !bytes), cmd_adr );
/* Write data */
z = 0;
- while(z < words * CFIDEV_BUSWIDTH) {
- if (cfi_buswidth_is_1()) {
- u8 *b = (u8 *)buf;
-
- map_write8 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else if (cfi_buswidth_is_2()) {
- u16 *b = (u16 *)buf;
-
- map_write16 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else if (cfi_buswidth_is_4()) {
- u32 *b = (u32 *)buf;
-
- map_write32 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else if (cfi_buswidth_is_8()) {
- u64 *b = (u64 *)buf;
-
- map_write64 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else {
- ret = -EINVAL;
- goto out;
- }
- z += CFIDEV_BUSWIDTH;
+ 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) {
- int i = 0, n = 0;
- u_char tmp_buf[8], *tmp_p = tmp_buf;
-
- while (bytes--)
- tmp_buf[i++] = buf[n++];
- while (i < CFIDEV_BUSWIDTH)
- tmp_buf[i++] = 0xff;
- if (cfi_buswidth_is_2()) {
- u16 *b = (u16 *)tmp_p;
-
- map_write16 (map, *b++, adr+z);
- tmp_p = (u_char *)b;
- } else if (cfi_buswidth_is_4()) {
- u32 *b = (u32 *)tmp_p;
-
- map_write32 (map, *b++, adr+z);
- tmp_p = (u_char *)b;
- } else if (cfi_buswidth_is_8()) {
- u64 *b = (u64 *)tmp_p;
-
- map_write64 (map, *b++, adr+z);
- tmp_p = (u_char *)b;
- } else {
- ret = -EINVAL;
- goto out;
- }
+ 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);
+ INVALIDATE_CACHED_RANGE(map, adr, len);
cfi_udelay(chip->buffer_write_time);
spin_lock(chip->mutex);
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 */
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;
}
{
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;
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,
}
}
- /* Write buffer is worth it only if more than one word to write... */
while(len) {
/* We must not cross write block boundaries */
int size = wbufsize - (ofs & (wbufsize-1));
}
typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
- unsigned long adr, void *thunk);
+ unsigned long adr, int len, void *thunk);
static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
loff_t ofs, size_t len, void *thunk)
i=first;
while(len) {
- ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+ unsigned long chipmask;
+ int size = regions[i].erasesize;
+
+ ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
if (ret)
return ret;
- adr += regions[i].erasesize;
- len -= regions[i].erasesize;
+ adr += size;
+ len -= size;
- if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
+ chipmask = (1 << cfi->chipshift) - 1;
+ if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask))
i++;
if (adr >> cfi->chipshift) {
}
-static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int 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);
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;
chip->erase_suspended = 0;
spin_unlock(chip->mutex);
+ INVALIDATE_CACHED_RANGE(map, adr, len);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((chip->erase_time*HZ)/(2*1000));
spin_lock(chip->mutex);
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_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));
+ printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %lx, status = %lx.\n",
+ adr, status.x[0], map_read(map, adr).x[0]);
/* Clear status 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);
DISABLE_VPP(map);
spin_unlock(chip->mutex);
return -EIO;
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<CFIDEV_INTERLEAVE; i++) {
- chipstatus |= status >> (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<map_words(map); w++) {
+ for (i = 0; i<cfi_interleave(cfi); i++) {
+ chipstatus |= status.x[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);
+ 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%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;
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;
}
}
}
#ifdef DEBUG_LOCK_BITS
-static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int 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;
cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
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);
-
+ chip->state = FL_JEDEC_QUERY;
return 0;
}
#endif
#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 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;
int ret;
}
ENABLE_VPP(map);
- cfi_write(map, CMD(0x60), 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();
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_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));
+ 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(chip->mutex);
return -EIO;
ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
#ifdef DEBUG_LOCK_BITS
- printk(KERN_DEBUG
- "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
+ printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+ __FUNCTION__, ret);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
#ifdef DEBUG_LOCK_BITS
- printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
+ printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+ __FUNCTION__, ret);
cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif
/* 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), cfi->chips[i].start);
chip->state = FL_READY;
wake_up(&chip->wq);
}
struct cfi_private *cfi = map->fldrv_priv;
kfree(cfi->cmdset_priv);
kfree(cfi->cfiq);
+ kfree(cfi->chips[0].priv);
kfree(cfi);
kfree(mtd->eraseregions);
}
* AMD & Fujitsu Standard Vendor Command Set (ID 0x0002)
*
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
+ * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
*
* 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.74 2003/05/28 12:51:48 dwmw2 Exp $
+ * $Id: cfi_cmdset_0002.c,v 1.103 2004/07/14 16:24:03 dwmw2 Exp $
*
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/mtd/compatmac.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
-#include <linux/mtd/compatmac.h>
#define AMD_BOOTLOC_BUG
+#define FORCE_WORD_WRITE 0
+
+#define MAX_WORD_RETRIES 3
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 int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);
+static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);
static void cfi_amdstd_sync (struct mtd_info *);
static int cfi_amdstd_suspend (struct mtd_info *);
static void cfi_amdstd_resume (struct mtd_info *);
};
+/* #define DEBUG_LOCK_BITS */
+/* #define DEBUG_CFI_FEATURES */
+
+
+#ifdef DEBUG_CFI_FEATURES
+static void cfi_tell_features(struct cfi_pri_amdstd *extp)
+{
+ 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"
+ };
+
+ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1);
+ printk(" Address sensitive unlock: %s\n",
+ (extp->SiliconRevision & 1) ? "Not required" : "Required");
+
+ 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);
+
+ if (extp->BlkProt == 0)
+ printk(" Block protection: Not supported\n");
+ else
+ printk(" Block protection: %d sectors per group\n", extp->BlkProt);
+
+
+ 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
+
+#ifdef AMD_BOOTLOC_BUG
+/* Wheee. Bring me the head of someone at AMD. */
+static void fixup_amd_bootblock(struct map_info *map, void* param)
+{
+ 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);
+ extp->TopBottom = 3; /* top boot */
+ } else {
+ extp->TopBottom = 2; /* bottom boot */
+ }
+ }
+}
+#endif
+
+static struct cfi_fixup fixup_table[] = {
+#ifdef AMD_BOOTLOC_BUG
+ {
+ 0x0001, /* AMD */
+ CFI_ID_ANY,
+ fixup_amd_bootblock, NULL
+ },
+#endif
+ { 0, 0, NULL, NULL }
+};
+
+
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{
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;
if (cfi->cfi_mode==CFI_MODE_CFI){
+ /*
+ * 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)
+ return NULL;
+
+ /* Install our own private info structure */
+ cfi->cmdset_priv = extp;
+
+ cfi_fixup(map, 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;
+ }
- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-
- major = cfi_read_query(map, base + (adr+3)*ofs_factor);
- minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
-
- 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);
- /* FIXME - should have a delay before continuing */
- cfi->mfr = cfi_read_query(map, base);
- cfi->id = cfi_read_query(map, base + ofs_factor);
-
- /* Wheee. Bring me the head of someone at AMD. */
-#ifdef AMD_BOOTLOC_BUG
- if (((major << 8) | minor) < 0x3131) {
- /* CFI version 1.0 => don't trust bootloc */
- if (cfi->id & 0x80) {
- printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
- bootloc = 3; /* top boot */
- } else {
- bootloc = 2; /* bottom boot */
- }
- } else
-#endif
- {
- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor);
- }
if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
}
}
/*
- * FIXME - These might already be setup (more correctly)
- * buy jedec_probe.c.
+ * These might already be setup (more correctly) by
+ * jedec_probe.c - still need it for cfi_probe.c path.
*/
- switch (cfi->device_type) {
- case CFI_DEVICETYPE_X8:
- cfi->addr_unlock1 = 0x555;
- cfi->addr_unlock2 = 0x2aa;
- break;
- case CFI_DEVICETYPE_X16:
- cfi->addr_unlock1 = 0xaaa;
- if (map->buswidth == cfi->interleave) {
- /* X16 chip(s) in X8 mode */
- cfi->addr_unlock2 = 0x555;
- } else {
- cfi->addr_unlock2 = 0x554;
+ if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
+ switch (cfi->device_type) {
+ case CFI_DEVICETYPE_X8:
+ cfi->addr_unlock1 = 0x555;
+ cfi->addr_unlock2 = 0x2aa;
+ break;
+ case CFI_DEVICETYPE_X16:
+ cfi->addr_unlock1 = 0xaaa;
+ if (map_bankwidth(map) == cfi_interleave(cfi)) {
+ /* X16 chip(s) in X8 mode */
+ cfi->addr_unlock2 = 0x555;
+ } else {
+ cfi->addr_unlock2 = 0x554;
+ }
+ break;
+ case CFI_DEVICETYPE_X32:
+ cfi->addr_unlock1 = 0x1554;
+ if (map_bankwidth(map) == cfi_interleave(cfi)*2) {
+ /* X32 chip(s) in X16 mode */
+ cfi->addr_unlock1 = 0xaaa;
+ } else {
+ cfi->addr_unlock2 = 0xaa8;
+ }
+ break;
+ default:
+ printk(KERN_WARNING
+ "MTD %s(): Unsupported device type %d\n",
+ __func__, cfi->device_type);
+ return NULL;
}
- 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 */
for (i=0; i< cfi->numchips; i++) {
map->fldrv = &cfi_amdstd_chipdrv;
- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
return cfi_amdstd_setup(map);
}
+
static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
{
struct cfi_private *cfi = map->fldrv_priv;
struct mtd_info *mtd;
unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
+ 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);
+ (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;
+ printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+ goto setup_err;
}
memset(mtd, 0, sizeof(*mtd));
mtd->type = MTD_NORFLASH;
/* Also select the correct geometry setup too */
mtd->size = devsize * cfi->numchips;
-
- if (cfi->cfiq->NumEraseRegions == 1) {
- /* No need to muck about with multiple erase sizes */
- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
- } else {
- unsigned long offset = 0;
- int i,j;
-
- mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
- if (!mtd->eraseregions) {
- printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
- goto setup_err;
- }
+
+ mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
+ 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;
+ }
- for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
- unsigned long ernum, ersize;
- ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
- ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
+ for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
+ unsigned long ernum, ersize;
+ ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
+ ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
- if (mtd->erasesize < ersize) {
- mtd->erasesize = ersize;
- }
- for (j=0; j<cfi->numchips; j++) {
- mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
- mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
- mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
- }
- offset += (ersize * ernum);
+ if (mtd->erasesize < ersize) {
+ mtd->erasesize = ersize;
}
- if (offset != devsize) {
- /* Argh */
- printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
- goto setup_err;
+ for (j=0; j<cfi->numchips; j++) {
+ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
+ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
+ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
}
+ offset += (ersize * ernum);
+ }
+ if (offset != devsize) {
+ /* Argh */
+ printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
+ goto setup_err;
+ }
#if 0
- // debug
- for (i=0; i<mtd->numeraseregions;i++){
- printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
- i,mtd->eraseregions[i].offset,
- mtd->eraseregions[i].erasesize,
- mtd->eraseregions[i].numblocks);
- }
-#endif
+ // debug
+ for (i=0; i<mtd->numeraseregions;i++){
+ printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
+ i,mtd->eraseregions[i].offset,
+ mtd->eraseregions[i].erasesize,
+ mtd->eraseregions[i].numblocks);
}
-
- switch (CFIDEV_BUSWIDTH)
- {
- case 1:
- case 2:
- case 4:
-#if 1
- if (mtd->numeraseregions > 1)
- mtd->erase = cfi_amdstd_erase_varsize;
- else
#endif
- 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 (mtd->numeraseregions == 1
+ && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
+ mtd->erase = cfi_amdstd_erase_chip;
+ } else {
+ mtd->erase = cfi_amdstd_erase_varsize;
+ mtd->lock = cfi_amdstd_lock_varsize;
+ mtd->unlock = cfi_amdstd_unlock_varsize;
}
- 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;
+
+ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
+ mtd->write = cfi_amdstd_write_buffers;
+ } else {
+ DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );
+ mtd->write = cfi_amdstd_write_words;
}
+ mtd->read = cfi_amdstd_read;
- /* does this chip have a secsi area? */
+ /* 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");
+
+ /* does this chip have a secsi area? */
if(cfi->mfr==1){
switch(cfi->id){
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:
- cfi_spin_lock(chip->mutex);
+ switch (chip->state) {
- if (chip->state != FL_READY){
-#if 0
- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
-#endif
+ 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;
+ }
+
+ 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
+ || (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;
- chip->state = FL_READY;
+ /* 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);
- 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;
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);
if (chip->state != FL_READY){
#if 0
- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
+ 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);
-
+
cfi_spin_unlock(chip->mutex);
schedule();
adr += chip->start;
chip->state = FL_READY;
-
+
+ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
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(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
map_copy_from(map, buf, adr, len);
+ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
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(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
return ret;
}
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word 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, prev_oldstatus, prev_status;
- unsigned int dq6;
struct cfi_private *cfi = map->fldrv_priv;
- /* 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. Using a static variable allows makes us save the costly
- divide operation at each word write.*/
- static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
- 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;
- int ta = 0;
+ map_word oldd, curd;
+ int retry_cnt = 0;
- retry:
- cfi_spin_lock(chip->mutex);
+ adr += chip->start;
- 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);
-
+ cfi_spin_lock(chip->mutex);
+ ret = get_chip(map, chip, adr, FL_WRITING);
+ if (ret) {
cfi_spin_unlock(chip->mutex);
+ return ret;
+ }
- 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;
- }
-
- chip->state = FL_WRITING;
+ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+ __func__, adr, datum.x[0] );
- adr += chip->start;
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8llx)\n",
- __func__, adr, datum );
+ /*
+ * 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;
+ }
ENABLE_VPP(map);
- if (fast) { /* Unlock bypass */
- cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
- }
- else {
- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
- cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
- }
- cfi_write(map, datum, adr);
+ retry:
+ /*
+ * The CFI_DEVICETYPE_X8 argument is needed even when
+ * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for
+ * command sequences don't scale even when the device is
+ * wider. This is the case for many of the cfi_send_gen_cmd()
+ * below. I'm not sure, however, why some use
+ * cfi->device_type.
+ */
+ 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);
+ 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.
- *
- * It appears tha the polling and decoding of error state might
- * be simplified. Don't do it unless you really know what you
- * are doing. You must remember that JESD21-C 3.5.3 states that
- * the status must be read back an _additional_ two times before
- * a failure is determined. This is because these devices have
- * internal state machines that are asynchronous to the external
- * data bus. During an erase or write the read-back status of the
- * polling bits might be transitioning internaly when the external
- * read-back occurs. This means that the bits aren't in the final
- * state and they might appear to report an error as they transition
- * and are in a weird state. This will produce infrequent errors
- * that will usually disappear the next time an erase or write
- * happens (Try tracking those errors down!). To ensure that
- * the bits are not in transition the location must be read-back
- * two more times and compared against what was written - BOTH reads
- * MUST match what was written - don't think this can be simplified
- * to only the last read matching. If the comparison fails, error
- * state can then be decoded.
- *
- * - Thayne Harbaugh
- */
- dq6 = CMD(1<<6);
/* See comment above for timeout value. */
timeo = jiffies + uWriteTimeout;
-
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
-
- /*
- * This only checks if dq6 is still toggling and that our
- * timer hasn't expired. We purposefully ignore the chips
- * internal timer that will assert dq5 and leave dq6 toggling.
- * This is done for a variety of reasons:
- * 1) Not all chips support dq5.
- * 2) Dealing with asynchronous status bit and data updates
- * and reading a device two more times creates _messy_
- * logic when trying to deal with interleaved devices -
- * some may be changing while others are still busy.
- * 3) Checking dq5 only helps to optimize an error case that
- * should at worst be infrequent and at best non-existent.
- *
- * If our timeout occurs _then_ we will check dq5 to see
- * if the device also had an internal timeout.
- */
- while( ( ( status ^ oldstatus ) & dq6 )
- && ! ( ta = time_after(jiffies, timeo) ) ) {
+ 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 );
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
- }
+ continue;
+ }
- /*
- * Something kicked us out of the read-back loop. We'll
- * check success befor checking failure.
- * Even though dq6 might be true data, it is unkown if
- * all of the other bits have changed to true data due to
- * the asynchronous nature of the internal state machine.
- * We will read two more times and use this to either
- * verify that the write completed successfully or
- * that something really went wrong. BOTH reads
- * must match what was written - this certifies that
- * bits aren't still changing and that the status
- * bits erroneously match the datum that was written.
- */
- prev_oldstatus = oldstatus;
- prev_status = status;
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
-
- if ( oldstatus == datum && status == datum ) {
- /* success - do nothing */
- goto write_done;
- }
+ /* Test to see if toggling has stopped. */
+ oldd = map_read(map, adr);
+ curd = map_read(map, adr);
+ if (map_word_equal(map, curd, oldd)) {
+ /* Do we have the correct value? */
+ if (map_word_equal(map, curd, datum)) {
+ goto op_done;
+ }
+ /* Nope something has gone wrong. */
+ break;
+ }
- if ( ta ) {
- int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
- if ( status & dq5mask ) {
- /* dq5 asserted - decode interleave chips */
- printk( KERN_WARNING
- "MTD %s(): FLASH internal timeout: 0x%.8x\n",
- __func__,
- status & dq5mask );
- } else {
- printk( KERN_WARNING
- "MTD %s(): Software timed out during write.\n",
+ if (time_after(jiffies, timeo)) {
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
+ break;
}
- goto write_failed;
- }
-
- /*
- * If we get to here then it means that something
- * is wrong and it's not a timeout. Something
- * is seriously wacky! Dump some debug info.
- */
- printk(KERN_WARNING
- "MTD %s(): Wacky! Unable to decode failure status\n",
- __func__ );
- printk(KERN_WARNING
- "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
- __func__, adr, datum,
- prev_oldstatus, prev_status,
- oldstatus, status);
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
+ }
- write_failed:
- ret = -EIO;
/* reset on all failures. */
- cfi_write( map, CMD(0xF0), chip->start );
+ map_write( map, CMD(0xF0), chip->start );
/* FIXME - should have reset delay before continuing */
+ if (++retry_cnt <= MAX_WORD_RETRIES)
+ goto retry;
- write_done:
- 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 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)
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[8];
- cfi_word 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 ++;
}
}
- 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) {
- 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 {
- return -EINVAL;
- }
- ret = do_write_oneword(map, &cfi->chips[chipnum],
- ofs, datum, cfi->fast_prog);
- if (ret) {
- if (cfi->fast_prog){
- /* Get out of unlock bypass mode */
- cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
- cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
- }
- return ret;
- }
+ while(len >= map_bankwidth(map)) {
+ map_word datum;
- ofs += CFIDEV_BUSWIDTH;
- buf += CFIDEV_BUSWIDTH;
- (*retlen) += CFIDEV_BUSWIDTH;
- len -= CFIDEV_BUSWIDTH;
+ datum = map_word_load(map, buf);
- 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);
- }
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+ ofs, datum);
+ if (ret)
+ return ret;
+
+ ofs += map_bankwidth(map);
+ buf += map_bankwidth(map);
+ (*retlen) += map_bankwidth(map);
+ len -= map_bankwidth(map);
+ if (ofs >> cfi->chipshift) {
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[8];
- cfi_word datum;
-
- map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
- while (len--)
- tmp_buf[i++] = buf[n++];
-
- if (cfi_buswidth_is_2()) {
- datum = *(__u16*)tmp_buf;
- } else if (cfi_buswidth_is_4()) {
- datum = *(__u32*)tmp_buf;
- } else {
- return -EINVAL; /* should never happen, but be safe */
+ if (len & (map_bankwidth(map)-1)) {
+ map_word tmp_buf;
+
+ retry1:
+ cfi_spin_lock(cfi->chips[chipnum].mutex);
+
+ 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, prev_oldstatus, prev_status;
- unsigned int dq6;
- unsigned long timeo = jiffies + HZ;
- unsigned long int adr;
struct cfi_private *cfi = map->fldrv_priv;
- DECLARE_WAITQUEUE(wait, current);
- int ret = 0;
- int ta = 0;
- cfi_word ones = 0;
+ unsigned long timeo = jiffies + HZ;
+ /* see comments in do_write_oneword() regarding uWriteTimeo. */
+ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+ int ret = -EIO;
+ unsigned long cmd_adr;
+ int z, words;
+ map_word datum;
- retry:
- cfi_spin_lock(chip->mutex);
+ adr += chip->start;
+ cmd_adr = adr;
- if (chip->state != FL_READY){
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&chip->wq, &wait);
-
+ cfi_spin_lock(chip->mutex);
+ ret = get_chip(map, chip, adr, FL_WRITING);
+ if (ret) {
cfi_spin_unlock(chip->mutex);
+ return ret;
+ }
- schedule();
- remove_wait_queue(&chip->wq, &wait);
-#if 0
- if(signal_pending(current))
- return -EINTR;
-#endif
- timeo = jiffies + HZ;
+ datum = map_word_load(map, buf);
- goto retry;
- }
+ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+ __func__, adr, datum.x[0] );
- chip->state = FL_ERASING;
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
- __func__, chip->start );
-
- /* 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;
+ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
- /* Wait for the end of programing/erasure by using the toggle method.
- * As long as there is a programming procedure going on, bit 6
- * is toggling it's state with each consecutive 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.
- */
- /* see comments in do_write_oneword */
- dq6 = CMD(1<<6);
+ /* Write Buffer Load */
+ map_write(map, CMD(0x25), cmd_adr);
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
+ chip->state = FL_WRITING_TO_BUFFER;
- while( ( ( status ^ oldstatus ) & dq6 )
- && ! ( ta = time_after(jiffies, timeo) ) ) {
- int wait_reps;
+ /* 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);
- /* an initial short sleep */
- cfi_spin_unlock(chip->mutex);
- schedule_timeout(HZ/100);
- cfi_spin_lock(chip->mutex);
+ z += map_bankwidth(map);
+ buf += map_bankwidth(map);
+ }
+ z -= map_bankwidth(map);
+
+ adr += z;
+
+ /* Write Buffer Program Confirm: GO GO GO */
+ map_write(map, CMD(0x29), cmd_adr);
+ chip->state = FL_WRITING;
+
+ cfi_spin_unlock(chip->mutex);
+ cfi_udelay(chip->buffer_write_time);
+ cfi_spin_lock(chip->mutex);
+
+ timeo = jiffies + uWriteTimeout;
- if (chip->state != FL_ERASING) {
- /* Someone's suspended the erase. Sleep */
+ 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 ^ oldstatus ) & dq6 );
- wait_reps++) {
-
- /* 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);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
- }
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
- }
-
- prev_oldstatus = oldstatus;
- prev_status = status;
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
-
- if ( cfi_buswidth_is_1() ) {
- ones = (__u8)~0;
- } else if ( cfi_buswidth_is_2() ) {
- ones = (__u16)~0;
- } else if ( cfi_buswidth_is_4() ) {
- ones = (__u32)~0;
- } else {
- printk(KERN_WARNING "Unsupported buswidth\n");
- goto erase_failed;
- }
-
- if ( oldstatus == ones && status == ones ) {
- /* success - do nothing */
- goto erase_done;
- }
+ if (chip_ready(map, adr))
+ goto op_done;
+
+ if( time_after(jiffies, timeo))
+ break;
- if ( ta ) {
- int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
- if ( status & dq5mask ) {
- /* dq5 asserted - decode interleave chips */
- printk( KERN_WARNING
- "MTD %s(): FLASH internal timeout: 0x%.8x\n",
- __func__,
- status & dq5mask );
- } else {
- printk( KERN_WARNING
- "MTD %s(): Software timed out during write.\n",
- __func__ );
- }
- goto erase_failed;
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
}
- printk(KERN_WARNING
- "MTD %s(): Wacky! Unable to decode failure status\n",
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
- printk(KERN_WARNING
- "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
- __func__, adr, ones,
- prev_oldstatus, prev_status,
- oldstatus, status);
-
- erase_failed:
- ret = -EIO;
/* reset on all failures. */
- cfi_write( map, CMD(0xF0), chip->start );
+ map_write( map, CMD(0xF0), chip->start );
/* FIXME - should have reset delay before continuing */
- erase_done:
- 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 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, prev_oldstatus, prev_status;
- unsigned int dq6;
- 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 ta = 0;
- cfi_word ones = 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);
-
- cfi_spin_unlock(chip->mutex);
+ chipnum = to >> cfi->chipshift;
+ ofs = to - (chipnum << cfi->chipshift);
- schedule();
- remove_wait_queue(&chip->wq, &wait);
-#if 0
- if(signal_pending(current))
- return -EINTR;
-#endif
- timeo = jiffies + HZ;
+ /* 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, to, local_len,
+ retlen, buf);
+ if (ret)
+ return ret;
+ ofs += local_len;
+ buf += local_len;
+ len -= local_len;
- goto retry;
- }
+ if (ofs >> cfi->chipshift) {
+ chipnum ++;
+ ofs = 0;
+ if (chipnum == cfi->numchips)
+ return 0;
+ }
+ }
- chip->state = FL_ERASING;
+ /* 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;
+ }
+ }
+
+ if (len) {
+ size_t retlen_dregs = 0;
+
+ ret = cfi_amdstd_write_words(mtd, to, len, &retlen_dregs, buf);
+
+ *retlen += retlen_dregs;
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Handle devices with one erase region, that only implement
+ * the chip erase command.
+ */
+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;
+
+ adr = cfi->addr_unlock1;
+
+ cfi_spin_lock(chip->mutex);
+ ret = get_chip(map, chip, adr, FL_WRITING);
+ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+ return ret;
+ }
- adr += chip->start;
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
- __func__, adr );
+ __func__, 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(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);
- cfi_write(map, CMD(0x30), adr);
-
- timeo = jiffies + (HZ*20);
-
- /* Wait for the end of programing/erasure by using the toggle method.
- * As long as there is a programming procedure going on, bit 6
- * is toggling it's state with each consecutive 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.
- */
- /* see comments in do_write_oneword */
- dq6 = CMD(1<<6);
+ chip->state = FL_ERASING;
+ chip->erase_suspended = 0;
+ chip->in_progress_block_addr = adr;
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
+ cfi_spin_unlock(chip->mutex);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((chip->erase_time*HZ)/(2*1000));
+ cfi_spin_lock(chip->mutex);
- while( ( ( status ^ oldstatus ) & dq6 )
- && ! ( ta = time_after(jiffies, timeo) ) ) {
- int wait_reps;
+ timeo = jiffies + (HZ*20);
- /* an initial short sleep */
- cfi_spin_unlock(chip->mutex);
- schedule_timeout(HZ/100);
- cfi_spin_lock(chip->mutex);
-
+ 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;
}
-
- /* Busy wait for 1/10 of a milisecond */
- for(wait_reps = 0;
- (wait_reps < 100)
- && ( ( status ^ oldstatus ) & dq6 );
- wait_reps++) {
-
- /* 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);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
+ if (chip->erase_suspended) {
+ /* This erase was suspended and resumed.
+ Adjust the timeout */
+ timeo = jiffies + (HZ*20); /* FIXME */
+ chip->erase_suspended = 0;
}
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
- }
- prev_oldstatus = oldstatus;
- prev_status = status;
- oldstatus = cfi_read(map, adr);
- status = cfi_read(map, adr);
- DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
- __func__, oldstatus, status );
-
- if ( cfi_buswidth_is_1() ) {
- ones = (__u8)~0;
- } else if ( cfi_buswidth_is_2() ) {
- ones = (__u16)~0;
- } else if ( cfi_buswidth_is_4() ) {
- ones = (__u32)~0;
- } else {
- printk(KERN_WARNING "Unsupported buswidth\n");
- goto erase_failed;
- }
+ if (chip_ready(map, adr))
+ goto op_done;
- if ( oldstatus == ones && status == ones ) {
- /* success - do nothing */
- goto erase_done;
- }
+ if (time_after(jiffies, timeo))
+ break;
- if ( ta ) {
- int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
- if ( status & dq5mask ) {
- /* dq5 asserted - decode interleave chips */
- printk( KERN_WARNING
- "MTD %s(): FLASH internal timeout: 0x%.8x\n",
- __func__,
- status & dq5mask );
- } else {
- printk( KERN_WARNING
- "MTD %s(): Software timed out during write.\n",
- __func__ );
- }
- goto erase_failed;
+ /* 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);
}
- printk(KERN_WARNING
- "MTD %s(): Wacky! Unable to decode failure status\n",
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
- printk(KERN_WARNING
- "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
- __func__, adr, ones,
- prev_oldstatus, prev_status,
- oldstatus, status);
-
- erase_failed:
- ret = -EIO;
/* reset on all failures. */
- cfi_write( map, CMD(0xF0), chip->start );
+ map_write( map, CMD(0xF0), chip->start );
/* FIXME - should have reset delay before continuing */
- erase_done:
- 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 ret;
}
-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+
+typedef int (*frob_t)(struct map_info *map, struct flchip *chip,
+ unsigned long adr, void *thunk);
+
+
+static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, 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, len;
+ unsigned long adr;
int chipnum, ret = 0;
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
- if (instr->addr > mtd->size)
+ if (ofs > mtd->size)
return -EINVAL;
- if ((instr->len + instr->addr) > mtd->size)
+ if ((len + ofs) > mtd->size)
return -EINVAL;
/* Check that both start and end of the requested erase are
start of the requested erase, and then go back one.
*/
- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
+ while (i < mtd->numeraseregions && ofs >= regions[i].offset)
i++;
i--;
effect here.
*/
- if (instr->addr & (regions[i].erasesize-1))
+ if (ofs & (regions[i].erasesize-1))
return -EINVAL;
/* Remember the erase region we start on */
* with the erase region at that address.
*/
- while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
+ while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which
*/
i--;
- if ((instr->addr + instr->len) & (regions[i].erasesize-1))
+ if ((ofs + len) & (regions[i].erasesize-1))
return -EINVAL;
-
- chipnum = instr->addr >> cfi->chipshift;
- adr = instr->addr - (chipnum << cfi->chipshift);
- len = instr->len;
- i=first;
+ chipnum = ofs >> cfi->chipshift;
+ adr = ofs - (chipnum << cfi->chipshift);
- while(len) {
- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
+ i=first;
+ while (len) {
+ ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+
if (ret)
return ret;
}
}
- 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)
+
+static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
{
- struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
- unsigned long adr, len;
- int chipnum, ret = 0;
+ unsigned long timeo = jiffies + HZ;
+ DECLARE_WAITQUEUE(wait, current);
+ int ret = 0;
- if (instr->addr & (mtd->erasesize - 1))
- return -EINVAL;
+ adr += chip->start;
- if (instr->len & (mtd->erasesize -1))
- return -EINVAL;
+ cfi_spin_lock(chip->mutex);
+ ret = get_chip(map, chip, adr, FL_ERASING);
+ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+ return ret;
+ }
- if ((instr->len + instr->addr) > mtd->size)
- return -EINVAL;
+ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+ __func__, adr );
- chipnum = instr->addr >> cfi->chipshift;
- adr = instr->addr - (chipnum << cfi->chipshift);
- len = instr->len;
+ 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);
+ map_write(map, CMD(0x30), adr);
+
+ chip->state = FL_ERASING;
+ chip->erase_suspended = 0;
+ chip->in_progress_block_addr = adr;
+
+ cfi_spin_unlock(chip->mutex);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((chip->erase_time*HZ)/(2*1000));
+ cfi_spin_lock(chip->mutex);
- while(len) {
- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
+ timeo = jiffies + (HZ*20);
- if (ret)
- return ret;
+ 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;
+ }
- adr += mtd->erasesize;
- len -= mtd->erasesize;
+ if (chip_ready(map, adr))
+ goto op_done;
- if (adr >> cfi->chipshift) {
- adr = 0;
- chipnum++;
-
- if (chipnum >= cfi->numchips)
+ if (time_after(jiffies, timeo))
break;
- }
+
+ /* 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);
}
-
+
+ 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;
+ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+ return ret;
+}
+
+
+int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+{
+ unsigned long ofs, len;
+ int ret;
+
+ ofs = instr->addr;
+ len = instr->len;
+
+ ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
+ if (ret)
+ return ret;
+
instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
return 0;
}
+
static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
return 0;
}
+
static void cfi_amdstd_sync (struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
schedule();
- remove_wait_queue(&chip->wq, &wait);
+ remove_wait_queue(&chip->wq, &wait);
goto retry;
}
/* Unlock the chips again */
if (ret) {
- for (i--; i >=0; i--) {
+ for (i--; i >=0; i--) {
chip = &cfi->chips[i];
cfi_spin_lock(chip->mutex);
return ret;
}
+
static void cfi_amdstd_resume(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
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
}
}
+
+#ifdef DEBUG_LOCK_BITS
+
+static int do_printlockstatus_oneblock(struct map_info *map,
+ struct flchip *chip,
+ unsigned long adr,
+ void *thunk)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ int ofs_factor = cfi->interleave * cfi->device_type;
+
+ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
+ 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);
+
+ return 0;
+}
+
+
+#define debug_dump_locks(mtd, frob, ofs, len, thunk) \
+ cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk))
+
+#else
+
+#define debug_dump_locks(...)
+
+#endif /* DEBUG_LOCK_BITS */
+
+
+struct xxlock_thunk {
+ uint8_t val;
+ flstate_t state;
+};
+
+
+#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING})
+#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING})
+
+
+/*
+ * FIXME - this is *very* specific to a particular chip. It likely won't
+ * work for all chips that require unlock. It also hasn't been tested
+ * with interleaved chips.
+ */
+static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk;
+ int ret;
+
+ /*
+ * 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.
+ */
+
+ adr += chip->start;
+ /*
+ * lock block registers:
+ * - on 64k boundariesand
+ * - bit 1 set high
+ * - block lock registers are 4MiB lower - overflow subtract (danger)
+ */
+ adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff;
+
+ 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 cfi_amdstd_lock_varsize(struct mtd_info *mtd,
+ loff_t ofs,
+ size_t len)
+{
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
+ __func__, ofs, len);
+ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
+
+ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
+ (void *)&DO_XXLOCK_ONEBLOCK_LOCK);
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s: lock status after, ret=%d\n",
+ __func__, ret);
+
+ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
+
+ return ret;
+}
+
+
+static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd,
+ loff_t ofs,
+ size_t len)
+{
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
+ __func__, ofs, len);
+ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
+
+ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
+ (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK);
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s: lock status after, ret=%d\n",
+ __func__, ret);
+ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
+
+ return ret;
+}
+
+
static void cfi_amdstd_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
static char im_name[]="cfi_cmdset_0002";
+
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. <info@crossnet.co.jp> et al.");
MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
-
*
* (C) 2000 Red Hat. GPL'd
*
+ * $Id: cfi_cmdset_0020.c,v 1.13 2004/07/12 21:52:50 dwmw2 Exp $
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
* - completely revamped method functions so they are aware and
{
struct cfi_private *cfi = map->fldrv_priv;
int i;
- __u32 base = cfi->chips[0].start;
if (cfi->cfi_mode) {
/*
*/
__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)
+ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics");
+ if (!extp)
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; i<sizeof(*extp); i++) {
- ((unsigned char *)extp)[i] =
- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
- }
-
- if (extp->MajorVersion != '1' ||
- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
- printk(KERN_WARNING " Unknown staa Extended Query "
- "version %c.%c.\n", extp->MajorVersion,
- extp->MinorVersion);
- kfree(extp);
- return NULL;
- }
-
/* Do some byteswapping if necessary */
extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask);
cfi->chips[i].erase_time = 1024;
}
- /* 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);
}
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;
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);
*/
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;
}
}
suspended = 1;
- cfi_write(map, CMD(0xff), cmd_addr);
+ map_write(map, CMD(0xff), cmd_addr);
chip->state = FL_READY;
break;
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;
}
/* 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;
}
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);
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);
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 = %llx\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;
}
}
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);
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()) {
- u8 *b = (u8 *)buf;
-
- map_write8 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else if (cfi_buswidth_is_2()) {
- u16 *b = (u16 *)buf;
-
- map_write16 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } else if (cfi_buswidth_is_4()) {
- u32 *b = (u32 *)buf;
-
- map_write32 (map, *b++, adr+z);
- buf = (const u_char *)b;
- } 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);
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);
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);
- /* put back into read status register mode */
- cfi_write(map, CMD(0x70), adr);
- wake_up(&chip->wq);
- spin_unlock_bh(chip->mutex);
- return (status & CMD(0x02)) ? -EROFS : -EIO;
- }
+ /* clear status */
+ map_write(map, CMD(0x50), cmd_adr);
+ /* put back into read status register mode */
+ map_write(map, CMD(0x70), adr);
+ wake_up(&chip->wq);
+ spin_unlock_bh(chip->mutex);
+ return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO;
+ }
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
{
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;
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
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);
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. */
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);
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 = %llx.\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;
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<CFIDEV_INTERLEAVE; i++) {
- chipstatus |= status >> (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<map_words(map); w++) {
+ for (i = 0; i<cfi_interleave(cfi); i++) {
+ chipstatus |= status.x[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;
}
}
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);
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. */
}
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);
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 = %llx.\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;
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);
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. */
}
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);
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 = %llx.\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;
/* 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);
}
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
- $Id: cfi_probe.c,v 1.71 2003/05/28 12:51:48 dwmw2 Exp $
+ $Id: cfi_probe.c,v 1.77 2004/07/14 08:38:44 dwmw2 Exp $
*/
#include <linux/config.h>
#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);
in: interleave,type,mode
ret: table index, <0 for error
*/
-static inline int qry_present(struct map_info *map, __u32 base,
+static int qry_present(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
int osf = cfi->interleave * cfi->device_type; // scale factor
+ map_word val;
+ map_word qry;
- 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 = cfi_build_cmd('Q', map, cfi);
+ val = map_read(map, base + osf*0x10);
- return 0; // nothing found
+ if (!map_word_equal(map, qry, val))
+ return 0;
+
+ qry = cfi_build_cmd('R', map, cfi);
+ val = map_read(map, base + osf*0x11);
+
+ if (!map_word_equal(map, qry, val))
+ return 0;
+
+ qry = cfi_build_cmd('Y', map, cfi);
+ val = map_read(map, base + osf*0x12);
+
+ if (!map_word_equal(map, qry, val))
+ return 0;
+
+ return 1; // nothing found
}
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)
{
int i;
return 0;
}
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))
}
/* Check each previous chip to see if it's an alias */
- for (i=0; i<cfi->numchips; 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);
+ 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)) {
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
* 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);
+ cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
if (qry_present(map, base, cfi)) {
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;
}
}
/* 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);
+ 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 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;
}
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++) {
(cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
#endif
}
+
+ /* 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);
+
+ 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;
}
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");
/*
- * $Id: chipreg.c,v 1.15 2003/05/21 15:15:05 dwmw2 Exp $
+ * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $
*
* Registration for chip drivers
*
* Routines common to all CFI-type probes.
* (C) 2001-2003 Red Hat, Inc.
* GPL'd
- * $Id: gen_probe.c,v 1.13 2003/06/25 11:50:37 dwmw2 Exp $
+ * $Id: gen_probe.c,v 1.19 2004/07/13 22:33:32 dwmw2 Exp $
*/
#include <linux/kernel.h>
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));
- memset(&chip[0], 0, sizeof(chip));
/* Call the probetype-specific code with all permutations of
interleave and device type, etc. */
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<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
- base += (1<<cfi.chipshift))
- cp->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
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);
-
- /* 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;
+ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips);
+
+ 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;
}
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;
-
- 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 */
-
-#ifdef CFIDEV_BUSWIDTH_8
- case CFIDEV_BUSWIDTH_8:
-#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
- cfi->interleave = CFIDEV_INTERLEAVE_2;
-
- 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;
-
-#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;
-
- 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_8 */
- break;
-#endif /* CFIDEV_BUSWIDTH_8 */
-
- default:
- printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth);
- return 0;
+ 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;
+
+ for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) {
+
+ if (!cfi_interleave_supported(nr_chips))
+ continue;
+
+ cfi->interleave = nr_chips;
+
+ for (type = 0; type < 3; type++) {
+ cfi->device_type = 1<<type;
+
+ if (cp->probe_chip(map, 0, NULL, cfi))
+ return 1;
+ }
}
return 0;
}
-
typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int);
extern cfi_cmdset_fn_t cfi_cmdset_0001;
* not going to guess how to send commands to them, plus I expect they will
* all speak CFI..
*
- * $Id: jedec.c,v 1.19 2003/05/29 09:25:23 dwmw2 Exp $
+ * $Id: jedec.c,v 1.20 2004/07/12 14:03:01 dwmw2 Exp $
*/
#include <linux/init.h>
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
- $Id: jedec_probe.c,v 1.29 2003/05/28 13:57:46 dwmw2 Exp $
+ $Id: jedec_probe.c,v 1.51 2004/07/14 14:44:30 thayne 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 <linux/config.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
/* 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 AT49BV32XT 0x00C9
/* Fujitsu */
+#define MBM29F040C 0x00A4
#define MBM29LV650UE 0x22D7
#define MBM29LV320TE 0x22F6
#define MBM29LV320BE 0x22F9
#define MBM29LV160BE 0x2249
#define MBM29LV800BA 0x225B
#define MBM29LV800TA 0x22DA
+#define MBM29LV400TC 0x22B9
+#define MBM29LV400BC 0x22BA
+
/* Intel */
#define I28F004B3T 0x00d4
#define M50FW016 0x002E
/* SST */
+#define SST29EE020 0x0010
+#define SST29LE020 0x0012
#define SST29EE512 0x005d
#define SST29LE512 0x003d
#define SST39LF800 0x2781
#define SST39LF040 0x00D7
#define SST39SF010A 0x00B5
#define SST39SF020A 0x00B6
+#define SST49LF004B 0x0060
+#define SST49LF008A 0x005a
#define SST49LF030A 0x001C
#define SST49LF040A 0x0051
#define SST49LF080A 0x005B
const __u16 dev_id;
const char *name;
const int DevSize;
- const int InterfaceDesc;
const int NumEraseRegions;
const int CmdSet;
- const __u8 uaddr[3]; /* unlock addrs for 8, 16, 32 modes */
- 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)
ERASEINFO(0x08000,1),
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_AMD,
.dev_id = AM29LV800BB,
ERASEINFO(0x10000,15),
}
}, {
+/* 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_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",
ERASEINFO(0x10000,63),
ERASEINFO(0x02000,8)
}
+ }, {
+ .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 = MBM29LV650UE,
ERASEINFO(0x02000,2),
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,7)
+ }
+ }, {
+ .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_INTEL,
.dev_id = I28F004B3B,
ERASEINFO(0x01000,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_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_SST,
.dev_id = SST39LF020,
.name = "SST 39LF020",
.regions = {
ERASEINFO(0x01000,64),
}
+ }, {
+ .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,
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 inline u32 jedec_read_mfr(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
- u32 result, mask;
+ map_word result;
+ unsigned long mask;
mask = (1 << (cfi->device_type * 8)) -1;
- result = cfi_read(map, base);
- result &= mask;
- return result;
+ result = map_read(map, base);
+ 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;
+ map_word result;
+ unsigned long mask;
osf = cfi->interleave *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 + osf);
+ 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) {
+ /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/
+ 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(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
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;
+ unsigned long mask;
__u8 uaddr;
printk("Found: %s\n",jedec_table[index].name);
p_cfi->id = jedec_table[index].dev_id;
uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type);
- if ( MTD_UADDR_NOT_SUPPORTED ) {
+ 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;
+
+ /* Mask out address bits which are smaller than the device type */
+ mask = ~(p_cfi->device_type-1);
+ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask;
+ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask;
return 1; /* ok */
}
int rc = 0; /* failure until all tests pass */
u32 mfr, id;
__u8 uaddr;
+ unsigned long mask;
- /* The ID's must match */
- if ( cfi->mfr != finfo->mfr_id || cfi->id != finfo->dev_id ) {
+ /*
+ * 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:
+ mfr = (__u8)finfo->mfr_id;
+ id = (__u8)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X16:
+ mfr = (__u16)finfo->mfr_id;
+ id = (__u16)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X32:
+ mfr = (__u16)finfo->mfr_id;
+ id = (__u32)finfo->dev_id;
+ break;
+ default:
+ 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;
}
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 + ( 1 << finfo->DevSize ) > map->size ) {
+ if ( base + cfi->interleave * ( 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,
}
uaddr = finfo_uaddr(finfo, cfi->device_type);
- if ( MTD_UADDR_NOT_SUPPORTED ) {
+ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
goto match_done;
}
+ mask = ~(cfi->device_type-1);
+
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 ) ) {
+ && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 ||
+ (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) {
DEBUG( MTD_DEBUG_LEVEL3,
- "MTD %s(): 0x%.4x 0x%.4x did not match\n",
+ "MTD %s(): 0x%.4lx 0x%.4lx did not match\n",
__func__,
- unlock_addrs[uaddr].addr1,
- unlock_addrs[uaddr].addr2 );
+ unlock_addrs[uaddr].addr1 & mask,
+ unlock_addrs[uaddr].addr2 & mask);
goto match_done;
}
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)
{
int i;
- int unlockpass = 0;
+ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
- /*
- * FIXME - eventually replace these unlock address seeds with
- * information from unlock_addrs[].
- */
+ retry:
if (!cfi->numchips) {
- switch (cfi->device_type) {
- case CFI_DEVICETYPE_X8:
- cfi->addr_unlock1 = 0x555;
- cfi->addr_unlock2 = 0x2aa;
- break;
- case CFI_DEVICETYPE_X16:
- cfi->addr_unlock1 = 0xaaa;
- if (map->buswidth == cfi->interleave) {
- /* X16 chip(s) in X8 mode */
- cfi->addr_unlock2 = 0x555;
- } else {
- cfi->addr_unlock2 = 0x554;
- }
- break;
- case CFI_DEVICETYPE_X32:
- cfi->addr_unlock1 = 0x1555;
- cfi->addr_unlock2 = 0xaaa;
- break;
- default:
- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type);
- return 0;
- }
+ unsigned long mask = ~(cfi->device_type-1);
+
+ uaddr_idx++;
+
+ if (MTD_UADDR_UNNECESSARY == uaddr_idx)
+ return 0;
+
+ /* Mask out address bits which are smaller than the device type */
+ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask;
+ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask;
}
- retry:
/* Make certain we aren't probing past the end of map */
if (base >= map->size) {
printk(KERN_NOTICE
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",
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
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;
+ goto retry;
} else {
__u16 mfr;
__u16 id;
}
}
- /* Check each previous chip to see if it's an alias */
- for (i=0; i<cfi->numchips; 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;
}
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;
}
}
/* 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",
+ 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;
}
/*
* Common code to handle map devices which are simple RAM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $
+ * $Id: map_ram.c,v 1.19 2004/07/12 21:58:44 dwmw2 Exp $
*/
#include <linux/module.h>
/* Yeah, it's inefficient. Who cares? It's faster than a _real_
flash erase. */
struct map_info *map = (struct map_info *)mtd->priv;
+ map_word allff;
unsigned long i;
- for (i=0; i<instr->len; i++)
- map_write8(map, 0xFF, instr->addr + i);
+ allff = map_word_ff(map);
+
+ for (i=0; i<instr->len; i += map_bankwidth(map))
+ map_write(map, allff, instr->addr + i);
+
+ instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
/*
* Common code to handle map devices which are simple ROM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $
+ * $Id: map_rom.c,v 1.21 2004/07/12 14:06:01 dwmw2 Exp $
*/
#include <linux/module.h>
* Copyright 2000,2001 David A. Schleef <ds@schleef.org>
* 2000,2001 Lineo, Inc.
*
- * $Id: sharp.c,v 1.12 2003/05/28 15:39:52 dwmw2 Exp $
+ * $Id: sharp.c,v 1.13 2004/07/12 14:06:34 dwmw2 Exp $
*
* Devices supported:
* LH28F016SCT Symmetrical block flash memory, 2Mx8
/*
- * $Id: cmdlinepart.c,v 1.9 2003/05/16 17:08:24 dwmw2 Exp $
+ * $Id: cmdlinepart.c,v 1.14 2004/07/12 12:34:23 dwmw2 Exp $
*
* Read flash partition table from command line
*
* mtdparts=<mtddef>[;<mtddef]
* <mtddef> := <mtd-id>:<partdef>[,<partdef>]
* <partdef> := <size>[@offset][<name>][ro]
- * <mtd-id> := unique id used in mapping driver/device
+ * <mtd-id> := unique name used in mapping driver/device (mtd->name)
* <size> := standard linux memsize OR "-" to denote all remaining space
* <name> := '(' NAME ')'
*
return register_mtd_parser(&cmdline_parser);
}
-static void __exit cmdline_parser_exit(void)
-{
- deregister_mtd_parser(&cmdline_parser);
-}
-
module_init(cmdline_parser_init);
-module_exit(cmdline_parser_exit);
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
# drivers/mtd/maps/Kconfig
-# $Id: Kconfig,v 1.4 2003/05/28 15:18:54 dwmw2 Exp $
+# $Id: Kconfig,v 1.10 2004/07/15 00:34:49 dwmw2 Exp $
menu "Self-contained MTD device drivers"
depends on MTD!=n
depends on MTD && PCI
---help---
This provides a MTD device driver for the Ramix PMC551 RAM PCI card
- from Ramix Inc. <http://www.ramix.com/products/>. These devices come
- in memory configurations from 32M - 1G. If you have one, you
- probably want to enable this.
+ from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
+ These devices come in memory configurations from 32M - 1G. If you
+ have one, you probably want to enable this.
If this driver is compiled as a module you get the ability to select
the size of the aperture window pointing into the devices memory.
config MTD_MS02NV
tristate "DEC MS02-NV NVRAM module support"
- depends on CONFIG_MACH_DECSTATION
+ depends on MTD && MACH_DECSTATION
help
- Support for NVRAM module on DECstation.
+ This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery
+ backed-up NVRAM module. The module was originally meant as an NFS
+ accelerator. Say Y here if you have a DECstation 5000/2x0 or a
+ DECsystem 5900 equipped with such a module.
config MTD_SLRAM
tristate "Uncached system RAM"
you can still use it for storage or swap by using this driver to
present it to the system as a Memory Technology Device.
+config MTD_PHRAM
+ tristate "Physical system RAM"
+ depends on MTD
+ help
+ This is a re-implementation of the slram driver above.
+
+ Use this driver to access physical memory that the kernel proper
+ doesn't have access to, memory beyond the mem=xxx limit, nvram,
+ memory on the video card, etc...
+
config MTD_LART
tristate "28F160xx flash driver for LART"
depends on SA1100_LART && MTD
config MTD_DOCPROBE
tristate
- default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MOD_DOC2001PLUS=m)
+ default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m)
default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y
help
This isn't a real config option, it's derived.
+config MTD_DOCECC
+ tristate
+ default m if MTD_DOCPROBE!=y && MTD_NAND_DISKONCHIP!=y && (MTD_DOCPROBE=m || MTD_NAND_DISKONCHIP=m)
+ default y if MTD_DOCPROBE=y || MTD_NAND_DISKONCHIP=y
+ help
+ This isn't a real config option, it's derived.
+
config MTD_DOCPROBE_ADVANCED
bool "Advanced detection options for DiskOnChip"
depends on MTD_DOCPROBE
#
# linux/drivers/devices/Makefile
#
-# $Id: Makefile.common,v 1.3 2003/05/28 10:54:23 dwmw2 Exp $
+# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $
# *** BIG UGLY NOTE ***
#
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
-obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.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
/*
- * $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $
+ * $Id: blkmtd-25.c,v 1.6 2004/07/15 15:09:15 dwmw2 Exp $
*
* blkmtd.c - use a block device as a fake MTD
*
/* Default erase size in K, always make it a multiple of PAGE_SIZE */
#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */
-#define VERSION "$Revision: 1.5 $"
+#define VERSION "$Revision: 1.6 $"
/* Info for the block device */
struct blkmtd_dev {
* (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
*
- * $Id: doc2000.c,v 1.53 2003/06/11 09:45:19 dwmw2 Exp $
+ * $Id: doc2000.c,v 1.60 2004/04/07 08:30:04 gleixner Exp $
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/doc2000.h>
#define DOC_SUPPORT_2000
+#define DOC_SUPPORT_2000TSOP
#define DOC_SUPPORT_MILLENNIUM
#ifdef DOC_SUPPORT_2000
#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)
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 *unused);
+ 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 *unused);
+ 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 iovec *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,
/* 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;
/* 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);
}
}
+ 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
/* Read the manufacturer and device id codes from the device */
- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
- dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
- DoC_Delay(doc, 2);
- mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+ if (DoC_is_Millennium(doc)) {
+ DoC_Delay(doc, 2);
+ dummy = ReadDOC(doc->virtadr, ReadPipeInit);
+ mfr = ReadDOC(doc->virtadr, LastDataRead);
- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
- dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
- DoC_Delay(doc, 2);
- id = ReadDOC_(doc->virtadr, doc->ioreg);
+ 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 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)
if (!doc->mfr) {
doc->mfr = mfr;
doc->id = id;
- doc->chipshift =
- nand_flash_ids[i].chipshift;
- doc->page256 = nand_flash_ids[i].page256;
- doc->pageadrlen =
- nand_flash_ids[i].chipshift > 25 ? 3 : 2;
+ doc->chipshift =
+ 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;
/* 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;
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
struct DiskOnChip *old = NULL;
+ int maxchips;
/* We must avoid being called twice for the same device. */
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((void *) this->virtadr);
+ return;
}
printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
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;
init_MUTEX(&this->lock);
/* Ident all the chips present. */
- DoC_ScanChips(this);
+ DoC_ScanChips(this, maxchips);
if (!this->totlen) {
kfree(mtd);
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);
+ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
}
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 *unused)
+ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long docptr;
unsigned char syndrome[6];
volatile char dummy;
int i, len256 = 0, ret=0;
+ size_t left = len;
docptr = this->virtadr;
down(&this->lock);
- /* Don't allow a single read to cross a 512-byte block boundary */
- if (from + len > ((from | 0x1ff) + 1))
- len = ((from | 0x1ff) + 1) - from;
-
- /* The ECC will not be calculated correctly if less than 512 is read */
- if (len != 0x200 && eccbuf)
- printk(KERN_WARNING
- "ECC needs a full sector read (adr: %lx size %lx)\n",
- (long) from, (long) len);
-
- /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
+ *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;
- /* Find the chip which is to be used and select it */
- mychip = &this->chips[from >> (this->chipshift)];
+ /* The ECC will not be calculated correctly if less than 512 is read */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector read (adr: %lx size %lx)\n",
+ (long) from, (long) len);
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
+ /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
- DoC_Command(this,
- (!this->page256
- && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
- CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
- CDSN_CTRL_ECC_IO);
-
- if (eccbuf) {
- /* Prime the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- }
+ /* Find the chip which is to be used and select it */
+ mychip = &this->chips[from >> (this->chipshift)];
- /* treat crossing 256-byte sector for 2M x 8bits devices */
- if (this->page256 && from + len > (from | 0xff) + 1) {
- len256 = (from | 0xff) + 1 - from;
- DoC_ReadBuf(this, buf, len256);
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
- DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
- DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
- CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
- }
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
- DoC_ReadBuf(this, &buf[len256], len - len256);
+ DoC_Command(this,
+ (!this->page256
+ && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
+ CDSN_CTRL_ECC_IO);
- /* Let the caller know we completed it */
- *retlen = len;
+ if (eccbuf) {
+ /* Prime the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ }
- if (eccbuf) {
- /* Read the ECC data through the DiskOnChip ECC logic */
- /* Note: this will work even with 2M x 8bit devices as */
- /* they have 8 bytes of OOB per 256 page. mf. */
- DoC_ReadBuf(this, eccbuf, 6);
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && from + len > (from | 0xff) + 1) {
+ len256 = (from | 0xff) + 1 - from;
+ DoC_ReadBuf(this, buf, len256);
- /* Flush the pipeline */
- if (DoC_is_Millennium(this)) {
- dummy = ReadDOC(docptr, ECCConf);
- dummy = ReadDOC(docptr, ECCConf);
- i = ReadDOC(docptr, ECCConf);
- } else {
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- dummy = ReadDOC(docptr, 2k_ECCStatus);
- i = ReadDOC(docptr, 2k_ECCStatus);
+ DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
+ CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
- /* Check the ECC Status */
- if (i & 0x80) {
- int nb_errors;
- /* There was an ECC error */
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
+ /* Let the caller know we completed it */
+ *retlen += len;
+
+ if (eccbuf) {
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ /* Note: this will work even with 2M x 8bit devices as */
+ /* they have 8 bytes of OOB per 256 page. mf. */
+ DoC_ReadBuf(this, eccbuf, 6);
+
+ /* Flush the pipeline */
+ if (DoC_is_Millennium(this)) {
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+ i = ReadDOC(docptr, ECCConf);
+ } else {
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ i = ReadDOC(docptr, 2k_ECCStatus);
+ }
+
+ /* Check the ECC Status */
+ if (i & 0x80) {
+ int nb_errors;
+ /* There was an ECC error */
#ifdef ECC_DEBUG
- printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
+ printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
#endif
- /* Read the ECC syndrom through the DiskOnChip ECC logic.
- These syndrome will be all ZERO when there is no error */
- for (i = 0; i < 6; i++) {
- syndrome[i] =
- ReadDOC(docptr, ECCSyndrome0 + i);
- }
- nb_errors = doc_decode_ecc(buf, syndrome);
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] =
+ ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
#ifdef ECC_DEBUG
- printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
+ printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
#endif
- if (nb_errors < 0) {
- /* We return error, but have actually done the read. Not that
- this can be told to user-space, via sys_read(), but at least
- MTD-aware stuff can know about it by checking *retlen */
- ret = -EIO;
- }
- }
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
+ }
#ifdef PSYCHO_DEBUG
- printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
- eccbuf[3], eccbuf[4], eccbuf[5]);
+ printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
+ eccbuf[3], eccbuf[4], eccbuf[5]);
#endif
- /* disable the ECC engine */
- WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
- }
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+ }
- /* according to 11.4.1, we need to wait for the busy line
- * drop if we read to the end of the page. */
- if(0 == ((from + *retlen) & 0x1ff))
- {
- DoC_WaitReady(this);
+ /* 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 + len) & 0x1ff))
+ {
+ DoC_WaitReady(this);
+ }
+
+ from += len;
+ left -= len;
+ buf += len;
}
up(&this->lock);
size_t * retlen, const u_char * buf)
{
char eccbuf[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
}
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 *unused)
+ u_char * eccbuf, struct nand_oobinfo *oobsel)
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
volatile char dummy;
int len256 = 0;
struct Nand *mychip;
+ size_t left = len;
+ int status;
docptr = this->virtadr;
down(&this->lock);
- /* Don't allow a single write to cross a 512-byte block boundary */
- if (to + len > ((to | 0x1ff) + 1))
- len = ((to | 0x1ff) + 1) - to;
+ *retlen = 0;
+ while (left) {
+ len = left;
- /* The ECC will not be calculated correctly if less than 512 is written */
- if (len != 0x200 && eccbuf)
- printk(KERN_WARNING
- "ECC needs a full sector write (adr: %lx size %lx)\n",
- (long) to, (long) len);
+ /* Don't allow a single write to cross a 512-byte block boundary */
+ if (to + len > ((to | 0x1ff) + 1))
+ len = ((to | 0x1ff) + 1) - to;
- /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
+ /* 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 */
- /* Find the chip which is to be used and select it */
- mychip = &this->chips[to >> (this->chipshift)];
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
- if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(this, mychip->floor);
- DoC_SelectChip(this, mychip->chip);
- } else if (this->curchip != mychip->chip) {
- DoC_SelectChip(this, mychip->chip);
- }
+ /* Find the chip which is to be used and select it */
+ mychip = &this->chips[to >> (this->chipshift)];
- this->curfloor = mychip->floor;
- this->curchip = mychip->chip;
+ if (this->curfloor != mychip->floor) {
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
+ }
- /* Set device to main plane of flash */
- DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(this,
- (!this->page256
- && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
- CDSN_CTRL_WP);
+ this->curfloor = mychip->floor;
+ this->curchip = mychip->chip;
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
+ /* Set device to main plane of flash */
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(this,
+ (!this->page256
+ && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
- if (eccbuf) {
- /* Prime the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
- } else {
- /* disable the ECC engine */
- WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
- }
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
- /* treat crossing 256-byte sector for 2M x 8bits devices */
- if (this->page256 && to + len > (to | 0xff) + 1) {
- len256 = (to | 0xff) + 1 - to;
- DoC_WriteBuf(this, buf, len256);
+ if (eccbuf) {
+ /* Prime the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ }
+
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && to + len > (to | 0xff) + 1) {
+ len256 = (to | 0xff) + 1 - to;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk(KERN_ERR "Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+ up(&this->lock);
+ return -EIO;
+ }
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
+ CDSN_CTRL_ECC_IO);
+ }
+
+ DoC_WriteBuf(this, &buf[len256], len - len256);
+
+ if (eccbuf) {
+ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ if (DoC_is_Millennium(this)) {
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ } else {
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
+ 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);
+ }
+
+ /* Reset the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+
+#ifdef PSYCHO_DEBUG
+ printk
+ ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
+#endif
+ }
DoC_Command(this, NAND_CMD_PAGEPROG, 0);
DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
/* There's an implicit DoC_WaitReady() in DoC_Command */
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
+ if (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;
return -EIO;
}
- DoC_Command(this, NAND_CMD_SEQIN, 0);
- DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
- CDSN_CTRL_ECC_IO);
+ /* Let the caller know we completed it */
+ *retlen += len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+ size_t dummy;
+ int ret;
+
+ /* Write the ECC data to flash */
+ for (di=0; di<6; di++)
+ x[di] = eccbuf[di];
+
+ x[6]=0x55;
+ 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;
}
- DoC_WriteBuf(this, &buf[len256], len - len256);
+ up(&this->lock);
+ return 0;
+}
- if (eccbuf) {
- WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
- CDSNControl);
+static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *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);
- if (DoC_is_Millennium(this)) {
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- WriteDOC(0, docptr, NOP);
- } else {
- WriteDOC_(0, docptr, this->ioreg);
- WriteDOC_(0, docptr, this->ioreg);
- WriteDOC_(0, docptr, this->ioreg);
- }
+ size_t totretlen = 0;
+ size_t thisvecofs = 0;
+ int ret= 0;
- /* Read the ECC data through the DiskOnChip ECC logic */
- for (di = 0; di < 6; di++) {
- eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
- }
+ down(&writev_buf_sem);
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ while(count) {
+ size_t thislen, thisretlen;
+ unsigned char *buf;
-#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
- }
+ buf = vecs->iov_base + thisvecofs;
+ thislen = vecs->iov_len - thisvecofs;
- DoC_Command(this, NAND_CMD_PAGEPROG, 0);
- DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- /* There's an implicit DoC_WaitReady() in DoC_Command */
+ 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);
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
+ totretlen += thisretlen;
- if (ReadDOC_(docptr, this->ioreg) & 1) {
- printk(KERN_ERR "Error programming flash\n");
- /* Error in programming */
- *retlen = 0;
- up(&this->lock);
- return -EIO;
- }
+ if (ret || thisretlen != thislen)
+ break;
- /* Let the caller know we completed it */
- *retlen = len;
-
- if (eccbuf) {
- unsigned char x[8];
- size_t dummy;
- int ret;
-
- /* Write the ECC data to flash */
- for (di=0; di<6; di++)
- x[di] = eccbuf[di];
-
- x[6]=0x55;
- x[7]=0x55;
-
- ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
- up(&this->lock);
- return ret;
- }
- up(&this->lock);
- return 0;
+ 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)
{
unsigned long 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]);
DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
+ 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;
DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
+ 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;
volatile int dummy;
unsigned long docptr;
struct Nand *mychip;
+ int status;
down(&this->lock);
DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- dummy = ReadDOC(docptr, CDSNSlowIO);
- DoC_Delay(this, 2);
-
- if (ReadDOC_(docptr, this->ioreg) & 1) {
+ 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 (status & 1) {
printk(KERN_ERR "Error erasing at 0x%x\n", ofs);
/* There was an error */
instr->state = MTD_ERASE_FAILED;
* (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
*
- * $Id: doc2001.c,v 1.41 2003/06/11 09:45:19 dwmw2 Exp $
+ * $Id: doc2001.c,v 1.42 2004/04/04 12:36:45 gleixner Exp $
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
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 *unused);
+ size_t *retlen, u_char *buf, u_char *eccbuf, int 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 *unused);
-
+ size_t *retlen, const u_char *buf, u_char *eccbuf, int 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,
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;
}
}
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);
+ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
}
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 *unused)
+ size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
{
int i, ret;
volatile char dummy;
size_t *retlen, const u_char *buf)
{
char eccbuf[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
}
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 *unused)
+ size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel)
{
int i,ret = 0;
volatile char dummy;
* (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
*
- * $Id: doc2001plus.c,v 1.5 2003/06/11 09:45:19 dwmw2 Exp $
+ * $Id: doc2001plus.c,v 1.8 2004/04/04 12:36:45 gleixner Exp $
+ *
+ * Released under GPL
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
* | 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)
{
- unsigned int ofs = *from & 0x3ff;
- unsigned int cmd;
+ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- if (ofs < 512) {
- cmd = NAND_CMD_READ0;
- ofs &= 0x1ff;
- } else if (ofs < 1014) {
- cmd = NAND_CMD_READ1;
- ofs = (ofs & 0x1ff) + 10;
+ 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 {
- cmd = NAND_CMD_READOOB;
- ofs = ofs - 1014;
+ /* No interleave */
+ if ((*from) & 0x100)
+ return NAND_CMD_READ1;
+ return NAND_CMD_READ0;
}
-
- *from = (*from & ~0x3ff) | ofs;
- return cmd;
}
static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from)
dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
mfr = ReadDOC(docptr, Mil_CDSN_IO);
- dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
+ if (doc->interleave)
+ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
id = ReadDOC(docptr, Mil_CDSN_IO);
- dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
+ if (doc->interleave)
+ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
dummy = ReadDOC(docptr, Mplus_LastDataRead);
dummy = ReadDOC(docptr, Mplus_LastDataRead);
nand_manuf_ids[j].name, nand_flash_ids[i].name);
doc->mfr = mfr;
doc->id = id;
- doc->interleave = 0;
- if (doc->ChipID == DOC_ChipID_DocMilPlus32)
- doc->interleave = 1;
- doc->chipshift = nand_flash_ids[i].chipshift;
+ doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
break;
}
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(this->virtadr, conf, 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;
return -EINVAL;
/* Determine position of OOB flags, before or after data */
- before = to & 0x200;
+ before = (this->interleave && (to & 0x200));
DoC_CheckASIC(docptr);
/* Figure out which region we are accessing... */
fofs = ofs;
base = ofs & 0xf;
- if (base < 6) {
+ 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) {
/* Figure out which region we are accessing... */
fofs = ofs;
base = ofs & 0x0f;
- if (base < 6) {
+ 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) {
/* (C) 1999 Machine Vision Holdings, Inc. */
/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */
-/* $Id: docprobe.c,v 1.36 2003/05/23 11:29:34 dwmw2 Exp $ */
+/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */
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) {
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;
tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
if (tmp != tmpb && tmp == tmpc)
return ChipID;
- break;
default:
break;
}
default:
-#ifndef CONFIG_MTD_DOCPROBE_55AA
- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+#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
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((void *)docptr);
+ return;
+ }
docfound = 1;
mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
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";
/*
* MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
*
- * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $
+ * $Id: lart.c,v 1.6 2004/07/14 17:21:38 dwmw2 Exp $
*
* Author: Abraham vd Merwe <abraham@2d3d.co.za>
*
/*
- * Copyright (c) 2001 Maciej W. Rozycki
+ * Copyright (c) 2001 Maciej W. Rozycki
*
- * 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 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.c,v 1.4 2003/05/20 21:03:07 dwmw2 Exp $
+ * $Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $
*/
#include <linux/init.h>
static char version[] __initdata =
- "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";
+ "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";
MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>");
MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
/*
* 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,
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;
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;
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;
/*
- * Copyright (c) 2001 Maciej W. Rozycki
+ * Copyright (c) 2001, 2003 Maciej W. Rozycki
*
- * 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.
+ * DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for
+ * DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260
+ * systems.
*
- * $Id: ms02-nv.h,v 1.1 2002/09/13 13:46:55 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.
+ *
+ * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $
*/
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
+/*
+ * 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 {
/*
- * $Id: pmc551.c,v 1.24 2003/05/20 21:03:08 dwmw2 Exp $
+ * $Id: pmc551.c,v 1.26 2004/07/14 17:25:07 dwmw2 Exp $
*
* PMC551 PCI Mezzanine Ram Device
*
/* This version ported to the Linux-MTD system by dwmw2@infradead.org
- * $Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $
+ * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $
*
* Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
int init_ftl(void)
{
- DEBUG(0, "$Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $\n");
+ DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n");
return register_mtd_blktrans(&ftl_tr);
}
* (c) 1999 Machine Vision Holdings, Inc.
* Author: David Woodhouse <dwmw2@infradead.org>
*
- * $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $
+ * $Id: inftlcore.c,v 1.16 2004/07/12 12:34:58 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
struct INFTLrecord *inftl;
unsigned long temp;
- if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
+ 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);
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");
if (BlockMap[block] == BLOCK_NIL)
continue;
- ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
+ ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
- &retlen, movebuf, (char *)&oob, NULL);
+ &retlen, movebuf);
if (ret < 0) {
- ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
+ ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE),
- SECTORSIZE, &retlen, movebuf, (char *)&oob,
- NULL);
+ 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, NULL);
+ movebuf, (char *)&oob, &inftl->oobinfo);
}
/*
if (INFTL_formatblock(inftl, thisEUN) < 0) {
/*
* Could not erase : mark block as reserved.
- * FixMe: Update Bad Unit Table on disk.
*/
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else {
if (INFTL_formatblock(inftl, thisEUN) < 0) {
/*
* Could not erase : mark block as reserved.
- * FixMe: Update Bad Unit Table on medium.
*/
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else {
unsigned int writeEUN;
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
size_t retlen;
- u8 eccbuf[6];
+ struct inftl_oob oob;
char *p, *pend;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%ld,"
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 *)eccbuf, NULL);
+ (char *)&oob, &inftl->oobinfo);
/*
- * No need to write SECTOR_USED flags since they are written
+ * need to write SECTOR_USED flags since they are not written
* in mtd_writeecc
*/
} else {
} else {
size_t retlen;
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
- u_char eccbuf[6];
- if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
- buffer, eccbuf, NULL))
+ if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
+ buffer))
return -EIO;
}
return 0;
int __init init_inftl(void)
{
- printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, "
+ printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.16 $, "
"inftlmount.c %s\n", inftlmountrev);
return register_mtd_blktrans(&inftl_tr);
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
*
- * $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 Exp $
+ * $Id: inftlmount.c,v 1.13 2004/06/28 16:06:36 dbrown 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
#include <linux/mtd/inftl.h>
#include <linux/mtd/compatmac.h>
-char inftlmountrev[]="$Revision: 1.11 $";
+char inftlmountrev[]="$Revision: 1.13 $";
/*
* find_boot_record: Find the INFTL Media Header and its Spare copy which
{
struct inftl_unittail h1;
//struct inftl_oob oob;
- unsigned int i, block, boot_record_count = 0;
+ unsigned int i, block;
u8 buf[SECTORSIZE];
struct INFTLMediaHeader *mh = &inftl->MediaHdr;
struct INFTLPartition *ip;
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
inftl->MediaUnit = BLOCK_NIL;
- inftl->SpareMediaUnit = BLOCK_NIL;
/* Search for a valid boot record */
for (block = 0; block < inftl->nb_blocks; block++) {
* Check for BNAND header first. Then whinge if it's found
* but later checks fail.
*/
- if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
- SECTORSIZE, &retlen, buf))) {
+ 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) {
continue;
}
- if (boot_record_count) {
- /*
- * We've already processed one. So we just check if
- * this one is the same as the first one we found.
- */
- if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
- printk(KERN_WARNING "INFTL: Media Headers at "
- "0x%x and 0x%x disagree.\n",
- inftl->MediaUnit * inftl->EraseSize,
- block * inftl->EraseSize);
- return -1;
- }
- if (boot_record_count == 1)
- inftl->SpareMediaUnit = block;
-
- /*
- * Mark this boot record (INFTL MediaHeader) block as
- * reserved.
- */
- inftl->PUtable[block] = BLOCK_RESERVED;
-
- boot_record_count++;
- 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);
"UnitSizeFactor 0x%02x is experimental\n",
mh->BlockMultiplierBits);
inftl->EraseSize = inftl->mbd.mtd->erasesize <<
- (0xff - mh->BlockMultiplierBits);
+ mh->BlockMultiplierBits;
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+ block >>= mh->BlockMultiplierBits;
}
/* Scan the partitions */
/* Mark this boot record (NFTL MediaHeader) block as reserved */
inftl->PUtable[block] = BLOCK_RESERVED;
-#if 0
/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
for (i = 0; i < inftl->nb_blocks; i++) {
- if ((i & (SECTORSIZE - 1)) == 0) {
- /* read one sector for every SECTORSIZE of blocks */
- if ((ret = MTD_READECC(inftl->mbd.mtd,
- block * inftl->EraseSize + i + SECTORSIZE,
- SECTORSIZE, &retlen, buf,
- (char *)&oob, NULL)) < 0) {
- printk(KERN_WARNING "INFTL: read of "
- "bad sector table failed "
- "(err %d)\n", ret);
- kfree(inftl->VUtable);
- kfree(inftl->PUtable);
- return -1;
- }
+ 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;
}
- /* Mark the Bad Erase Unit as RESERVED in PUtable */
- if (buf[i & (SECTORSIZE - 1)] != 0xff)
- inftl->PUtable[i] = BLOCK_RESERVED;
}
-#endif
inftl->MediaUnit = block;
- boot_record_count++;
+ return 0;
}
-
- return boot_record_count ? 0 : -1;
+
+ /* Not found. */
+ return -1;
}
static int memcmpb(void *a, int c, int n)
int len, int check_oob)
{
int i, retlen;
- u8 buf[SECTORSIZE];
+ u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
"address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
address, len, check_oob);
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(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
+ 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 (MTD_READOOB(inftl->mbd.mtd, address,
- inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
- return -1;
- if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
+ if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
return -1;
}
address += SECTORSIZE;
* Return: 0 when succeed, -1 on error.
*
* ToDo: 1. Is it neceressary to check_free_sector after erasing ??
- * 2. UnitSizeFactor != 0xFF
*/
int INFTL_formatblock(struct INFTLrecord *inftl, int block)
{
int retlen;
struct inftl_unittail uci;
struct erase_info *instr = &inftl->instr;
+ int physblock;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
"block=%d)\n", (int)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->addr = block * inftl->EraseSize;
- instr->len = inftl->EraseSize;
- MTD_ERASE(inftl->mbd.mtd, instr);
+ 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;
+ }
- if (instr->state == MTD_ERASE_FAILED) {
/*
- * Could not format, FixMe: We should update the BadUnitTable
- * both in memory and on disk.
- */
- printk(KERN_WARNING "INFTL: error while formatting block %d\n",
- block);
- return -1;
+ * 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;
}
- /*
- * 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, inftl->EraseSize, 1) != 0)
- return -1;
-
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;
- if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 +
+ instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
+ if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
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 PUtable to BLOCK_RESERVED on failure) */
+ inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr);
+ return -1;
}
/*
if (INFTL_formatblock(inftl, block) < 0) {
/*
* Cannot format !!!! Mark it as Bad Unit,
- * FixMe: update the BadUnitTable on disk.
*/
inftl->PUtable[block] = BLOCK_RESERVED;
} else {
# drivers/mtd/maps/Kconfig
-# $Id: Kconfig,v 1.12 2003/06/23 07:38:11 dwmw2 Exp $
+# $Id: Kconfig,v 1.29 2004/07/15 15:29:17 dwmw2 Exp $
menu "Mapping drivers for chip access"
depends on MTD!=n
BE VERY CAREFUL.
-config MTD_ICH2ROM
- tristate "BIOS flash chip on Intel Hub Controller 2"
+config MTD_ICHXROM
+ tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5"
depends on X86 && MTD_JEDECPROBE && MTD_COMPLEX_MAPPINGS
help
- Support for treating the BIOS flash chip on ICH2 motherboards
+ Support for treating the BIOS flash chip on ICHX motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_LASAT
tristate "Flash chips on LASAT board"
- depends on LASAT && MTD_CFI
+ depends on LASAT
help
Support for the flash chips on the Lasat 100 and 200 boards.
You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use
both banks.
+config MTD_PB1550
+ tristate "Flash devices on Alchemy PB1550 board"
+ depends on MIPS && MIPS_PB1550
+ help
+ Flash memory access on Alchemy Pb1550 board
+
+config MTD_PB1550_BOOT
+ bool "PB1550 boot flash device"
+ depends on MTD_PB1550
+ help
+ Use the first of the two 64MiB flash banks on Pb1550 board.
+ You can say 'Y' to both this and 'MTD_PB1550_USER' below, to use
+ both banks.
+
+config MTD_PB1550_USER
+ bool "PB1550 user flash device"
+ depends on MTD_PB1550
+ default y if MTD_PB1550_BOOT = n
+ help
+ Use the second of the two 64MiB flash banks on Pb1550 board.
+ You can say 'Y' to both this and 'MTD_PB1550_BOOT' above, to use
+ both banks.
+
+config MTD_DB1550
+ tristate "Flash devices on Alchemy DB1550 board"
+ depends on MIPS && MIPS_DB1550
+ help
+ Flash memory access on Alchemy Db1550 board
+
+config MTD_DB1550_BOOT
+ bool "DB1550 boot flash device"
+ depends on MTD_DB1550
+ help
+ Use the first of the two 64MiB flash banks on Db1550 board.
+ You can say 'Y' to both this and 'MTD_DB1550_USER' below, to use
+ both banks.
+
+config MTD_DB1550_USER
+ bool "DB1550 user flash device"
+ depends on MTD_DB1550
+ default y if MTD_DB1550_BOOT = n
+ help
+ Use the second of the two 64MiB flash banks on Db1550 board.
+ You can say 'Y' to both this and 'MTD_DB1550_BOOT' above, to use
+ both banks.
+
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT
BE VERY CAREFUL.
+config MTD_SBC8240
+ tristate "Flash device on SBC8240"
+ depends on PPC32 && MTD_JEDECPROBE && 6xx && 8260
+ help
+ Flash access on the SBC8240 board from Wind River. See
+ <http://www.windriver.com/products/sbc8240/>
+
config MTD_TQM8XXL
tristate "CFI Flash device mapped on TQM8XXL"
depends on MTD_CFI && PPC32 && 8xx && TQM8xxL
config MTD_DBOX2
tristate "CFI Flash device mapped on D-Box2"
- depends on PPC32 && 8xx && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD
+ depends on PPC32 && 8xx && DBOX2 && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD
help
This enables access routines for the flash chips on the Nokia/Sagem
D-Box 2 board. If you have one of these boards and would like to use
PhotoMax Digital Picture Frame.
If you have such a device, say 'Y'.
+config MTD_NOR_TOTO
+ tristate "NOR Flash device on TOTO board"
+ depends on ARM && ARCH_OMAP && OMAP_TOTO
+ help
+ This enables access to the NOR flash on the Texas Instruments
+ TOTO board.
+
config MTD_H720X
tristate "Hynix evaluation board mappings"
depends on ARM && MTD_CFI && ( ARCH_H7201 || ARCH_H7202 )
This enables access to the flash chips on the Hynix evaluation boards.
If you have such a board, say 'Y'.
+config MTD_MPC1211
+ tristate "CFI Flash device mapped on Interface MPC-1211"
+ depends on SUPERH && SH_MPC1211 && MTD_CFI
+ help
+ This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02).
+ If you have such a board, say 'Y'.
+
# This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI
tristate "PCI MTD driver"
config MTD_WRSBC8260
tristate "Map driver for WindRiver PowerQUICC II MPC82xx board"
- depends on MTD_PARTITIONS && SBC82xx
+ depends on (SBC82xx || SBC8560)
+ select MTD_PARTITIONS
+ select MTD_MAP_BANK_WIDTH_4
+ select MTD_MAP_BANK_WIDTH_1
+ select MTD_CFI_I1
+ select MTD_CFI_I4
help
Map driver for WindRiver PowerQUICC II MPC82xx board. Drives
all three flash regions on CS0, CS1 and CS6 if they are configured
correctly by the boot loader.
+config MTD_DMV182
+ tristate "Map driver for Dy-4 SVME/DMV-182 board."
+ depends on DMV182
+ select MTD_PARTITIONS
+ select MTD_MAP_BANK_WIDTH_32
+ select MTD_CFI_I8
+ select MTD_CFI_AMDSTD
+ help
+ Map driver for Dy-4 SVME/DMV-182 board.
+
endmenu
#
# linux/drivers/maps/Makefile
#
-# $Id: Makefile.common,v 1.2 2003/05/28 10:48:41 dwmw2 Exp $
+# $Id: Makefile.common,v 1.14 2004/07/12 16:07:31 dwmw2 Exp $
ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
obj-$(CONFIG_MTD) += map_funcs.o
obj-$(CONFIG_MTD_IQ80310) += iq80310.o
obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o
+obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
obj-$(CONFIG_MTD_MBX860) += mbx860.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o
+obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
+obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o
+obj-$(CONFIG_MTD_DB1550) += db1550-flash.o
obj-$(CONFIG_MTD_LASAT) += lasat.o
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
obj-$(CONFIG_MTD_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_BEECH) += beech-mtd.o
obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.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_WRSBC8260) += wr_sbc82xx_flash.o
+obj-$(CONFIG_MTD_DMV182) += dmv182.o
* amd76xrom.c
*
* Normal mappings of chips in physical memory
- * $Id: amd76xrom.c,v 1.8 2003/05/28 15:44:28 dwmw2 Exp $
+ * $Id: amd76xrom.c,v 1.12 2004/07/14 14:44:31 thayne Exp $
*/
#include <linux/module.h>
#include <linux/pci_ids.h>
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
+
+#define MTD_DEV_NAME_LENGTH 16
+
struct amd76xrom_map_info {
struct map_info map;
struct mtd_info *mtd;
unsigned long window_addr;
u32 window_start, window_size;
struct pci_dev *pdev;
+ struct resource window_rsrc;
+ struct resource rom_rsrc;
+ char mtd_name[MTD_DEV_NAME_LENGTH];
};
static struct amd76xrom_map_info amd76xrom_map = {
.map = {
- .name = "AMD76X rom",
+ .name = MOD_NAME,
.size = 0,
- .buswidth = 1,
- },
- .mtd = NULL,
- .window_addr = 0,
+ .bankwidth = 1,
+ }
+ /* remaining fields of structure are initialized to 0 */
};
+
+static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
+{
+ u8 byte;
+
+ /* Disable writes through the rom window */
+ pci_read_config_byte(info->pdev, 0x40, &byte);
+ pci_write_config_byte(info->pdev, 0x40, byte & ~1);
+
+ if (info->mtd) {
+ del_mtd_device(info->mtd);
+ map_destroy(info->mtd);
+ info->mtd = NULL;
+ info->map.virt = 0;
+ }
+ if (info->rom_rsrc.parent)
+ release_resource(&info->rom_rsrc);
+ if (info->window_rsrc.parent)
+ release_resource(&info->window_rsrc);
+
+ if (info->window_addr) {
+ iounmap((void *)(info->window_addr));
+ info->window_addr = 0;
+ }
+}
+
+
static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
u8 segen_bits;
};
static struct rom_window rom_window[] = {
+ /*
+ * Need the 5MiB window for chips that have block lock/unlock
+ * registers located below 4MiB window.
+ */
{ 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
{ 0xffc00000, 4*1024*1024, (1<<7), },
{ 0xffff0000, 64*1024, 0 },
int i;
u32 rom_size;
+ info->pdev = pdev;
window = &rom_window[0];
- /* disabled because it fights with BIOS reserved regions */
-#define REQUEST_MEM_REGION 0
-#if REQUEST_MEM_REGION
- while(window->size) {
- if (request_mem_region(window->start, window->size, "amd76xrom")) {
- break;
+ while (window->size) {
+ /*
+ * 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.
+ */
+ info->window_rsrc.name = MOD_NAME;
+ info->window_rsrc.start = window->start;
+ info->window_rsrc.end = window->start + window->size - 1;
+ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (request_resource(&iomem_resource, &info->window_rsrc)) {
+ info->window_rsrc.parent = NULL;
+ printk(KERN_ERR MOD_NAME
+ " %s(): Unable to register resource"
+ " 0x%.08lx-0x%.08lx - kernel bug?\n",
+ __func__,
+ info->window_rsrc.start, info->window_rsrc.end);
}
- window++;
- }
- if (!window->size) {
- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n");
- goto err_out_none;
- }
-#endif /* REQUEST_MEM_REGION */
- /* Enable the selected rom window */
- pci_read_config_byte(pdev, 0x43, &byte);
- pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
+ /* Enable the selected rom window */
+ pci_read_config_byte(pdev, 0x43, &byte);
+ pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
- /* Enable writes through the rom window */
- pci_read_config_byte(pdev, 0x40, &byte);
- pci_write_config_byte(pdev, 0x40, byte | 1);
+ /* Enable writes through the rom window */
+ pci_read_config_byte(pdev, 0x40, &byte);
+ pci_write_config_byte(pdev, 0x40, byte | 1);
- /* FIXME handle registers 0x80 - 0x8C the bios region locks */
+ /* 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);
+ printk(KERN_NOTICE MOD_NAME " 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);
- if (!info->window_addr) {
- printk(KERN_ERR "Failed to ioremap\n");
- goto err_out_free_mmio_region;
- }
- info->mtd = NULL;
- for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
- char **chip_type;
- if (rom_size > window->size) {
+ if (!info->window_addr) {
+ printk(KERN_ERR "Failed to ioremap\n");
continue;
}
- info->map.phys = window->start + window->size - rom_size;
- info->map.virt =
- info->window_addr + window->size - rom_size;
- info->map.size = rom_size;
- simple_map_init(&info->map);
- 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;
+
+ info->mtd = NULL;
+
+ for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
+ char **chip_type;
+ if (rom_size > window->size) {
+ continue;
+ }
+ info->map.phys = window->start + window->size - rom_size;
+ info->map.virt =
+ info->window_addr + window->size - rom_size;
+ info->map.size = rom_size;
+ simple_map_init(&info->map);
+ 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) goto found_mtd;
}
+ 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);
+
+ window++;
}
- if (!info->mtd) {
- goto err_out_iounmap;
- }
- printk(KERN_NOTICE "amd76xrom chip at offset: 0x%x\n",
+ goto failed;
+
+ found_mtd:
+ printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n",
window->size - rom_size);
-
+
info->mtd->owner = THIS_MODULE;
+
+ if (!info->window_rsrc.parent) {
+ /* failed to reserve entire window - try fragments */
+ info->window_rsrc.name = MOD_NAME;
+ info->window_rsrc.start = window->start;
+ info->window_rsrc.end = window->start + window->size - rom_size - 1;
+ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (request_resource(&iomem_resource, &info->window_rsrc)) {
+ printk(KERN_ERR MOD_NAME
+ ": cannot reserve window resource fragment\n");
+#if 0
+ /*
+ * The BIOS e820 usually reserves this so it isn't
+ * usually an error.
+ */
+ goto failed;
+#endif
+ }
+ }
+
add_mtd_device(info->mtd);
info->window_start = window->start;
info->window_size = window->size;
+
+ if (info->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.
+ */
+ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
+ "mtd%d", info->mtd->index);
+
+ info->rom_rsrc.name = info->mtd_name;
+ info->rom_rsrc.start = window->start + window->size - rom_size;
+ info->rom_rsrc.end = window->start + window->size - 1;
+ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
+ printk(KERN_ERR MOD_NAME
+ ": cannot reserve MTD resource\n");
+ info->rom_rsrc.parent = NULL;
+ }
+ }
+
return 0;
-err_out_iounmap:
- iounmap((void *)(info->window_addr));
-err_out_free_mmio_region:
-#if REQUEST_MEM_REGION
- release_mem_region(window->start, window->size);
-err_out_none:
-#endif /* REQUEST_MEM_REGION */
+ failed:
+ amd76xrom_cleanup(info);
return -ENODEV;
}
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 = NULL;
- info->map.virt = 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);
-#if REQUEST_MEM_REGION
- release_mem_region(info->window_start, info->window_size);
-#endif /* REQUEST_MEM_REGION */
+ amd76xrom_cleanup(info);
}
static struct pci_device_id amd76xrom_pci_tbl[] = {
#if 0
static struct pci_driver amd76xrom_driver = {
- .name = "amd76xrom",
+ .name = MOD_NAME,
.id_table = amd76xrom_pci_tbl,
.probe = amd76xrom_init_one,
.remove = amd76xrom_remove_one,
/*
- * $Id: arctic-mtd.c,v 1.10 2003/06/02 16:37:59 trini Exp $
+ * $Id: arctic-mtd.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $
*
* drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for
* IBM 405LP Arctic boards.
static struct map_info arctic_mtd_map = {
.name = NAME,
.size = SIZE,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
.phys = PADDR,
};
* NV-RAM memory access on autcpu12
* (C) 2002 Thomas Gleixner (gleixner@autronix.de)
*
- * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: autcpu12-nvram.c,v 1.6 2004/07/12 21:59:43 dwmw2 Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
struct map_info autcpu12_sram_map = {
.name = "SRAM",
.size = 32768,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = 0x12000000,
};
/*
- * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: beech-mtd.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $
*
* drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for
* IBM 405LP Beech boards.
static struct map_info beech_mtd_map = {
.name = NAME,
.size = SIZE,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
.phys = PADDR
};
/*
* Flash on Cirrus CDB89712
*
- * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: cdb89712.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $
*/
#include <linux/module.h>
struct map_info cdb89712_flash_map = {
.name = "flash",
.size = FLASH_SIZE,
- .buswidth = FLASH_WIDTH,
+ .bankwidth = FLASH_WIDTH,
.phys = FLASH_START,
};
struct map_info cdb89712_sram_map = {
.name = "SRAM",
.size = SRAM_SIZE,
- .buswidth = SRAM_WIDTH,
+ .bankwidth = SRAM_WIDTH,
.phys = SRAM_START,
};
struct map_info cdb89712_bootrom_map = {
.name = "BootROM",
.size = BOOTROM_SIZE,
- .buswidth = BOOTROM_WIDTH,
+ .bankwidth = BOOTROM_WIDTH,
.phys = BOOTROM_START,
};
*
* (C) 2000 Nicolas Pitre <nico@cam.org>
*
- * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: ceiva.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $
*/
#include <linux/config.h>
}
clps[i].map->virt = (unsigned long)clps[i].vbase;
- clps[i].map->buswidth = clps[i].width;
+ clps[i].map->bankwidth = clps[i].width;
clps[i].map->size = clps[i].size;
simple_map_init(&clps[i].map);
/*
* Copyright © 2001 Flaga hf. Medical Devices, Kári DavÃðsson <kd@flaga.is>
*
- * $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: cfi_flagadm.c,v 1.12 2004/07/12 21:59:43 dwmw2 Exp $
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
struct map_info flagadm_map = {
.name = "FlagaDM flash device",
.size = FLASH_SIZE,
- .buswidth = 2,
+ .bankwidth = 2,
};
struct mtd_partition flagadm_parts[] = {
/*
- * $Id: cstm_mips_ixx.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: cstm_mips_ixx.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $
*
* Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
* Config with both CFI and JEDEC device support.
char *name;
unsigned long window_addr;
unsigned long window_size;
- int buswidth;
+ int bankwidth;
int num_partitions;
};
"big flash", // name
0x08000000, // window_addr
0x02000000, // window_size
- 4, // buswidth
+ 4, // bankwidth
1, // num_partitions
}
"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
},
}
cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
- cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
+ cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth;
#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
#endif
/*
- * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: dbox2-flash.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $
*
* D-Box 2 flash driver
*/
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/config.h>
+#include <linux/errno.h>
/* partition_info gives details on the logical partitions that the split the
* single flash device into. If the size if zero we use up to the end of the
.mask_flags = MTD_WRITEABLE
},
{
- .name = "flfs (ppcboot)",
+ .name = "FLFS (U-Boot)",
.size = 128 * 1024,
.offset = MTDPART_OFS_APPEND,
.mask_flags = 0
},
{
- .name = "root (cramfs)",
+ .name = "Root (SquashFS)",
.size = 7040 * 1024,
.offset = MTDPART_OFS_APPEND,
.mask_flags = 0
},
{
- .name = "var (jffs2)",
+ .name = "var (JFFS2)",
.size = 896 * 1024,
.offset = MTDPART_OFS_APPEND,
.mask_flags = 0
},
{
- .name = "flash without bootloader",
+ .name = "Flash without bootloader",
.size = MTDPART_SIZ_FULL,
.offset = 128 * 1024,
.mask_flags = 0
},
{
- .name = "complete flash",
+ .name = "Complete Flash",
.size = MTDPART_SIZ_FULL,
.offset = 0,
.mask_flags = MTD_WRITEABLE
struct map_info dbox2_flash_map = {
.name = "D-Box 2 flash memory",
.size = WINDOW_SIZE,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = WINDOW_ADDR,
};
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);
}
*
* This code is GPL
*
- * $Id: dc21285.c,v 1.15 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: dc21285.c,v 1.20 2004/07/12 22:38:29 dwmw2 Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <asm/io.h>
#include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
-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) {
+ 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
+
+static map_word dc21285_read8(struct map_info *map, unsigned long ofs)
{
- return *(__u8*)(map->map_priv_1 + ofs);
+ return *(uint8_t*)(map->map_priv_1 + ofs);
}
-__u16 dc21285_read16(struct map_info *map, unsigned long ofs)
+static map_word dc21285_read16(struct map_info *map, unsigned long ofs)
{
- return *(__u16*)(map->map_priv_1 + ofs);
+ return *(uint16_t*)(map->map_priv_1 + ofs);
}
-__u32 dc21285_read32(struct map_info *map, unsigned long ofs)
+static map_word dc21285_read32(struct map_info *map, unsigned long ofs)
{
- return *(__u32*)(map->map_priv_1 + ofs);
+ return *(uint32_t*)(map->map_priv_1 + ofs);
}
-void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy(to, (void*)(map->map_priv_1 + from), len);
}
-void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr)
+static void dc21285_write(struct map_info *map, 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->map_priv_1 + adr) = d.x[0];
}
-void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void dc21285_write16(struct map_info *map, 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->map_priv_1 + adr) = d.x[0];
}
-void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr)
+static void dc21285_write32(struct map_info *map, map_word d, unsigned long adr)
{
- *(__u32*)(map->map_priv_1 + adr) = d;
+ if (machine_is_netwinder())
+ nw_en_write();
+ *(uint32_t*)(map->map_priv_1 + 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)++;
- dc21285_write32(map, d, to);
- to += 4;
- len -= 4;
- }
- break;
- case 2:
- while (len > 0) {
- __u16 d = *((__u16*)from)++;
- dc21285_write16(map, d, to);
- to += 2;
- len -= 2;
- }
- break;
- case 1:
- while (len > 0) {
- __u8 d = *((__u8*)from)++;
- dc21285_write8(map, d, to);
- to++;
- len--;
- }
- break;
+ while (len > 0) {
+ uint32_t d = *((uint32_t*)from)++;
+ dc21285_write32(map, d, to);
+ to += 4;
+ len -= 4;
+ }
+}
+
+static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+ while (len > 0) {
+ uint16_t d = *((uint16_t*)from)++;
+ dc21285_write16(map, d, to);
+ to += 2;
+ len -= 2;
}
}
-struct map_info dc21285_map = {
+static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+ uint8_t d = *((uint8_t*)from)++;
+ dc21285_write8(map, d, to);
+ to++;
+ len--;
+}
+
+static struct map_info dc21285_map = {
.name = "DC21285 flash",
.phys = NO_XIP,
.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 const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
#endif
-int __init init_dc21285(void)
+static int __init init_dc21285(void)
{
- /*
- * Flash timing is determined with bits 19-16 of the
- * CSR_SA110_CNTL. The value is the number of wait cycles, or
- * 0 for 16 cycles (the default). Cycles are 20 ns.
- * Here we use 7 for 140 ns flash chips.
- */
- /* access time */
- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
- /* burst time */
- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
- /* tristate time */
- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
-
- /* 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;
break;
+ dc21285_map.read = dc21285_read32;
+ dc21285_map.write = dc21285_write32;
+ dc21285_map.copy_to = dc21285_copy_to_32;
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);
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->owner = THIS_MODULE;
-
- /* partition fixup */
+ if (!dc21285_mtd) {
+ iounmap((void *)dc21285_map.map_priv_1);
+ return -ENXIO;
+ }
+
+ dc21285_mtd->owner = THIS_MODULE;
#ifdef CONFIG_MTD_PARTITIONS
- nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0);
- if (nrparts > 0) {
- add_mtd_partitions(mymtd, dc21285_parts, nrparts);
- return 0;
- }
-#endif
- add_mtd_device(mymtd);
- return 0;
+ nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, (void *)0);
+ if (nrparts > 0)
+ add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts);
+ else
+#endif
+ 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
+ * 0 for 16 cycles (the default). Cycles are 20 ns.
+ * Here we use 7 for 140 ns flash chips.
+ */
+ /* access time */
+ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
+ /* burst time */
+ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
+ /* tristate time */
+ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
}
-
- iounmap((void *)dc21285_map.map_priv_1);
- return -ENXIO;
+
+ return 0;
}
static void __exit cleanup_dc21285(void)
{
#ifdef CONFIG_MTD_PARTITIONS
if (dc21285_parts) {
- del_mtd_partitions(mymtd);
+ del_mtd_partitions(dc21285_mtd);
kfree(dc21285_parts);
} else
#endif
- del_mtd_device(mymtd);
+ del_mtd_device(dc21285_mtd);
- map_destroy(mymtd);
+ map_destroy(dc21285_mtd);
iounmap((void *)dc21285_map.map_priv_1);
}
* 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.12 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: dilnetpc.c,v 1.13 2004/07/12 21:59:44 dwmw2 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
static struct map_info dnpc_map = {
.name = "ADNP Flash Bank",
.size = ADNP_WINDOW_SIZE,
- .buswidth = 1,
+ .bankwidth = 1,
.set_vpp = adnp_set_vpp,
.phys = WINDOW_ADDR
};
/*
- * $Id: ebony.c,v 1.8 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: ebony.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $
*
* Mapping for Ebony user flash
*
#include <linux/mtd/partitions.h>
#include <linux/config.h>
#include <asm/io.h>
-#include <asm/ibm440.h>
+#include <asm/ibm44x.h>
#include <platforms/ebony.h>
static struct mtd_info *flash;
static struct map_info ebony_small_map = {
.name = "Ebony small flash",
.size = EBONY_SMALL_FLASH_SIZE,
- .buswidth = 1,
+ .bankwidth = 1,
};
static struct map_info ebony_large_map = {
.name = "Ebony large flash",
.size = EBONY_LARGE_FLASH_SIZE,
- .buswidth = 1,
+ .bankwidth = 1,
};
static struct mtd_partition ebony_small_partitions[] = {
return -ENOMEM;
fpga0_reg = readb(fpga0_adr);
- iounmap64(fpga0_adr);
+ iounmap(fpga0_adr);
if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
!EBONY_FLASH_SEL(fpga0_reg))
/*
- * $Id: edb7312.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: edb7312.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $
*
* Handle mapping of the NOR flash on Cogent EDB7312 boards
*
#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 */
struct map_info edb7312nor_map = {
.name = "NOR flash on EDB7312",
.size = WINDOW_SIZE,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR,
};
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- $Id: elan-104nc.c,v 1.18 2003/06/23 07:37:02 dwmw2 Exp $
+ $Id: elan-104nc.c,v 1.21 2004/07/12 22:38:29 dwmw2 Exp $
The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16
mode. This drivers uses the CFI probe and Intel Extended Command Set drivers.
}
-static __u8 elan_104nc_read8(struct map_info *map, unsigned long ofs)
+static map_word elan_104nc_read16(struct map_info *map, unsigned long ofs)
{
- __u8 ret;
+ map_word ret;
spin_lock(&elan_104nc_spin);
elan_104nc_page(map, ofs);
- ret = readb(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&elan_104nc_spin);
- return ret;
-}
-
-static __u16 elan_104nc_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&elan_104nc_spin);
- elan_104nc_page(map, ofs);
- ret = readw(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&elan_104nc_spin);
- return ret;
-}
-
-static __u32 elan_104nc_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&elan_104nc_spin);
- elan_104nc_page(map, ofs);
- ret = readl(iomapadr + (ofs & WINDOW_MASK));
+ ret.x[0] = readw(iomapadr + (ofs & WINDOW_MASK));
spin_unlock(&elan_104nc_spin);
return ret;
}
static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- while(len) {
+ while (len) {
unsigned long thislen = len;
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
}
}
-static void elan_104nc_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
- spin_lock(&elan_104nc_spin);
- elan_104nc_page(map, adr);
- writeb(d, iomapadr + (adr & WINDOW_MASK));
- spin_unlock(&elan_104nc_spin);
-}
-
-static void elan_104nc_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
- spin_lock(&elan_104nc_spin);
- elan_104nc_page(map, adr);
- writew(d, iomapadr + (adr & WINDOW_MASK));
- spin_unlock(&elan_104nc_spin);
-}
-
-static void elan_104nc_write32(struct map_info *map, __u32 d, unsigned long adr)
+static void elan_104nc_write16(struct map_info *map, map_word d, unsigned long adr)
{
spin_lock(&elan_104nc_spin);
elan_104nc_page(map, adr);
- writel(d, iomapadr + (adr & WINDOW_MASK));
+ writew(d.x[0], iomapadr + (adr & WINDOW_MASK));
spin_unlock(&elan_104nc_spin);
}
.size = 8*1024*1024, /* this must be set to a maximum possible amount
of flash so the cfi probe routines find all
the chips */
- .buswidth = 2,
- .read8 = elan_104nc_read8,
- .read16 = elan_104nc_read16,
- .read32 = elan_104nc_read32,
+ .bankwidth = 2,
+ .read = elan_104nc_read16,
.copy_from = elan_104nc_copy_from,
- .write8 = elan_104nc_write8,
- .write16 = elan_104nc_write16,
- .write32 = elan_104nc_write32,
+ .write = elan_104nc_write16,
.copy_to = elan_104nc_copy_to
};
* Copyright (C) 2001 Altera Corporation
* Copyright (C) 2001 Red Hat, Inc.
*
- * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: epxa10db-flash.c,v 1.11 2004/07/12 21:59:44 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
static struct map_info epxa_map = {
.name = "EPXA flash",
.size = FLASH_SIZE,
- .buswidth = 2,
+ .bankwidth = 2,
.phys = FLASH_START,
};
/* fortunet.c memory map
*
- * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $
+ * $Id: fortunet.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $
*/
#include <linux/module.h>
struct map_region
{
int window_addr_physical;
- int altbuswidth;
+ int altbankwidth;
struct map_info map_info;
struct mtd_info *mymtd;
struct mtd_partition parts[MAX_NUM_PARTITIONS];
struct map_info default_map = {
.size = DEF_WINDOW_SIZE,
- .buswidth = 4,
+ .bankwidth = 4,
};
static char * __init get_string_option(char *dest,int dest_size,char *sor)
if(params[0]<1)
{
printk(MTD_FORTUNET_PK "Bad parameters for MTD Region "
- " name,region-number[,base,size,buswidth,altbuswidth]\n");
+ " name,region-number[,base,size,bankwidth,altbankwidth]\n");
return 1;
}
if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS))
&default_map,sizeof(map_regions[params[1]].map_info));
map_regions_set[params[1]] = 1;
map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY;
- map_regions[params[1]].altbuswidth = 2;
+ 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]>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;
}
sizeof(map_regions[ix].map_info));
map_regions_set[ix] = 1;
map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY;
- map_regions[ix].altbuswidth = 2;
+ 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");
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 alternate 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);
}
* Flash memory access on Hynix GMS30C7201/HMS30C7202 based
* evaluation boards
*
+ * $Id: h720x-flash.c,v 1.9 2004/07/14 17:45:40 dwmw2 Exp $
+ *
* (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
* 2003 Thomas Gleixner <tglx@linutronix.de>
-*/
+ */
#include <linux/config.h>
#include <linux/module.h>
static struct map_info h720x_map = {
.name = "H720X",
- .buswidth = 4,
+ .bankwidth = 4,
.size = FLASH_SIZE,
.phys = FLASH_PHYS,
};
simple_map_init(&h720x_map);
- // Probe for flash buswidth 4
+ // 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 buswidth 2
- h720x_map.buswidth = 2;
+ // Probe for bankwidth 2
+ h720x_map.bankwidth = 2;
mymtd = do_map_probe("cfi_probe", &h720x_map);
}
/*
- * $Id: impa7.c,v 1.9 2003/06/23 11:47:43 dwmw2 Exp $
+ * $Id: impa7.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $
*
* Handle mapping of the NOR flash on implementa A7 boards
*
#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 };
+static struct mtd_info *impa7_mtd[NUM_FLASHBANKS];
static struct map_info impa7_map[NUM_FLASHBANKS] = {
{
.name = "impA7 NOR Flash Bank #0",
.size = WINDOW_SIZE0,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
},
{
.name = "impA7 NOR Flash Bank #1",
.size = WINDOW_SIZE1,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
},
};
This is access code for flashes using ARM's flash partitioning
standards.
- $Id: integrator-flash.c,v 1.15 2004/02/27 22:37:39 rmk Exp $
+ $Id: integrator-flash.c,v 1.16 2004/07/12 21:59:44 dwmw2 Exp $
======================================================================*/
* look for CFI based flash parts fitted to this board
*/
info->map.size = size;
- info->map.buswidth = plat->width;
+ info->map.bankwidth = plat->width;
info->map.phys = res->start;
info->map.virt = (unsigned long) base;
info->map.name = dev->dev.bus_id;
/*
- * $Id: iq80310.c,v 1.17 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: iq80310.c,v 1.18 2004/07/12 21:59:44 dwmw2 Exp $
*
* Mapping for the Intel XScale IQ80310 evaluation board
*
static struct map_info iq80310_map = {
.name = "IQ80310 flash",
.size = WINDOW_SIZE,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR
};
/*
- * $Id: ixp4xx.c,v 1.1 2004/05/13 22:21:26 dsaxena Exp $
+ * $Id: ixp4xx.c,v 1.3 2004/07/12 22:38:29 dwmw2 Exp $
*
* drivers/mtd/maps/ixp4xx.c
*
#define BYTE1(h) ((h) & 0xFF)
#endif
-static __u16
-ixp4xx_read16(struct map_info *map, unsigned long ofs)
+static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
{
- return *(__u16 *) (map->map_priv_1 + ofs);
+ map_word val;
+ val.x[0] = *(__u16 *) (map->map_priv_1 + ofs);
+ return val;
}
/*
* 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)
+static void ixp4xx_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
{
int i;
u8 *dest = (u8 *) to;
dest[len - 1] = BYTE0(src[i]);
}
-static void
-ixp4xx_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
{
- *(__u16 *) (map->map_priv_1 + adr) = d;
+ *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
}
struct ixp4xx_flash_info {
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-static int
-ixp4xx_flash_remove(struct device *_dev)
+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;
* any board use 8-bit access, we'll fixup the driver to
* handle that.
*/
- info->map.buswidth = 2;
+ info->map.bankwidth = 2;
info->map.name = dev->dev.bus_id;
- info->map.read16 = ixp4xx_read16,
- info->map.write16 = ixp4xx_write16,
+ info->map.read = ixp4xx_read16,
+ info->map.write = ixp4xx_write16,
info->map.copy_from = ixp4xx_copy_from,
info->res = request_mem_region(dev->resource->start,
/*
- * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: l440gx.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $
*
* BIOS Flash chip on Intel 440GX board.
*
struct map_info l440gx_map = {
.name = "L440GX BIOS",
.size = WINDOW_SIZE,
- .buswidth = BUSWIDTH,
+ .bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR,
#if 0
/* FIXME verify that this is the
/*
- * Flash device on lasat 100 and 200 boards
+ * Flash device on Lasat 100 and 200 boards
*
- * Presumably (C) 2002 Brian Murphy <brian@murphy.dk> or whoever he
- * works for.
+ * (C) 2002 Brian Murphy <brian@murphy.dk>
*
* This program is free software; you can 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.5 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: lasat.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $
*
*/
#include <linux/mtd/partitions.h>
#include <linux/config.h>
#include <asm/lasat/lasat.h>
-#include <asm/lasat/lasat_mtd.h>
-static struct mtd_info *mymtd;
-
-static struct map_info sp_map = {
- .name = "SP flash",
- .buswidth = 4,
-};
+static struct mtd_info *lasat_mtd;
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 void lasat_set_vpp(struct map_info *map, int vpp)
{
- int i;
- /* this does not play well with the old flash code which
- * protects and uprotects the flash when necessary */
- /* FIXME: Implement set_vpp() */
- printk(KERN_NOTICE "Unprotecting flash\n");
- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
+ 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 lasat_map = {
+ .name = "LASAT flash",
+ .bankwidth = 4,
+ .set_vpp = lasat_set_vpp
+};
- sp_map.virt = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
- sp_map.phys = virt_to_phys(sp_map.virt);
- sp_map.size = lasat_board_info.li_flash_size;
+static int __init init_lasat(void)
+{
+ int i;
+ /* 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");
+ ENABLE_VPP((&lasat_map));
- simple_map_init(&sp_map);
+ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
+ lasat_map.virt = (unsigned long)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.phys);
+ 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->owner = THIS_MODULE;
+ lasat_mtd->owner = THIS_MODULE;
for (i=0; i < LASAT_MTD_LAST; i++) {
size = lasat_flash_partition_size(i);
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.virt) {
- sp_map.virt = 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 <brian@murphy.dk>");
/*
- * $Id: lubbock-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: lubbock-flash.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $
*
* Map driver for the Lubbock developer platform.
*
#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[] = {
int flashboot = (LUB_CONF_SWITCHES & 1);
int ret = 0, i;
- lubbock_maps[0].buswidth = lubbock_maps[1].buswidth =
+ lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
(BOOT_DEF & 1) ? 2 : 4;
/* Compensate for the nROMBT switch which swaps the flash banks */
ret = -ENOMEM;
continue;
}
+ lubbock_maps[i].cached = __ioremap(lubbock_maps[i].phys,
+ WINDOW_SIZE,
+ L_PTE_CACHEABLE, 1);
+ 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 buswidth)\n",
+ 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].buswidth * 8);
+ 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;
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]);
/*
- * $Id: map_funcs.c,v 1.2 2003/05/21 15:15:07 dwmw2 Exp $
+ * $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 <linux/kernel.h>
#include <linux/module.h>
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <asm/io.h>
#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-static u8 simple_map_read8(struct map_info *map, unsigned long ofs)
+static map_word simple_map_read(struct map_info *map, unsigned long ofs)
{
- return __raw_readb(map->virt + ofs);
+ return inline_map_read(map, ofs);
}
-static u16 simple_map_read16(struct map_info *map, unsigned long ofs)
+static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
{
- return __raw_readw(map->virt + ofs);
-}
-
-static u32 simple_map_read32(struct map_info *map, unsigned long ofs)
-{
- return __raw_readl(map->virt + ofs);
-}
-
-static u64 simple_map_read64(struct map_info *map, unsigned long ofs)
-{
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
- BUG();
- return 0;
-#else
- return __raw_readll(map->virt + ofs);
-#endif
-}
-
-static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs)
-{
- __raw_writeb(datum, map->virt + ofs);
- mb();
-}
-
-static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs)
-{
- __raw_writew(datum, map->virt + ofs);
- mb();
-}
-
-static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs)
-{
- __raw_writel(datum, map->virt + ofs);
- mb();
-}
-
-static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs)
-{
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
- BUG();
-#else
- __raw_writell(datum, map->virt + ofs);
- mb();
-#endif /* CFI_B8 */
+ inline_map_write(map, datum, ofs);
}
static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- memcpy_fromio(to, map->virt + from, 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)
{
- memcpy_toio(map->virt + to, from, len);
+ inline_map_copy_to(map, to, from, len);
}
void simple_map_init(struct map_info *map)
{
- map->read8 = simple_map_read8;
- map->read16 = simple_map_read16;
- map->read32 = simple_map_read32;
- map->read64 = simple_map_read64;
- map->write8 = simple_map_write8;
- map->write16 = simple_map_write16;
- map->write32 = simple_map_write32;
- map->write64 = simple_map_write64;
+ 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;
}
/*
- * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: mbx860.c,v 1.6 2004/07/12 21:59:44 dwmw2 Exp $
*
* Handle mapping of the flash on MBX860 boards
*
.name = "MBX flash",
.size = WINDOW_SIZE,
.phys = WINDOW_ADDR,
- .buswidth = 4,
+ .bankwidth = 4,
};
int __init init_mbx(void)
* Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
* based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
*
- * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: netsc520.c,v 1.10 2004/07/12 21:59:44 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
static struct map_info netsc520_map = {
.name = "netsc520 Flash Bank",
.size = WINDOW_SIZE,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = WINDOW_ADDR,
};
* (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
* (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
*
- * $Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $
+ * $Id: nettel.c,v 1.5 2004/07/12 21:59:44 dwmw2 Exp $
*/
/****************************************************************************/
static struct map_info nettel_intel_map = {
.name = "SnapGear Intel",
.size = 0,
- .buswidth = INTEL_BUSWIDTH,
+ .bankwidth = INTEL_BUSWIDTH,
};
static struct mtd_partition nettel_intel_partitions[] = {
static struct map_info nettel_amd_map = {
.name = "SnapGear AMD",
.size = AMD_WINDOW_MAXSIZE,
- .buswidth = AMD_BUSWIDTH,
+ .bankwidth = AMD_BUSWIDTH,
};
static struct mtd_partition nettel_amd_partitions[] = {
/*
- * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: ocelot.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $
*
* Flash on Momenco Ocelot
*/
struct map_info ocelot_flash_map = {
.name = "Ocelot boot flash",
.size = FLASH_WINDOW_SIZE,
- .buswidth = FLASH_BUSWIDTH,
+ .bankwidth = FLASH_BUSWIDTH,
.phys = FLASH_WINDOW_ADDR,
};
struct map_info ocelot_nvram_map = {
.name = "Ocelot NVRAM",
.size = NVRAM_WINDOW_SIZE,
- .buswidth = NVRAM_BUSWIDTH,
+ .bankwidth = NVRAM_BUSWIDTH,
.phys = NVRAM_WINDOW_ADDR,
};
-// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $
+// $Id: octagon-5066.c,v 1.26 2004/07/12 22:38:29 dwmw2 Exp $
/* ######################################################################
Octagon 5066 MTD Driver.
}
-static __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
+static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
{
- __u8 ret;
+ map_word ret;
spin_lock(&oct5066_spin);
oct5066_page(map, ofs);
- ret = readb(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
- return ret;
-}
-
-static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&oct5066_spin);
- oct5066_page(map, ofs);
- ret = readw(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&oct5066_spin);
- return ret;
-}
-
-static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&oct5066_spin);
- oct5066_page(map, ofs);
- ret = readl(iomapadr + (ofs & WINDOW_MASK));
+ ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
spin_unlock(&oct5066_spin);
return ret;
}
}
}
-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);
}
.name = "Octagon 5066 Socket",
.phys = NO_XIP,
.size = 512 * 1024,
- .buswidth = 1,
- .read8 = oct5066_read8,
- .read16 = oct5066_read16,
- .read32 = oct5066_read32,
+ .bankwidth = 1,
+ .read = oct5066_read8,
.copy_from = oct5066_copy_from,
- .write8 = oct5066_write8,
- .write16 = oct5066_write16,
- .write32 = oct5066_write32,
+ .write = oct5066_write8,
.copy_to = oct5066_copy_to,
.map_priv_1 = 1<<6
},
.name = "Octagon 5066 Internal Flash",
.phys = NO_XIP,
.size = 2 * 1024 * 1024,
- .buswidth = 1,
- .read8 = oct5066_read8,
- .read16 = oct5066_read16,
- .read32 = oct5066_read32,
+ .bankwidth = 1,
+ .read = oct5066_read8,
.copy_from = oct5066_copy_from,
- .write8 = oct5066_write8,
- .write16 = oct5066_write16,
- .write32 = oct5066_write32,
+ .write = oct5066_write8,
.copy_to = oct5066_copy_to,
.map_priv_1 = 2<<6
}
*
* (C) 2001 Pete Popov <ppopov@mvista.com>
*
- * $Id: pb1xxx-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: pb1xxx-flash.c,v 1.11 2004/07/12 21:59:44 dwmw2 Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#endif
#ifdef CONFIG_MIPS_PB1000
+
#define WINDOW_ADDR 0x1F800000
#define WINDOW_SIZE 0x800000
-#endif
-
-
-static struct map_info pb1xxx_map = {
- .name = "Pb1xxx flash",
-};
-
-
-#ifdef CONFIG_MIPS_PB1000
-static unsigned long flash_size = 0x00800000;
-static unsigned char flash_buswidth = 4;
static struct mtd_partition pb1xxx_partitions[] = {
{
- .name = "yamon env",
- .size = 0x00020000,
- .offset = 0,
- .mask_flags = MTD_WRITEABLE
- },{
- .name = "User FS",
- .size = 0x003e0000,
- .offset = 0x20000,
- },{
- .name = "boot code",
- .size = 0x100000,
- .offset = 0x400000,
- .mask_flags = MTD_WRITEABLE
- },{
- .name = "raw/kernel",
- .size = 0x300000,
- .offset = 0x500000
- }
+ .name = "yamon env",
+ .size = 0x00020000,
+ .offset = 0,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "User FS",
+ .size = 0x003e0000,
+ .offset = 0x20000,},
+ {
+ .name = "boot code",
+ .size = 0x100000,
+ .offset = 0x400000,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "raw/kernel",
+ .size = 0x300000,
+ .offset = 0x500000}
};
#elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
-static unsigned char flash_buswidth = 4;
#if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
-/* both 32MiB banks will be used. Combine the first 32MiB bank and the
- * first 28MiB of the second bank together into a single jffs/jffs2
+/* both 32MB banks will be used. Combine the first 32MB bank and the
+ * first 28MB of the second bank together into a single jffs/jffs2
* partition.
*/
-static unsigned long flash_size = 0x04000000;
#define WINDOW_ADDR 0x1C000000
#define WINDOW_SIZE 0x4000000
static struct mtd_partition pb1xxx_partitions[] = {
{
- .name = "User FS",
- .size = 0x3c00000,
- .offset = 0x0000000
+ .name = "User FS",
+ .size = 0x3c00000,
+ .offset = 0x0000000
},{
- .name = "yamon",
- .size = 0x0100000,
- .offset = 0x3c00000,
- .mask_flags = MTD_WRITEABLE
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = 0x3c00000,
+ .mask_flags = MTD_WRITEABLE
},{
- .name = "raw kernel",
- .size = 0x02c0000,
- .offset = 0x3d00000
+ .name = "raw kernel",
+ .size = 0x02c0000,
+ .offset = 0x3d00000
}
};
#elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER)
-static unsigned long flash_size = 0x02000000;
#define WINDOW_ADDR 0x1E000000
#define WINDOW_SIZE 0x2000000
static struct mtd_partition pb1xxx_partitions[] = {
{
- .name = "User FS",
- .size = 0x1c00000,
- .offset = 0x0000000
+ .name = "User FS",
+ .size = 0x1c00000,
+ .offset = 0x0000000
},{
- .name = "yamon",
- .size = 0x0100000,
- .offset = 0x1c00000,
- .mask_flags = MTD_WRITEABLE
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = 0x1c00000,
+ .mask_flags = MTD_WRITEABLE
},{
- .name = "raw kernel",
- .size = 0x02c0000,
- .offset = 0x1d00000
+ .name = "raw kernel",
+ .size = 0x02c0000,
+ .offset = 0x1d00000
}
};
#elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
-static unsigned long flash_size = 0x02000000;
#define WINDOW_ADDR 0x1C000000
#define WINDOW_SIZE 0x2000000
static struct mtd_partition pb1xxx_partitions[] = {
{
- .name = "User FS",
- .size = 0x1e00000,
- .offset = 0x0000000
+ .name = "User FS",
+ .size = 0x1e00000,
+ .offset = 0x0000000
},{
- .name = "raw kernel",
- .size = 0x0200000,
- .offset = 0x1e00000,
+ .name = "raw kernel",
+ .size = 0x0200000,
+ .offset = 0x1e00000,
}
};
#else
#error Unsupported board
#endif
-static struct mtd_partition *parsed_parts;
-static struct mtd_info *mymtd;
+#define NAME "Pb1x00 Linux Flash"
+#define PADDR WINDOW_ADDR
+#define BUSWIDTH 4
+#define SIZE WINDOW_SIZE
+#define PARTITIONS 4
+
+static struct map_info pb1xxx_mtd_map = {
+ .name = NAME,
+ .size = SIZE,
+ .bankwidth = BUSWIDTH,
+ .phys = PADDR,
+};
+
+static struct mtd_info *pb1xxx_mtd;
int __init pb1xxx_mtd_init(void)
{
int nb_parts = 0;
char *part_type;
- /* Default flash buswidth */
- pb1xxx_map.buswidth = flash_buswidth;
-
/*
* Static partition definition selection
*/
part_type = "static";
parts = pb1xxx_partitions;
nb_parts = ARRAY_SIZE(pb1xxx_partitions);
- pb1xxx_map.size = flash_size;
/*
* Now let's probe for the actual flash. Do it here since
* specific machine settings might have been set above.
*/
printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n",
- pb1xxx_map.buswidth*8);
- pb1xxx_map.phys = WINDOW_ADDR;
- pb1xxx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+ BUSWIDTH*8);
+ pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
- simple_map_init(&pb1xxx_map);
+ simple_map_init(&pb1xxx_mtd_map);
- mymtd = do_map_probe("cfi_probe", &pb1xxx_map);
- if (!mymtd) {
- iounmap(pb1xxx_map.virt);
- return -ENXIO;
- }
- mymtd->owner = THIS_MODULE;
+ pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
+ if (!pb1xxx_mtd) return -ENXIO;
+ pb1xxx_mtd->owner = THIS_MODULE;
- add_mtd_partitions(mymtd, parts, nb_parts);
+ add_mtd_partitions(pb1xxx_mtd, parts, nb_parts);
return 0;
}
static void __exit pb1xxx_mtd_cleanup(void)
{
- if (mymtd) {
- del_mtd_partitions(mymtd);
- map_destroy(mymtd);
- if (parsed_parts)
- kfree(parsed_parts);
+ if (pb1xxx_mtd) {
+ del_mtd_partitions(pb1xxx_mtd);
+ map_destroy(pb1xxx_mtd);
+ iounmap((void *) pb1xxx_mtd_map.virt);
}
- if (pb1xxx_map.virt)
- iounmap(pb1xxx_map.virt);
}
module_init(pb1xxx_mtd_init);
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * $Id: pci.c,v 1.5 2003/05/20 20:59:31 dwmw2 Exp $
+ * $Id: pci.c,v 1.8 2004/07/12 22:38:29 dwmw2 Exp $
*
* Generic PCI memory map driver. We support the following boards:
* - Intel IQ80310 ATU.
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
*/
{
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));
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);
* 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 = {
- .phys = NO_XIP,
- .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)
{
/*
- * $Id: pcmciamtd.c,v 1.48 2003/06/24 07:14: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
*
#define DRIVER_DESC "PCMCIA Flash memory card driver"
-#define DRIVER_VERSION "$Revision: 1.48 $"
+#define DRIVER_VERSION "$Revision: 1.51 $"
/* Size of the PCMCIA address space: 26 bits = 64 MB */
#define MAX_PCMCIA_ADDR 0x4000000
/* 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;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
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");
}
-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;
}
}
-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);
}
#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;
}
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);
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;
}
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);
}
card_settings(dev, link, &new_name);
dev->pcmcia_map.phys = NO_XIP;
- dev->pcmcia_map.read8 = pcmcia_read8_remap;
- dev->pcmcia_map.read16 = pcmcia_read16_remap;
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;
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;
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;
}
{
info(DRIVER_DESC " " DRIVER_VERSION);
- 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);
/*
- * $Id: physmap.c,v 1.29 2003/05/29 09:24:10 dwmw2 Exp $
+ * $Id: physmap.c,v 1.33 2004/07/12 14:37:24 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 <linux/module.h>
#include <linux/config.h>
#include <linux/mtd/partitions.h>
-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
-
static struct mtd_info *mymtd;
-
-struct map_info physmap_map = {
- .name = "Physically mapped flash",
- .size = WINDOW_SIZE,
- .buswidth = BUSWIDTH,
- .phys = WINDOW_ADDR,
-};
+struct map_info physmap_map = {.name = "phys_mapped_flash"};
#ifdef CONFIG_MTD_PARTITIONS
static struct mtd_partition *mtd_parts;
static int mtd_parts_nb;
-static struct mtd_partition physmap_partitions[] = {
-#if 0
-/* Put your own partition definitions here */
- {
- .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 int num_physmap_partitions;
+static struct mtd_partition *physmap_partitions;
-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition))
-const char *part_probes[] = {"cmdlinepart", "RedBoot", NULL};
+static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
+{
+ physmap_partitions=parts;
+ num_physmap_partitions=num_parts;
+}
#endif /* CONFIG_MTD_PARTITIONS */
-int __init init_physmap(void)
+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.virt = (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 = (unsigned long)ioremap(physmap_map.phys, physmap_map.size);
if (!physmap_map.virt) {
printk("Failed to ioremap\n");
return 0;
}
- 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;
}
if (mtd_parts_nb) {
del_mtd_partitions(mymtd);
kfree(mtd_parts);
- } else if (NUM_PARTITIONS) {
+ } else if (num_physmap_partitions) {
del_mtd_partitions(mymtd);
} else {
del_mtd_device(mymtd);
*
* This code is GPL
*
- * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: pnc2000.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $
*/
#include <linux/module.h>
struct map_info pnc_map = {
.name = "PNC-2000",
.size = WINDOW_SIZE,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = 0xFFFFFFFF,
.virt = WINDOW_ADDR,
};
/*
- * $Id: redwood.c,v 1.6 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: redwood.c,v 1.8 2004/07/12 21:59:44 dwmw2 Exp $
*
* drivers/mtd/maps/redwood.c
*
* FLASH map for the IBM Redwood 4/5/6 boards.
*
+ * Author: MontaVista Software, Inc. <source@mvista.com>
*
- * Author: Armin Kuster <akuster@mvista.com>
- *
- * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under
+ * 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.
static struct mtd_partition redwood_flash_partitions[] = {
{
- .name = "Redwood kernel",
+ .name = "Redwood filesystem",
.offset = RW_PART0_OF,
.size = RW_PART0_SZ
},
.mask_flags = MTD_WRITEABLE /* force read-only */
},
{
- .name = "Redwood filesystem",
+ .name = "Redwood kernel",
.offset = RW_PART2_OF,
.size = RW_PART2_SZ
},
struct map_info redwood_flash_map = {
.name = "IBM Redwood",
.size = WINDOW_SIZE,
- .buswidth = 2,
+ .bankwidth = 2,
.phys = WINDOW_ADDR,
};
module_exit(cleanup_redwood_flash);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Armin Kuster <akuster@mvista.com>");
+MODULE_AUTHOR("MontaVista Software <source@mvista.com>");
MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards");
/*
- * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $
+ * $Id: rpxlite.c,v 1.20 2004/07/12 21:59:44 dwmw2 Exp $
*
* Handle mapping of the flash on the RPX Lite and CLLF boards
*/
static struct map_info rpxlite_map = {
.name = "RPX",
.size = WINDOW_SIZE,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = WINDOW_ADDR,
};
*
* (C) 2000 Nicolas Pitre <nico@cam.org>
*
- * $Id: sa1100-flash.c,v 1.36 2003/05/29 08:59:35 dwmw2 Exp $
+ * $Id: sa1100-flash.c,v 1.39 2004/07/12 21:59:44 dwmw2 Exp $
*/
#include <linux/config.h>
sa[i].map->virt = (unsigned long)sa[i].vbase;
sa[i].map->phys = sa[i].base;
sa[i].map->set_vpp = sa[i].set_vpp;
- sa[i].map->buswidth = sa[i].width;
+ sa[i].map->bankwidth = sa[i].width;
sa[i].map->size = sa[i].size;
simple_map_init(sa[i].map);
return;
}
- sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4;
+ sa1100_probe_map.bankwidth = msc & MSC_RBW ? 2 : 4;
sa1100_probe_map.size = SZ_1M;
sa1100_probe_map.phys = phys;
sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M);
return nr;
/*
- * Retrieve the buswidth from the MSC registers.
+ * Retrieve the bankwidth from the MSC registers.
* We currently only implement CS0 and CS1 here.
*/
for (i = 0; i < nr; i++) {
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.26 2003/05/26 08:50:36 dwmw2 Exp $
+ $Id: sbc_gxx.c,v 1.29 2004/07/12 22:38:29 dwmw2 Exp $
The SBC-MediaGX / SBC-GXx has up to 16 MiB of
Intel StrataFlash (28F320/28F640) in x8 mode.
}
-static __u8 sbc_gxx_read8(struct map_info *map, unsigned long ofs)
+static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs)
{
- __u8 ret;
+ map_word ret;
spin_lock(&sbc_gxx_spin);
sbc_gxx_page(map, ofs);
- ret = readb(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&sbc_gxx_spin);
- return ret;
-}
-
-static __u16 sbc_gxx_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&sbc_gxx_spin);
- sbc_gxx_page(map, ofs);
- ret = readw(iomapadr + (ofs & WINDOW_MASK));
- spin_unlock(&sbc_gxx_spin);
- return ret;
-}
-
-static __u32 sbc_gxx_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&sbc_gxx_spin);
- sbc_gxx_page(map, ofs);
- ret = readl(iomapadr + (ofs & WINDOW_MASK));
+ ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
spin_unlock(&sbc_gxx_spin);
return ret;
}
}
}
-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);
}
.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,
+ .bankwidth = 1,
+ .read = sbc_gxx_read8,
.copy_from = sbc_gxx_copy_from,
- .write8 = sbc_gxx_write8,
- .write16 = sbc_gxx_write16,
- .write32 = sbc_gxx_write32,
+ .write = sbc_gxx_write8,
.copy_to = sbc_gxx_copy_to
};
* 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.15 2003/05/21 12:45:20 dwmw2 Exp $
+ * $Id: sc520cdp.c,v 1.16 2004/07/12 21:59:45 dwmw2 Exp $
*
*
* The SC520CDP is an evaluation board for the Elan SC520 processor available
{
.name = "SC520CDP Flash Bank #0",
.size = WINDOW_SIZE_0,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = WINDOW_ADDR_0
},
{
.name = "SC520CDP Flash Bank #1",
.size = WINDOW_SIZE_1,
- .buswidth = 4,
+ .bankwidth = 4,
.phys = WINDOW_ADDR_1
},
{
.name = "SC520CDP DIL Flash",
.size = WINDOW_SIZE_2,
- .buswidth = 1,
+ .bankwidth = 1,
.phys = WINDOW_ADDR_2
},
};
/*
* MTD map driver for BIOS Flash on Intel SCB2 boards
- * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $
+ * $Id: scb2_flash.c,v 1.8 2004/07/12 21:59:45 dwmw2 Exp $
* Copyright (C) 2002 Sun Microsystems, Inc.
* Tim Hockin <thockin@sun.com>
*
struct map_info scb2_map = {
.name = "SCB2 BIOS Flash",
.size = 0,
- .buswidth = 1,
+ .bankwidth = 1,
};
static int region_fail;
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
- $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $
+ $Id: scx200_docflash.c,v 1.6 2004/07/12 21:59:45 dwmw2 Exp $
National Semiconductor SCx200 flash mapped with DOCCS
*/
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;
simple_map_init(&scx200_docflash_map);
/*
- * $Id: solutionengine.c,v 1.10 2003/05/21 12:45:20 dwmw2 Exp $
+ * $Id: solutionengine.c,v 1.13 2004/07/12 21:59:45 dwmw2 Exp $
*
* Flash and EPROM on Hitachi Solution Engine and similar boards.
*
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/config.h>
-
+#include <linux/errno.h>
static struct mtd_info *flash_mtd;
static struct mtd_info *eprom_mtd;
struct map_info soleng_eprom_map = {
.name = "Solution Engine EPROM",
.size = 0x400000,
- .buswidth = 4,
+ .bankwidth = 4,
};
struct map_info soleng_flash_map = {
.name = "Solution Engine FLASH",
.size = 0x400000,
- .buswidth = 4,
+ .bankwidth = 4,
};
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-/* $Id: sun_uflash.c,v 1.7 2003/05/20 20:59:32 dwmw2 Exp $
+/* $Id: sun_uflash.c,v 1.9 2004/07/12 21:59:45 dwmw2 Exp $
*
* sun_uflash - Driver implementation for user-programmable flash
* present on many Sun Microsystems SME boardsets.
struct map_info uflash_map_templ = {
.name = "SUNW,???-????",
.size = UFLASH_WINDOW_SIZE,
- .buswidth = UFLASH_BUSWIDTH,
+ .bankwidth = UFLASH_BUSWIDTH,
};
int uflash_devinit(struct linux_ebus_device* edev)
* Handle mapping of the flash memory access routines
* on TQM8xxL based devices.
*
- * $Id: tqm8xxl.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
+ * $Id: tqm8xxl.c,v 1.11 2004/07/12 21:59:45 dwmw2 Exp $
*
* based on rpxlite.c
*
sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
map_banks[idx]->size = flash_size;
- map_banks[idx]->buswidth = 4;
+ map_banks[idx]->bankwidth = 4;
simple_map_init(map_banks[idx]);
* tsunami_flash.c
*
* flash chip on alpha ds10...
- * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $
+ * $Id: tsunami_flash.c,v 1.9 2004/07/14 09:52:55 dwmw2 Exp $
*/
#include <asm/io.h>
#include <asm/core_tsunami.h>
#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(
.name = "flash chip on the Tsunami TIG bus",
.size = MAX_TIG_FLASH_SIZE,
.phys = NO_XIP;
- .buswidth = 1,
- .read8 = tsunami_flash_read8,
+ .bankwidth = 1,
+ .read = tsunami_flash_read8,
.copy_from = tsunami_flash_copy_from,
- .write8 = tsunami_flash_write8,
+ .write = tsunami_flash_write8,
.copy_to = tsunami_flash_copy_to,
};
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);
*
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
*
- * $Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $
+ * $Id: uclinux.c,v 1.7 2004/07/12 21:59:45 dwmw2 Exp $
*/
/****************************************************************************/
mapp = &uclinux_ram_map;
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);
-// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $
+// $Id: vmax301.c,v 1.30 2004/07/12 22:38:29 dwmw2 Exp $
/* ######################################################################
Tempustech VMAX SBC301 MTD Driver.
__vmax301_page(map, page);
}
-static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
+static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
{
- __u8 ret;
+ map_word ret;
spin_lock(&vmax301_spin);
vmax301_page(map, ofs);
- ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
- return ret;
-}
-
-static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
-{
- __u16 ret;
- spin_lock(&vmax301_spin);
- vmax301_page(map, ofs);
- ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
- spin_unlock(&vmax301_spin);
- return ret;
-}
-
-static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
-{
- __u32 ret;
- spin_lock(&vmax301_spin);
- vmax301_page(map, ofs);
- ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK));
+ ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
spin_unlock(&vmax301_spin);
return ret;
}
}
}
-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);
}
.name = "VMAX301 Internal Flash",
.phys = NO_XIP,
.size = 3*2*1024*1024,
- .buswidth = 1,
- .read8 = vmax301_read8,
- .read16 = vmax301_read16,
- .read32 = vmax301_read32,
+ .bankwidth = 1,
+ .read = vmax301_read8,
.copy_from = vmax301_copy_from,
- .write8 = vmax301_write8,
- .write16 = vmax301_write16,
- .write32 = vmax301_write32,
+ .write = vmax301_write8,
.copy_to = vmax301_copy_to,
.map_priv_1 = WINDOW_START + WINDOW_LENGTH,
.map_priv_2 = 0xFFFFFFFF
.name = "VMAX301 Socket",
.phys = NO_XIP,
.size = 0,
- .buswidth = 1,
- .read8 = vmax301_read8,
- .read16 = vmax301_read16,
- .read32 = vmax301_read32,
+ .bankwidth = 1,
+ .read = vmax301_read8,
.copy_from = vmax301_copy_from,
- .write8 = vmax301_write8,
- .write16 = vmax301_write16,
- .write32 = vmax301_write32,
+ .write = vmax301_write8,
.copy_to = vmax301_copy_to,
.map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
.map_priv_2 = 0xFFFFFFFF
/*
- * $Id: wr_sbc82xx_flash.c,v 1.1 2004/06/07 10:21:32 dwmw2 Exp $
+ * $Id: wr_sbc82xx_flash.c,v 1.5 2004/07/15 14:52:02 dwmw2 Exp $
*
* Map for flash chips on Wind River PowerQUICC II SBC82xx board.
*
static struct mtd_partition bigflash_parts[] = {
{
.name = "bootloader",
- .size = 0x80000,
+ .size = 0x00100000,
.offset = 0,
}, {
.name = "file system",
- .size = MTDPART_SIZ_FULL,
+ .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;
+ volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl;
int bigflash;
int i;
- /* First, register the boot flash, whichever we're booting from */
- if ((mc->memc_br0 & 0x00001800) == 0x00001800) {
- bigflash = 0;
- } else if ((mc->memc_br0 & 0x00001800) == 0x00000800) {
- bigflash = 1;
- } else {
- printk(KERN_WARNING "Bus Controller register BR0 is %08x. Cannot determine flash configuration\n", mc->memc_br0);
- return 1;
- }
+#ifdef CONFIG_SBC8560
+ mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t));
+#else
+ mc = &cpm2_immr->im_memctl;
+#endif
- /* Set parameters for the big flash chip (CS6 or CS0) */
- sbc82xx_flash_map[bigflash].buswidth = 4;
- sbc82xx_flash_map[bigflash].size = 0x4000000;
-
- /* Set parameters for the small flash chip (CS0 or CS6) */
- sbc82xx_flash_map[!bigflash].buswidth = 1;
- sbc82xx_flash_map[!bigflash].size = 0x200000;
+ bigflash = 1;
+ if ((mc->memc_br0 & 0x00001800) == 0x00001800)
+ bigflash = 0;
- /* Set parameters for the user flash chip (CS1) */
- sbc82xx_flash_map[2].buswidth = 4;
- sbc82xx_flash_map[2].size = 0x4000000;
+ 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);
- sbc82xx_flash_map[0].phys = mc->memc_br0 & 0xffff8000;
- sbc82xx_flash_map[1].phys = mc->memc_br6 & 0xffff8000;
- sbc82xx_flash_map[2].phys = mc->memc_br1 & 0xffff8000;
+#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]);
+ 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");
/*
- * $Id: mtd_blkdevs.c,v 1.16 2003/06/23 13:34:43 dwmw2 Exp $
+ * $Id: mtd_blkdevs.c,v 1.22 2004/07/12 12:35:28 dwmw2 Exp $
*
* (C) 2003 David Woodhouse <dwmw2@infradead.org>
*
snprintf(gd->devfs_name, sizeof(gd->devfs_name),
"%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
- set_capacity(gd, new->size);
+ /* 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;
/*
* Direct MTD block device access
*
- * $Id: mtdblock.c,v 1.63 2003/06/23 12:00:08 dwmw2 Exp $
+ * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $
*
* (C) 2000-2003 Nicolas Pitre <nico@cam.org>
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
/*
- * $Id: mtdchar.c,v 1.54 2003/05/21 10:50:43 dwmw2 Exp $
+ * $Id: mtdchar.c,v 1.62 2004/07/14 13:20:42 dwmw2 Exp $
*
* Character-device access to raw MTD devices.
*
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/compatmac.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
-static void mtd_notify_add(struct mtd_info* mtd);
-static void mtd_notify_remove(struct mtd_info* mtd);
+
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+
+ 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);
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+ devfs_remove("mtd/%d", mtd->index);
+ devfs_remove("mtd/%dro", mtd->index);
+}
static struct mtd_notifier notifier = {
.add = mtd_notify_add,
.remove = mtd_notify_remove,
};
+static inline void mtdchar_devfs_init(void)
+{
+ devfs_mk_dir("mtd");
+ register_mtd_user(¬ifier);
+}
+
+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
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
memset (erase,0,sizeof(struct erase_info));
if (copy_from_user(&erase->addr, argp,
- 2 * sizeof(u_long))) {
+ sizeof(struct erase_info_user))) {
kfree(erase);
return -EFAULT;
}
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- if (copy_to_user(argp + 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);
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- if (copy_to_user(argp + 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;
case MEMLOCK:
{
- unsigned long adrs[2];
+ struct erase_info_user info;
- if (copy_from_user(adrs, argp, 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, argp, 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;
}
return -EFAULT;
break;
}
+
+ case MEMGETOOBSEL:
+ {
+ if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
+ return -EFAULT;
+ break;
+ }
+
+ case MEMGETBADBLOCK:
+ {
+ loff_t offs;
+ if (copy_from_user(&offs, argp, sizeof(loff_t)))
+ return -EFAULT;
+ if (!mtd->block_isbad)
+ ret = -EOPNOTSUPP;
+ else
+ return mtd->block_isbad(mtd, offs);
+ break;
+ }
+
+ case MEMSETBADBLOCK:
+ {
+ loff_t offs;
+
+ if (copy_from_user(&offs, argp, sizeof(loff_t)))
+ return -EFAULT;
+ if (!mtd->block_markbad)
+ ret = -EOPNOTSUPP;
+ else
+ return mtd->block_markbad(mtd, offs);
+ break;
+ }
+
default:
DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
ret = -ENOTTY;
.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)
-{
- if (!mtd)
- return;
- 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 | S_IWUGO, "mtd/%dro", mtd->index);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
- if (!mtd)
- return;
- devfs_remove("mtd/%d", mtd->index);
- devfs_remove("mtd/%dro", mtd->index);
-}
-#endif
-
static int __init init_mtdchar(void)
{
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
return -EAGAIN;
}
-#ifdef CONFIG_DEVFS_FS
- devfs_mk_dir("mtd");
-
- register_mtd_user(¬ifier);
-#endif
+ mtdchar_devfs_init();
return 0;
}
static void __exit cleanup_mtdchar(void)
{
-#ifdef CONFIG_DEVFS_FS
- unregister_mtd_user(¬ifier);
- devfs_remove("mtd");
-#endif
+ mtdchar_devfs_exit();
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}
*
* This code is GPL
*
- * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $
+ * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $
*/
#include <linux/module.h>
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 -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);
*/
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;
+ }
}
/* must never happen since size limit has been verified above */
* block alignment has been checked above */
if (err == -EINVAL)
BUG();
+ if (erase->fail_addr != 0xffffffff)
+ instr->fail_addr = erase->fail_addr + offset;
break;
}
/*
* 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;
/*
- * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.42 2004/07/13 10:21:13 dwmw2 Exp $
*
* Core registration and callback routines for MTD
* drivers and users.
/* 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)
{
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;
}
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
*eof = 1;
-#endif
done:
up(&mtd_table_mutex);
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 */
/*====================================================================*/
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, 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();
+ proc_mtd->read_proc = mtd_read_proc;
#endif
#ifdef CONFIG_PM
#endif
#ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if (proc_mtd)
- remove_proc_entry( "mtd", NULL);
-#else
- proc_unregister(&proc_root,mtd_proc_entry.low_ino);
-#endif
+ remove_proc_entry( "mtd", NULL);
#endif
}
*
* This code is GPL
*
- * $Id: mtdpart.c,v 1.41 2003/06/18 14:53:02 dwmw2 Exp $
+ * $Id: mtdpart.c,v 1.46 2004/07/12 13:28:07 dwmw2 Exp $
*
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
* added support for read_oob, write_oob
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);
+ if (instr->fail_addr != 0xffffffff)
+ instr->fail_addr -= part->offset;
+ return ret;
}
static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
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.
*/
int add_mtd_partitions(struct mtd_info *master,
- struct mtd_partition *parts,
+ const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
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;
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;
# drivers/mtd/nand/Kconfig
-# $Id: Kconfig,v 1.4 2003/05/28 10:04:23 dwmw2 Exp $
+# $Id: Kconfig,v 1.14 2004/07/13 00:14:35 dbrown Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
depends on MTD
help
This enables support for accessing all type of NAND flash
- devices with an 8-bit data bus interface. For further information see
+ devices. For further information see
<http://www.linux-mtd.infradead.org/tech/nand.html>.
config MTD_NAND_VERIFY_WRITE
help
If you had to ask, you don't have one. Say 'N'.
+config MTD_NAND_TOTO
+ tristate "NAND Flash device on TOTO board"
+ depends on ARM && ARCH_OMAP && MTD_NAND
+ help
+ Support for NAND flash on Texas Instruments Toto platform.
+
config MTD_NAND_IDS
tristate
default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y
default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m
-
-endmenu
+config MTD_NAND_TX4925NDFMC
+ tristate "SmartMedia Card on Toshiba RBTX4925 reference board"
+ depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND
+ help
+ This enables the driver for the NAND flash device found on the
+ Toshiba RBTX4925 reference board, which is a SmartMediaCard.
+
+config MTD_NAND_TX4938NDFMC
+ tristate "NAND Flash device on Toshiba RBTX4938 reference board"
+ depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND
+ help
+ This enables the driver for the NAND flash device found on the
+ Toshiba RBTX4938 reference board.
+
+config MTD_NAND_AU1550
+ tristate "Au1550 NAND support"
+ depends on SOC_AU1550 && MTD_NAND
+ help
+ This enables the driver for the NAND flash controller on the
+ AMD/Alchemy 1550 SOC.
+
+config MTD_NAND_PPCHAMELEONEVB
+ tristate "NAND Flash device on PPChameleonEVB board"
+ depends on PPCHAMELEONEVB && MTD_NAND
+ help
+ This enables the NAND flash driver on the PPChameleon EVB Board.
+
+config MTD_NAND_DISKONCHIP
+ tristate "DiskOnChip 2000 and Millennium (NAND reimplementation) (EXPERIMENTAL)"
+ depends on MTD_NAND && EXPERIMENTAL
+ help
+ This is a reimplementation of M-Systems DiskOnChip 2000 and
+ Millennium as a standard NAND device driver, as opposed to the
+ earlier self-contained MTD device drivers.
+ This should enable, among other things, proper JFFS2 operation on
+ these devices.
+
+config MTD_NAND_DISKONCHIP_BBTWRITE
+ bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
+ depends on MTD_NAND_DISKONCHIP
+ help
+ On DiskOnChip devices shipped with the INFTL filesystem (Millennium
+ and 2000 TSOP/Alon), Linux reserves some space at the end of the
+ device for the Bad Block Table (BBT). If you have existing INFTL
+ data on your device (created by non-Linux tools such as M-Systems'
+ DOS drivers), your data might overlap the area Linux wants to use for
+ the BBT. If this is a concern for you, leave this option disabled and
+ Linux will not write BBT data into this area.
+ The downside of leaving this option disabled is that if bad blocks
+ are detected by Linux, they will not be recorded in the BBT, which
+ could cause future problems.
+ Once you enable this option, new filesystems (INFTL or others, created
+ in Linux or other operating systems) will not use the reserved area.
+ The only reason not to enable this option is to prevent damage to
+ preexisting filesystems.
+ Even if you leave this disabled, you can enable BBT writes at module
+ load time (assuming you build diskonchip as a module) with the module
+ parameter "inftl_bbt_write=1".
+endmenu
#
# linux/drivers/nand/Makefile
#
-# $Id: Makefile.common,v 1.2 2003/05/28 11:38:54 dwmw2 Exp $
+# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $
-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
+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_DISKONCHIP) += diskonchip.o
+
+nand-objs = nand_base.o nand_bbt.o
* Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
- * $Id: autcpu12.c,v 1.11 2003/06/04 17:04:09 gleixner Exp $
+ * $Id: autcpu12.c,v 1.19 2004/07/12 15:02:15 dwmw2 Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* 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
/*
* Define partitions for flash devices
*/
-extern struct nand_oobinfo jffs2_oobinfo;
-
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
/*
* hardware specific access to control-lines
*/
-void autcpu12_hwcontrol(int cmd)
+static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd){
/*
* 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
*/
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;
- }
-
+
/* Register the partitions */
switch(autcpu12_mtd->size){
case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
- goto out_buf;
+ goto out_ior;
}
}
goto out;
-out_buf:
- kfree (this->data_buf);
out_ior:
iounmap((void *)autcpu12_fio_base);
out_mtd:
#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);
+ /* Release resources, unregister device */
+ nand_release (autcpu12_mtd);
/* unmap physical adress */
iounmap((void *)autcpu12_fio_base);
-
+
/* Free the MTD device structure */
kfree (autcpu12_mtd);
}
* Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
- * $Id: edb7312.c,v 1.5 2003/04/20 07:24:40 gleixner Exp $
+ * $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
/*
* hardware specific access to control-lines
*/
-static void ep7312_hwcontrol(int cmd)
+static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd) {
/*
* 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
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;
return -ENOMEM;
}
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts,
- "edb7312-nand");
+#ifdef CONFIG_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";
+ part_type = "command line";
else
- mtd_parts_nb = 0;
+ 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";
/*
- * 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@realitydiluted.com)
- * Toshiba America Electronics Components, Inc.
+ * drivers/mtd/nand/nand_ecc.c
*
- * $Id: nand_ecc.c,v 1.9 2003/02/20 13:34:19 sjhill Exp $
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
*
- * 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.
+ * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
*
- * 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 free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mtd/nand_ecc.h>
/*
* Pre-calculated 256-way 1 byte column parity
};
-/*
+/**
+ * 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,
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;
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;
* drivers/mtd/nandids.c
*
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
- *
- *
- * $Id: nand_ids.c,v 1.4 2003/05/21 15:15:08 dwmw2 Exp $
+ *
+ * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 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
*/
#include <linux/module.h>
#include <linux/mtd/nand.h>
-
/*
* 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 1MiB 5V", 0x6e, 20, 0x1000, 1},
- {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1},
- {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0},
- {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1},
- {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1},
- {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1},
- {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0},
- {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0},
- {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0},
- {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0},
- {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0},
- {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0},
- {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0},
- {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0},
- {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0},
+ {"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},
+
+ {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 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},
+
{NULL,}
};
{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);
* to controllines (due to change in nand.c)
* page_cache added
*
- * $Id: spia.c,v 1.19 2003/04/20 07:24:40 gleixner Exp $
+ * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* a 64Mibit (8MiB x 8 bits) NAND flash device.
*/
+#include <linux/kernel.h>
+#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
/*
* 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.
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);
-
/*
* Define partitions for flash device
*/
/*
* hardware specific access to control-lines
*/
-void spia_hwcontrol(int cmd){
+static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
switch(cmd){
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;
}
/* Linux driver for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@infradead.org> */
-/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */
+/* $Id: nftlcore.c,v 1.96 2004/06/28 13:52:55 dbrown Exp $ */
/*
The contents of this file are distributed under the GNU General
struct NFTLrecord *nftl;
unsigned long temp;
- if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
+ 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
+"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);
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 "NFTL: could not mount device\n");
if (BlockMap[block] == BLOCK_NIL)
continue;
- ret = MTD_READECC(nftl->mbd.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->mbd.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");
}
+ 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, NAND_ECC_DISKONCHIP);
+ 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
}
/* add the header so that it is now a valid chain */
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 {
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);
return 1;
}
+ 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 *)eccbuf, NAND_ECC_DISKONCHIP);
- /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
+ 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
+ /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
return 0;
}
} else {
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
size_t retlen;
- u_char eccbuf[6];
- if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
+ if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
return -EIO;
}
return 0;
int __init init_nftl(void)
{
- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev);
+ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.96 $, nftlmount.c %s\n", nftlmountrev);
return register_mtd_blktrans(&nftl_tr);
}
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
*
- * $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $
+ * $Id: nftlmount.c,v 1.36 2004/06/28 13:52:55 dbrown 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
#define SECTORSIZE 512
-char nftlmountrev[]="$Revision: 1.34 $";
+char nftlmountrev[]="$Revision: 1.36 $";
/* 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
/* Assume logical EraseSize == physical erasesize for starting the scan.
We'll sort it out later if we find a MediaHeader which says otherwise */
+ /* 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;
/* Check for ANAND header first. Then can whinge if it's found but later
checks fail */
- if ((ret = MTD_READ(nftl->mbd.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) {
/* Finally reread to check ECC */
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) {
+ &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->mbd.mtd->index, ret);
continue;
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) {
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");
/* 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->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);
/* 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;
int check_oob)
{
int i, retlen;
- u8 buf[SECTORSIZE];
+ 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->mbd.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->mbd.mtd, address, nftl->mbd.mtd->oobsize,
- &retlen, buf) < 0)
- return -1;
- if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
+ if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
return -1;
}
address += SECTORSIZE;
* 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)
{
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++;
* 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->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.
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;
/*
- * $Id: redboot.c,v 1.11 2003/05/21 10:39:26 dwmw2 Exp $
+ * $Id: redboot.c,v 1.13 2004/04/01 10:17:40 gthomas Exp $
*
* Parse RedBoot-style Flash Image System (FIS) tables and
* produce a Linux partition array to match.
char *names;
char *nullname;
int namelen = 0;
+ int nulllen = 0;
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
static char nullstring[] = "unallocated";
+#endif
- buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ buf = kmalloc(master->erasesize, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Read the start of the last erase block */
ret = master->read(master, master->size - master->erasesize,
- PAGE_SIZE, &retlen, (void *)buf);
+ master->erasesize, &retlen, (void *)buf);
if (ret)
goto out;
- if (retlen != PAGE_SIZE) {
+ if (retlen != master->erasesize) {
ret = -EIO;
goto out;
}
goto out;
}
- for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) {
+ for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) {
struct fis_list *new_fl, **prev;
if (buf[i].name[0] == 0xff)
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 + sizeof(nullstring) + namelen, GFP_KERNEL);
+#endif
+ parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
if (!parts) {
ret = -ENOMEM;
goto out;
}
- memset(parts, 0, sizeof(*parts)*nrparts + namelen);
+ memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen);
- /* FIXME: Include nullname only if it's used */
nullname = (char *)&parts[nrparts];
- sprintf(nullname, nullstring);
- names = nullname + sizeof(nullstring);
+#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 = nullname;
parts[0].size = fl->img->flash_base;
parts[0].offset = 0;
+ i++;
}
+#endif
for ( ; i<nrparts; i++) {
parts[i].size = fl->img->size;
parts[i].offset = fl->img->flash_base;
parts[i].name = names;
strcpy(names, fl->img->name);
+#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 = nullname;
}
+#endif
tmp_fl = fl;
fl = fl->next;
kfree(tmp_fl);
To compile this driver as a module, choose M here: the module
will be called tg3. This is recommended.
+config GIANFAR
+ tristate "Gianfar Ethernet"
+ depends on 85xx
+ help
+ This driver supports the Gigabit TSEC on the MPC85xx
+ family of chips, and the FEC on the 8540
+
+config GFAR_NAPI
+ bool "NAPI Support"
+ depends on GIANFAR
+
endmenu
#
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/
+obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o
#
# link order important here
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_MPSC
+ bool "Marvell MPSC serial port support"
+ depends on PPC32 && MV64X60
+ select SERIAL_CORE
+ help
+ Say Y here if you want to use the Marvell MPSC serial controller.
+
+config SERIAL_MPSC_CONSOLE
+ bool "Support for console on Marvell MPSC serial port"
+ depends on SERIAL_MPSC
+ select SERIAL_CORE_CONSOLE
+ help
+ Say Y here if you want to support a serial console on a Marvell MPSC.
+
config SERIAL_PXA
bool "PXA serial port support"
depends on ARM && ARCH_PXA
# $Id: Makefile,v 1.8 2002/07/21 21:32:30 rmk Exp $
#
+obj-$(CONFIG_SERIAL_MPSC) += mpsc/
+
serial-8250-y :=
serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o
serial-8250-$(CONFIG_GSC) += 8250_gsc.o
config JFFS_PROC_FS
bool "JFFS stats available in /proc filesystem"
- depends on JFFS_FS && PROC
+ depends on JFFS_FS && PROC_FS
help
Enabling this option will cause statistics from mounted JFFS file systems
to be made available to the user in the /proc/fs/jffs/ directory.
config JFFS2_FS
tristate "Journalling Flash File System v2 (JFFS2) support"
- depends on MTD
select CRC32
- select ZLIB_INFLATE
- select ZLIB_DEFLATE
+ depends on MTD
help
JFFS2 is the second generation of the Journalling Flash File System
for use on diskless embedded devices. It provides improved wear
Say 'N' unless you have NAND flash and you are willing to test and
develop JFFS2 support for it.
+config JFFS2_COMPRESSION_OPTIONS
+ bool "Advanced compression options for JFFS2"
+ default n
+ help
+ Enabling this option allows you to explicitly choose which
+ compression modules, if any, are enabled in JFFS2. Removing
+ compressors and mean you cannot read existing file systems,
+ and enabling experimental compressors can mean that you
+ write a file system which cannot be read by a standard kernel.
+
+ If unsure, you should _definitely_ say 'N'.
+
+config JFFS2_ZLIB
+ bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS
+ select ZLIB_INFLATE
+ select ZLIB_DEFLATE
+ depends on JFFS2_FS
+ default y
+ help
+ Zlib is designed to be a free, general-purpose, legally unencumbered,
+ lossless data-compression library for use on virtually any computer
+ hardware and operating system. See http://www.gzip.org/zlib/ for
+ further information.
+
+ Say 'Y' if unsure.
+
+config JFFS2_RTIME
+ bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
+ depends on JFFS2_FS
+ default y
+ help
+ Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+
+config JFFS2_RUBIN
+ bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
+ depends on JFFS2_FS
+ default n
+ help
+ RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+
+choice
+ prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
+ default JFFS2_CMODE_PRIORITY
+ depends on JFFS2_FS
+ help
+ You can set here the default compression mode of JFFS2 from
+ the avaiable compression modes. Don't touch if unsure.
+
+config JFFS2_CMODE_NONE
+ bool "no compression"
+ help
+ Uses no compression.
+
+config JFFS2_CMODE_PRIORITY
+ bool "priority"
+ help
+ Tries the compressors in a predefinied order and chooses the first
+ successful one.
+
+config JFFS2_CMODE_SIZE
+ bool "size (EXPERIMENTAL)"
+ help
+ Tries all compressors and chooses the one which has the smallest
+ result.
+
+endchoice
+
+config JFFS2_PROC
+ bool "JFFS2 proc interface support" if JFFS2_COMPRESSION_OPTIONS
+ depends on JFFS2_FS && PROC_FS
+ default n
+ help
+ You can read some statistics and set the compression mode and
+ compressor priorities with this interface.
+
+
config CRAMFS
tristate "Compressed ROM file system support"
select ZLIB_INFLATE
#
-# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
-# $Id: Makefile,v 1.34 2002/03/08 11:27:59 dwmw2 Exp $
+# $Id: Makefile.common,v 1.5 2004/07/15 16:06:41 dwmw2 Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
-jffs2-y := compr.o compr_rubin.o compr_rtime.o compr_zlib.o
-jffs2-y += dir.o file.o ioctl.o nodelist.o malloc.o
+jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o
jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
+jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
+jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
+jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
+jffs2-$(CONFIG_JFFS2_PROC) += proc.o
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: background.c,v 1.44 2003/10/08 13:29:55 dwmw2 Exp $
+ * $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $
*
*/
static int jffs2_garbage_collect_thread(void *);
-static int thread_should_wake(struct jffs2_sb_info *c);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
spin_lock(&c->erase_completion_lock);
- if (c->gc_task && thread_should_wake(c))
+ if (c->gc_task && jffs2_thread_should_wake(c))
send_sig(SIGHUP, c->gc_task, 1);
spin_unlock(&c->erase_completion_lock);
}
for (;;) {
allow_signal(SIGHUP);
- if (!thread_should_wake(c)) {
+ if (!jffs2_thread_should_wake(c)) {
set_current_state (TASK_INTERRUPTIBLE);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
- /* Yes, there's a race here; we checked thread_should_wake() before
- setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+ /* Yes, there's a race here; we checked jffs2_thread_should_wake()
+ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
matter - We don't care if we miss a wakeup, because the GC thread
is only an optimisation anyway. */
schedule();
spin_unlock(&c->erase_completion_lock);
complete_and_exit(&c->gc_thread_exit, 0);
}
-
-static int thread_should_wake(struct jffs2_sb_info *c)
-{
- int ret = 0;
- uint32_t dirty;
-
- if (c->unchecked_size) {
- D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
- c->unchecked_size, c->checked_ino));
- return 1;
- }
-
- /* dirty_size contains blocks on erase_pending_list
- * those blocks are counted in c->nr_erasing_blocks.
- * If one block is actually erased, it is not longer counted as dirty_space
- * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
- * with c->nr_erasing_blocks * c->sector_size again.
- * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
- * This helps us to force gc and pick eventually a clean block to spread the load.
- */
- dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
-
- if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
- (dirty > c->nospc_dirty_size))
- ret = 1;
-
- D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
-
- return ret;
-}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: build.c,v 1.52 2003/10/09 00:38:38 dwmw2 Exp $
+ * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
*
*/
#include <linux/slab.h>
#include "nodelist.h"
-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
static inline struct jffs2_inode_cache *
first_inode_chain(int *i, struct jffs2_sb_info *c)
ic; \
ic = next_inode(&i, ic, (c)))
+
+static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_full_dirent *fd;
+
+ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
+
+ /* For each child, increase nlink */
+ for(fd = ic->scan_dents; fd; fd = fd->next) {
+ struct jffs2_inode_cache *child_ic;
+ if (!fd->ino)
+ continue;
+
+ /* XXX: Can get high latency here with huge directories */
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+ fd->name, fd->ino, ic->ino);
+ continue;
+ }
+
+ if (child_ic->nlink++ && fd->type == DT_DIR) {
+ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+ if (fd->ino == 1 && ic->ino == 1) {
+ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+ }
+ /* What do we do about it? */
+ }
+ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+ /* Can't free them. We might need them in pass 2 */
+ }
+}
+
/* Scan plan:
- Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
- Scan directory tree from top down, setting nlink in inocaches
int ret;
int i;
struct jffs2_inode_cache *ic;
+ struct jffs2_full_dirent *dead_fds = NULL;
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
- ret = jffs2_build_inode_pass1(c, ic);
- if (ret) {
- D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
- return ret;
+
+ D1(BUG_ON(ic->ino > c->highest_ino));
+
+ if (ic->scan_dents) {
+ jffs2_build_inode_pass1(c, ic);
+ cond_resched();
}
- cond_resched();
}
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
- D1(jffs2_dump_block_lists(c));
/* Next, scan for inodes with nlink == 0 and remove them. If
they were directories, then decrement the nlink of their
children too, and repeat the scan. As that's going to be
a fairly uncommon occurrence, it's not so evil to do it this
way. Recursion bad. */
- do {
- D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
- ret = 0;
- for_each_inode(i, c, ic) {
- D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
- if (ic->nlink)
- continue;
+ D1(printk(KERN_DEBUG "Pass 2 starting\n"));
+
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+ if (ic->nlink)
+ continue;
- /* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */
+ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+ cond_resched();
+ }
- ret = jffs2_build_remove_unlinked_inode(c, ic);
- if (ret)
- break;
- /* -EAGAIN means the inode's nlink was zero, so we deleted it,
- and furthermore that it had children and their nlink has now
- gone to zero too. So we have to restart the scan. */
- }
- D1(jffs2_dump_block_lists(c));
+ D1(printk(KERN_DEBUG "Pass 2a starting\n"));
- cond_resched();
-
- } while(ret == -EAGAIN);
+ while (dead_fds) {
+ struct jffs2_inode_cache *ic;
+ struct jffs2_full_dirent *fd = dead_fds;
+
+ dead_fds = fd->next;
+
+ ic = jffs2_get_ino_cache(c, fd->ino);
+ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
+
+ if (ic)
+ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+ jffs2_free_full_dirent(fd);
+ }
D1(printk(KERN_DEBUG "Pass 2 complete\n"));
- /* Finally, we can scan again and free the dirent nodes and scan_info structs */
+ /* Finally, we can scan again and free the dirent structs */
for_each_inode(i, c, ic) {
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
return ret;
}
-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
-{
- struct jffs2_full_dirent *fd;
-
- D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
-
- if (ic->ino > c->highest_ino)
- c->highest_ino = ic->ino;
-
- /* For each child, increase nlink */
- for(fd=ic->scan_dents; fd; fd = fd->next) {
- struct jffs2_inode_cache *child_ic;
- if (!fd->ino)
- continue;
-
- /* XXX: Can get high latency here with huge directories */
-
- child_ic = jffs2_get_ino_cache(c, fd->ino);
- if (!child_ic) {
- printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
- fd->name, fd->ino, ic->ino);
- continue;
- }
-
- if (child_ic->nlink++ && fd->type == DT_DIR) {
- printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
- if (fd->ino == 1 && ic->ino == 1) {
- printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
- printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
- }
- /* What do we do about it? */
- }
- D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
- /* Can't free them. We might need them in pass 2 */
- }
- return 0;
-}
-
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
{
struct jffs2_raw_node_ref *raw;
struct jffs2_full_dirent *fd;
- int ret = 0;
D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
jffs2_free_full_dirent(fd);
continue;
}
- jffs2_free_full_dirent(fd);
+
+ /* Reduce nlink of the child. If it's now zero, stick it on the
+ dead_fds list to be cleaned up later. Else just free the fd */
+
child_ic->nlink--;
+
+ if (!child_ic->nlink) {
+ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
+ fd->ino, fd->name));
+ fd->next = *dead_fds;
+ *dead_fds = fd;
+ } else {
+ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
+ fd->ino, fd->name, child_ic->nlink));
+ jffs2_free_full_dirent(fd);
+ }
}
- ret = -EAGAIN;
}
/*
We don't delete the inocache from the hash list and free it yet.
The erase code will do that, when all the nodes are completely gone.
*/
-
- return ret;
}
static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
- *
* Created by Arjan van de Ven <arjanv@redhat.com>
*
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $
*
*/
-#if defined(__KERNEL__) || defined (__ECOS)
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#else
-#define KERN_DEBUG
-#define KERN_NOTICE
-#define KERN_WARNING
-#define printk printf
-#include <stdio.h>
-#include <stdint.h>
-#include <errno.h>
-#endif
+#include "compr.h"
+
+static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
-#include <linux/jffs2.h>
+void jffs2_set_compression_mode(int mode)
+{
+ jffs2_compression_mode = mode;
+}
-int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+int jffs2_get_compression_mode(void)
+{
+ return jffs2_compression_mode;
+}
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
/* jffs2_compress:
* @data: Pointer to uncompressed data
- * @cdata: Pointer to buffer for compressed data
+ * @cdata: Pointer to returned pointer to buffer for compressed data
* @datalen: On entry, holds the amount of data available for compression.
* On exit, expected to hold the amount of data actually compressed.
* @cdatalen: On entry, holds the amount of space available for compressed
* data. On exit, expected to hold the actual size of the compressed
* data.
*
- * Returns: Byte to be stored with data indicating compression type used.
+ * Returns: Lower byte to be stored with data indicating compression type used.
* Zero is used to show that the data could not be compressed - the
* compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
*
* If the cdata buffer isn't large enough to hold all the uncompressed data,
* jffs2_compress should compress as much as will fit, and should set
* *datalen accordingly to show the amount of data which were compressed.
*/
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *datalen, uint32_t *cdatalen)
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+ unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen)
{
- int ret;
+ int ret = JFFS2_COMPR_NONE;
+ int compr_ret;
+ struct jffs2_compressor *this, *best=NULL;
+ unsigned char *output_buf = NULL, *tmp_buf;
+ uint32_t orig_slen, orig_dlen;
+ uint32_t best_slen=0, best_dlen=0;
- ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
- if (!ret) {
- return JFFS2_COMPR_ZLIB;
- }
-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
- ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
- if (!ret) {
- return JFFS2_COMPR_DYNRUBIN;
- }
-#endif
-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
- ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
- if (!ret) {
- return JFFS2_COMPR_RUBINMIPS;
- }
-#endif
- /* rtime does manage to recompress already-compressed data */
- ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
- if (!ret) {
- return JFFS2_COMPR_RTIME;
- }
-#if 0
- /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
- /* If we get here, no compression is going to work */
- /* But we might want to use the fragmentation part -- Arjan */
- memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
- if (*datalen > *cdatalen)
- *datalen = *cdatalen;
-#endif
- return JFFS2_COMPR_NONE; /* We failed to compress */
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ break;
+ case JFFS2_COMPR_MODE_PRIORITY:
+ output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+ if (!output_buf) {
+ printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+ goto out;
+ }
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ if ((!this->compress)||(this->disabled))
+ continue;
+ this->usecount++;
+ spin_unlock(&jffs2_compressor_list_lock);
+ *datalen = orig_slen;
+ *cdatalen = orig_dlen;
+ compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+ spin_lock(&jffs2_compressor_list_lock);
+ this->usecount--;
+ if (!compr_ret) {
+ ret = this->compr;
+ this->stat_compr_blocks++;
+ this->stat_compr_orig_size += *datalen;
+ this->stat_compr_new_size += *cdatalen;
+ break;
+ }
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
+ break;
+ case JFFS2_COMPR_MODE_SIZE:
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ if ((!this->compress)||(this->disabled))
+ continue;
+ /* Allocating memory for output buffer if necessary */
+ if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ kfree(this->compr_buf);
+ spin_lock(&jffs2_compressor_list_lock);
+ this->compr_buf_size=0;
+ this->compr_buf=NULL;
+ }
+ if (!this->compr_buf) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
+ spin_lock(&jffs2_compressor_list_lock);
+ if (!tmp_buf) {
+ printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
+ continue;
+ }
+ else {
+ this->compr_buf = tmp_buf;
+ this->compr_buf_size = orig_dlen;
+ }
+ }
+ this->usecount++;
+ spin_unlock(&jffs2_compressor_list_lock);
+ *datalen = orig_slen;
+ *cdatalen = orig_dlen;
+ compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+ spin_lock(&jffs2_compressor_list_lock);
+ this->usecount--;
+ if (!compr_ret) {
+ if ((!best_dlen)||(best_dlen>*cdatalen)) {
+ best_dlen = *cdatalen;
+ best_slen = *datalen;
+ best = this;
+ }
+ }
+ }
+ if (best_dlen) {
+ *cdatalen = best_dlen;
+ *datalen = best_slen;
+ output_buf = best->compr_buf;
+ best->compr_buf = NULL;
+ best->compr_buf_size = 0;
+ best->stat_compr_blocks++;
+ best->stat_compr_orig_size += best_slen;
+ best->stat_compr_new_size += best_dlen;
+ ret = best->compr;
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ break;
+ default:
+ printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+ }
+ out:
+ if (ret == JFFS2_COMPR_NONE) {
+ *cpage_out = data_in;
+ *datalen = *cdatalen;
+ none_stat_compr_blocks++;
+ none_stat_compr_size += *datalen;
+ }
+ else {
+ *cpage_out = output_buf;
+ }
+ return ret;
}
-
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+ uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
{
- switch (comprtype) {
+ struct jffs2_compressor *this;
+ int ret;
+
+ switch (comprtype & 0xff) {
case JFFS2_COMPR_NONE:
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy(data_out, cdata_in, datalen);
+ none_stat_decompr_blocks++;
break;
-
case JFFS2_COMPR_ZERO:
memset(data_out, 0, datalen);
break;
+ default:
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (comprtype == this->compr) {
+ this->usecount++;
+ spin_unlock(&jffs2_compressor_list_lock);
+ ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+ spin_lock(&jffs2_compressor_list_lock);
+ if (ret) {
+ printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+ }
+ else {
+ this->stat_decompr_blocks++;
+ }
+ this->usecount--;
+ spin_unlock(&jffs2_compressor_list_lock);
+ return ret;
+ }
+ }
+ printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype);
+ spin_unlock(&jffs2_compressor_list_lock);
+ return -EIO;
+ }
+ return 0;
+}
- case JFFS2_COMPR_ZLIB:
- jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
- break;
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+ struct jffs2_compressor *this;
- case JFFS2_COMPR_RTIME:
- jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
- break;
+ if (!comp->name) {
+ printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+ return -1;
+ }
+ comp->compr_buf_size=0;
+ comp->compr_buf=NULL;
+ comp->usecount=0;
+ comp->stat_compr_orig_size=0;
+ comp->stat_compr_new_size=0;
+ comp->stat_compr_blocks=0;
+ comp->stat_decompr_blocks=0;
+ D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+
+ spin_lock(&jffs2_compressor_list_lock);
+
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ goto out;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+ D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+ printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+ })
+
+ spin_unlock(&jffs2_compressor_list_lock);
+
+ return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+ D2(struct jffs2_compressor *this;)
+
+ D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+
+ spin_lock(&jffs2_compressor_list_lock);
+
+ if (comp->usecount) {
+ spin_unlock(&jffs2_compressor_list_lock);
+ printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+ return -1;
+ }
+ list_del(&comp->list);
+
+ D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+ printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+ })
+ spin_unlock(&jffs2_compressor_list_lock);
+ return 0;
+}
+
+#ifdef CONFIG_JFFS2_PROC
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"disabled");
+ else
+ act_buf += sprintf(act_buf,"enabled");
+ act_buf += sprintf(act_buf,"\n");
+ }
+ return buf;
+}
+
+char *jffs2_stats(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+
+ act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
+ act_buf += sprintf(act_buf,"%10s ","none");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks,
+ none_stat_compr_size, none_stat_decompr_blocks);
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf,"%10s ",this->name);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"- ");
+ else
+ act_buf += sprintf(act_buf,"+ ");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks,
+ this->stat_compr_new_size, this->stat_compr_orig_size,
+ this->stat_decompr_blocks);
+ act_buf += sprintf(act_buf,"\n");
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+
+ return buf;
+}
+
+char *jffs2_get_compression_mode_name(void)
+{
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ return "none";
+ case JFFS2_COMPR_MODE_PRIORITY:
+ return "priority";
+ case JFFS2_COMPR_MODE_SIZE:
+ return "size";
+ }
+ return "unkown";
+}
+
+int jffs2_set_compression_mode_name(const char *name)
+{
+ if (!strcmp("none",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+ return 0;
+ }
+ if (!strcmp("priority",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+ return 0;
+ }
+ if (!strcmp("size",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+ return 0;
+ }
+ return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+ struct jffs2_compressor *this;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (!strcmp(this->name, name)) {
+ this->disabled = disabled;
+ spin_unlock(&jffs2_compressor_list_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
+ return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+ return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+ return jffs2_compressor_Xable(name, 1);
+}
+
+int jffs2_set_compressor_priority(const char *name, int priority)
+{
+ struct jffs2_compressor *this,*comp;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (!strcmp(this->name, name)) {
+ this->priority = priority;
+ comp = this;
+ goto reinsert;
+ }
+ }
+ spin_unlock(&jffs2_compressor_list_lock);
+ printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
+ return 1;
+reinsert:
+ /* list is sorted in the order of priority, so if
+ we change it we have to reinsert it into the
+ good place */
+ list_del(&comp->list);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ spin_unlock(&jffs2_compressor_list_lock);
+ return 0;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
+ spin_unlock(&jffs2_compressor_list_lock);
+ return 0;
+}
- case JFFS2_COMPR_RUBINMIPS:
-#if 0 /* Disabled 23/9/1 */
- jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
-#else
- printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
#endif
- break;
- case JFFS2_COMPR_DYNRUBIN:
-#if 1 /* Phase this one out */
- jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+ if (orig != comprbuf)
+ kfree(comprbuf);
+}
+
+int jffs2_compressors_init(void)
+{
+/* Registering compressors */
+#ifdef CONFIG_JFFS2_ZLIB
+ jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+ jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+ jffs2_rubinmips_init();
+ jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+ jffs2_lzari_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+ D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
#else
- printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+ D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+ D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
#endif
- break;
+#endif
+ return 0;
+}
- default:
- printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
- return -EIO;
- }
- return 0;
+int jffs2_compressors_exit(void)
+{
+/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+ jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+ jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+ jffs2_dynrubin_exit();
+ jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+ jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+ jffs2_zlib_exit();
+#endif
+ return 0;
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $
*
*
* Very simple lz77-ish encoder.
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/jffs2.h>
+#include "compr.h"
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *sourcelen, uint32_t *dstlen)
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
short positions[256];
int outpos = 0;
}
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t srclen, uint32_t destlen)
+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
{
short positions[256];
int outpos = 0;
outpos+=repeat;
}
}
- }
+ }
+ return 0;
}
+static struct jffs2_compressor jffs2_rtime_comp = {
+ .priority = JFFS2_RTIME_PRIORITY,
+ .name = "rtime",
+ .compr = JFFS2_COMPR_RTIME,
+ .compress = &jffs2_rtime_compress,
+ .decompress = &jffs2_rtime_decompress,
+#ifdef JFFS2_RTIME_DISABLED
+ .disabled = 1,
+#else
+ .disabled = 0,
+#endif
+};
+
+int jffs2_rtime_init(void)
+{
+ return jffs2_register_compressor(&jffs2_rtime_comp);
+}
+void jffs2_rtime_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $
+ * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $
*
*/
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/jffs2.h>
#include "compr_rubin.h"
#include "histo_mips.h"
-
-
+#include "compr.h"
static void init_rubin(struct rubin_state *rs, int div, int *bits)
{
#if 0
/* _compress returns the compressed size, -1 if bigger */
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *sourcelen, uint32_t *dstlen)
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
}
#endif
int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *sourcelen, uint32_t *dstlen)
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
int bits[8];
unsigned char histo[256];
}
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t sourcelen, uint32_t dstlen)
+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t sourcelen, uint32_t dstlen, void *model)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ return 0;
}
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t sourcelen, uint32_t dstlen)
+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t sourcelen, uint32_t dstlen, void *model)
{
int bits[8];
int c;
bits[c] = data_in[c];
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_rubinmips_comp = {
+ .priority = JFFS2_RUBINMIPS_PRIORITY,
+ .name = "rubinmips",
+ .compr = JFFS2_COMPR_DYNRUBIN,
+ .compress = NULL, /*&jffs2_rubinmips_compress,*/
+ .decompress = &jffs2_rubinmips_decompress,
+#ifdef JFFS2_RUBINMIPS_DISABLED
+ .disabled = 1,
+#else
+ .disabled = 0,
+#endif
+};
+
+int jffs2_rubinmips_init(void)
+{
+ return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
+void jffs2_rubinmips_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
+static struct jffs2_compressor jffs2_dynrubin_comp = {
+ .priority = JFFS2_DYNRUBIN_PRIORITY,
+ .name = "dynrubin",
+ .compr = JFFS2_COMPR_RUBINMIPS,
+ .compress = jffs2_dynrubin_compress,
+ .decompress = &jffs2_dynrubin_decompress,
+#ifdef JFFS2_DYNRUBIN_DISABLED
+ .disabled = 1,
+#else
+ .disabled = 0,
+#endif
+};
+
+int jffs2_dynrubin_init(void)
+{
+ return jffs2_register_compressor(&jffs2_dynrubin_comp);
+}
+
+void jffs2_dynrubin_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_dynrubin_comp);
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/zutil.h>
#include <asm/semaphore.h>
#include "nodelist.h"
+#include "compr.h"
/* Plan: call deflate() with avail_in == *sourcelen,
avail_out = *dstlen - 12 and flush == Z_FINISH.
static z_stream inf_strm, def_strm;
#ifdef __KERNEL__ /* Linux-only */
-int __init jffs2_zlib_init(void)
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+static int __init alloc_workspaces(void)
{
def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
if (!def_strm.workspace) {
return 0;
}
-void jffs2_zlib_exit(void)
+static void free_workspaces(void)
{
vfree(def_strm.workspace);
vfree(inf_strm.workspace);
}
+#else
+#define alloc_workspaces() (0)
+#define free_workspaces() do { } while(0)
#endif /* __KERNEL__ */
int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *sourcelen, uint32_t *dstlen)
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
int ret;
return ret;
}
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t srclen, uint32_t destlen)
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
{
int ret;
int wbits = MAX_WBITS;
if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
printk(KERN_WARNING "inflateInit failed\n");
up(&inflate_sem);
- return;
+ return 1;
}
while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
}
zlib_inflateEnd(&inf_strm);
up(&inflate_sem);
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+ .priority = JFFS2_ZLIB_PRIORITY,
+ .name = "zlib",
+ .compr = JFFS2_COMPR_ZLIB,
+ .compress = &jffs2_zlib_compress,
+ .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+ .disabled = 1,
+#else
+ .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+ int ret;
+
+ ret = alloc_workspaces();
+ if (ret)
+ return ret;
+
+ ret = jffs2_register_compressor(&jffs2_zlib_comp);
+ if (ret)
+ free_workspaces();
+
+ return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_zlib_comp);
+ free_workspaces();
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $
+ * $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $
*
*/
#ifndef __ECOS
static void jffs2_erase_callback(struct erase_info *);
#endif
-static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
int ret;
+ uint32_t bad_offset;
#ifdef __ECOS
ret = jffs2_flash_erase(c, jeb);
if (!ret) {
instr->len = c->sector_size;
instr->callback = jffs2_erase_callback;
instr->priv = (unsigned long)(&instr[1]);
+ instr->fail_addr = 0xffffffff;
((struct erase_priv_struct *)instr->priv)->jeb = jeb;
((struct erase_priv_struct *)instr->priv)->c = c;
- /* NAND , read out the fail counter, if possible */
- if (!jffs2_can_mark_obsolete(c))
- jffs2_nand_read_failcnt(c,jeb);
-
ret = c->mtd->erase(c->mtd, instr);
if (!ret)
return;
+ bad_offset = instr->fail_addr;
kfree(instr);
#endif /* __ECOS */
else
printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
- jffs2_erase_failed(c, jeb);
+ jffs2_erase_failed(c, jeb, bad_offset);
}
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
{
struct jffs2_eraseblock *jeb;
spin_unlock(&c->erase_completion_lock);
jffs2_mark_erased_block(c, jeb);
+ if (!--count) {
+ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
+ goto done;
+ }
+
} else if (!list_empty(&c->erase_pending_list)) {
jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
}
spin_unlock(&c->erase_completion_lock);
+ done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
up(&c->erase_free_sem);
jffs2_erase_pending_trigger(c);
}
-static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
{
- spin_lock(&c->erase_completion_lock);
- c->erasing_size -= c->sector_size;
- c->bad_size += c->sector_size;
- list_del(&jeb->list);
- list_add(&jeb->list, &c->bad_list);
- c->nr_erasing_blocks--;
- spin_unlock(&c->erase_completion_lock);
- wake_up(&c->erase_wait);
+ /* For NAND, if the failure did not occur at the device level for a
+ specific physical page, don't bother updating the bad block table. */
+ if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
+ /* We had a device-level failure to erase. Let's see if we've
+ failed too many times. */
+ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
+ /* We'd like to give this block another try. */
+ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+ c->dirty_size += c->sector_size;
+ jeb->dirty_size = c->sector_size;
+ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+ }
+
+ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->bad_size += c->sector_size;
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
}
#ifndef __ECOS
if(instr->state != MTD_ERASE_DONE) {
printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
- jffs2_erase_failed(priv->c, priv->jeb);
+ jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
} else {
jffs2_erase_succeeded(priv->c, priv->jeb);
}
jeb->last_node = NULL;
}
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
-{
- OFNI_BS_2SFFJ(c)->s_dirt = 1;
-}
-
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_raw_node_ref *marker_ref = NULL;
unsigned char *ebuf;
size_t retlen;
int ret;
+ uint32_t bad_offset;
if (!jffs2_cleanmarker_oob(c)) {
marker_ref = jffs2_alloc_raw_node_ref();
uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
int i;
+ bad_offset = ofs;
+
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
/* It's OK. We know it's properly aligned */
unsigned long datum = *(unsigned long *)(&ebuf[i]);
if (datum + 1) {
- printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+ bad_offset += i;
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
bad:
if (!jffs2_cleanmarker_oob(c))
jffs2_free_raw_node_ref(marker_ref);
- else
- jffs2_write_nand_badblock( c ,jeb );
kfree(ebuf);
bad2:
spin_lock(&c->erase_completion_lock);
- c->erasing_size -= c->sector_size;
- c->bad_size += c->sector_size;
-
- list_add_tail(&jeb->list, &c->bad_list);
- c->nr_erasing_blocks--;
+ /* Stick it on a list (any list) so
+ erase_failed can take it right off
+ again. Silly, but shouldn't happen
+ often. */
+ list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
- wake_up(&c->erase_wait);
+ jffs2_erase_failed(c, jeb, bad_offset);
return;
}
}
}
kfree(ebuf);
}
-
+
+ bad_offset = jeb->offset;
+
/* Write the erase complete marker */
D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
if (jffs2_cleanmarker_oob(c)) {
.totlen = cpu_to_je32(c->cleanmarker_size)
};
- marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4));
+ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
- ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker);
+ /* We only write the header; the rest was noise or padding anyway */
+ ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
goto bad2;
}
- if (retlen != je32_to_cpu(marker.totlen)) {
+ if (retlen != sizeof(marker)) {
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
- jeb->offset, je32_to_cpu(marker.totlen), retlen);
+ jeb->offset, sizeof(marker), retlen);
goto bad2;
}
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
- marker_ref->totlen = PAD(je32_to_cpu(marker.totlen));
+ marker_ref->__totlen = c->cleanmarker_size;
jeb->first_node = jeb->last_node = marker_ref;
- jeb->free_size = c->sector_size - marker_ref->totlen;
- jeb->used_size = marker_ref->totlen;
+ jeb->free_size = c->sector_size - c->cleanmarker_size;
+ jeb->used_size = c->cleanmarker_size;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: file.c,v 1.96 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $
*
*/
unsigned char *pg_buf;
int ret;
- D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
+ D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
if (!PageLocked(pg))
PAGE_BUG(pg);
flush_dcache_page(pg);
kunmap(pg);
- D1(printk(KERN_DEBUG "readpage finished\n"));
+ D2(printk(KERN_DEBUG "readpage finished\n"));
return 0;
}
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_raw_inode *ri;
+ unsigned aligned_start = start & ~3;
int ret = 0;
uint32_t writtenlen = 0;
hurt to do it again. The alternative is ifdefs, which are ugly. */
kmap(pg);
- ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
- (pg->index << PAGE_CACHE_SHIFT) + start,
- end - start, &writtenlen);
+ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
+ (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
+ end - aligned_start, &writtenlen);
kunmap(pg);
/* There was an error writing. */
SetPageError(pg);
}
+
+ /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
+ if (writtenlen < (start&3))
+ writtenlen = 0;
+ else
+ writtenlen -= (start&3);
if (writtenlen) {
if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: fs.c,v 1.32 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $
*
*/
mdata = kmalloc(f->metadata->size, GFP_USER);
if (!mdata)
return -ENOMEM;
- ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
+ ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
if (ret) {
kfree(mdata);
return ret;
old_metadata = f->metadata;
- if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
- vmtruncate(inode, iattr->ia_size);
+ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
- }
if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
jffs2_add_full_dnode_to_inode(c, f, new_metadata);
up(&f->sem);
jffs2_complete_reservation(c);
+ /* We have to do the vmtruncate() without f->sem held, since
+ some pages may be locked and waiting for it in readpage().
+ We are protected from a simultaneous write() extending i_size
+ back past iattr->ia_size, because do_truncate() holds the
+ generic inode semaphore. */
+ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
+ vmtruncate(inode, iattr->ia_size);
+
return 0;
}
case S_IFCHR:
/* Read the device numbers from the media */
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
- if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+ if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
/* Eep */
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
up(&f->sem);
struct iattr iattr;
if (!(inode->i_state & I_DIRTY_DATASYNC)) {
- D1(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
+ D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
return;
}
/* We stop if it was running, then restart if it needs to.
This also catches the case where it was stopped and this
- is just a remount to restart it */
- if (!(sb->s_flags & MS_RDONLY))
+ is just a remount to restart it.
+ Flush the writebuffer, if neccecary, else we loose it */
+ if (!(sb->s_flags & MS_RDONLY)) {
jffs2_stop_garbage_collect_thread(c);
+ down(&c->alloc_sem);
+ jffs2_flush_wbuf_pad(c);
+ up(&c->alloc_sem);
+ }
if (!(*flags & MS_RDONLY))
jffs2_start_garbage_collect_thread(c);
D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
jffs2_garbage_collect_trigger(c);
- jffs2_erase_pending_blocks(c);
+ jffs2_erase_pending_blocks(c, 0);
jffs2_flush_wbuf_gc(c, 0);
}
c = JFFS2_SB_INFO(sb);
+#ifndef CONFIG_JFFS2_FS_NAND
+ if (c->mtd->type == MTD_NANDFLASH) {
+ printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
+ return -EINVAL;
+ }
+#endif
+
c->flash_size = c->mtd->size;
/*
* Check, if we have to concatenate physical blocks to larger virtual blocks
* to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
*/
- blocks = c->flash_size / c->mtd->erasesize;
- while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
+ c->sector_size = c->mtd->erasesize;
+ blocks = c->flash_size / c->sector_size;
+ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
blocks >>= 1;
+ c->sector_size <<= 1;
+ }
- c->sector_size = c->flash_size / blocks;
+ /*
+ * Size alignment check
+ */
+ if ((c->sector_size * blocks) != c->flash_size) {
+ c->flash_size = c->sector_size * blocks;
+ printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
+ c->flash_size / 1024);
+ }
+
if (c->sector_size != c->mtd->erasesize)
printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
c->mtd->erasesize / 1024, c->sector_size / 1024);
c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
/* Joern -- stick alignment for weird 8-byte-page flash here */
- if (jffs2_cleanmarker_oob(c)) {
- /* NAND (or other bizarre) flash... do setup accordingly */
- ret = jffs2_nand_flash_setup(c);
- if (ret)
- return ret;
- }
+ /* NAND (or other bizarre) flash... do setup accordingly */
+ ret = jffs2_flash_setup(c);
+ if (ret)
+ return ret;
c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
if (!c->inocache_list) {
out_inohash:
kfree(c->inocache_list);
out_wbuf:
- jffs2_nand_flash_cleanup(c);
+ jffs2_flash_cleanup(c);
+
+ return ret;
+}
+
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f)
+{
+ iput(OFNI_EDONI_2SFFJ(f));
+}
+
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+ int inum, int nlink)
+{
+ struct inode *inode;
+ struct jffs2_inode_cache *ic;
+ if (!nlink) {
+ /* The inode has zero nlink but its nodes weren't yet marked
+ obsolete. This has to be because we're still waiting for
+ the final (close() and) iput() to happen.
+
+ There's a possibility that the final iput() could have
+ happened while we were contemplating. In order to ensure
+ that we don't cause a new read_inode() (which would fail)
+ for the inode in question, we use ilookup() in this case
+ instead of iget().
+
+ The nlink can't _become_ zero at this point because we're
+ holding the alloc_sem, and jffs2_do_unlink() would also
+ need that while decrementing nlink on any inode.
+ */
+ inode = ilookup(OFNI_BS_2SFFJ(c), inum);
+ if (!inode) {
+ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+ inum));
+
+ spin_lock(&c->inocache_lock);
+ ic = jffs2_get_ino_cache(c, inum);
+ if (!ic) {
+ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+ spin_unlock(&c->inocache_lock);
+ return NULL;
+ }
+ if (ic->state != INO_STATE_CHECKEDABSENT) {
+ /* Wait for progress. Don't just loop */
+ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
+ ic->ino, ic->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ } else {
+ spin_unlock(&c->inocache_lock);
+ }
+
+ return NULL;
+ }
+ } else {
+ /* Inode has links to it still; they're not going away because
+ jffs2_do_unlink() would need the alloc_sem and we have it.
+ Just iget() it, and if read_inode() is necessary that's OK.
+ */
+ inode = iget(OFNI_BS_2SFFJ(c), inum);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ }
+ if (is_bad_inode(inode)) {
+ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
+ inum, nlink);
+ /* NB. This will happen again. We need to do something appropriate here. */
+ iput(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ return JFFS2_INODE_INFO(inode);
+}
+
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ unsigned long offset,
+ unsigned long *priv)
+{
+ struct inode *inode = OFNI_EDONI_2SFFJ(f);
+ struct page *pg;
+
+ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
+ (void *)jffs2_do_readpage_unlock, inode);
+ if (IS_ERR(pg))
+ return (void *)pg;
+
+ *priv = (unsigned long)pg;
+ return kmap(pg);
+}
+
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+ unsigned char *ptr,
+ unsigned long *priv)
+{
+ struct page *pg = (void *)*priv;
+
+ kunmap(pg);
+ page_cache_release(pg);
+}
+
+int jffs2_flash_setup(struct jffs2_sb_info *c) {
+ int ret = 0;
+
+ if (jffs2_cleanmarker_oob(c)) {
+ /* NAND flash... do setup accordingly */
+ ret = jffs2_nand_flash_setup(c);
+ if (ret)
+ return ret;
+ }
+ /* add setups for other bizarre flashes here... */
return ret;
}
+
+void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
+
+ if (jffs2_cleanmarker_oob(c)) {
+ jffs2_nand_flash_cleanup(c);
+ }
+
+ /* add cleanups for other bizarre flashes here... */
+}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: gc.c,v 1.114 2003/10/09 13:53:35 dwmw2 Exp $
+ * $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $
*
*/
#include <linux/compiler.h>
#include <linux/stat.h>
#include "nodelist.h"
+#include "compr.h"
static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
struct jffs2_inode_cache *ic,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
uint32_t start, uint32_t end);
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
- struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic);
+ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
/* Called with erase_completion_lock held */
static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
nextlist = &c->erasable_list;
} else {
/* Eep. All were empty */
- printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
+ D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
return NULL;
}
*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{
+ struct jffs2_inode_info *f;
struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
- uint32_t inum;
- int ret = 0;
+ int ret = 0, inum, nlink;
if (down_interruptible(&c->alloc_sem))
return -EINTR;
ic->state = INO_STATE_CHECKING;
spin_unlock(&c->inocache_lock);
- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
ret = jffs2_do_crccheck_inode(c, ic);
if (ret)
jeb = jffs2_find_gc_block(c);
if (!jeb) {
- printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+ D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
return -EIO;
while(ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
- jeb->gc_node = raw = raw->next_phys;
- if (!raw) {
+ raw = raw->next_phys;
+ if (unlikely(!raw)) {
printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+ jeb->gc_node = raw;
spin_unlock(&c->erase_completion_lock);
up(&c->alloc_sem);
BUG();
}
}
+ jeb->gc_node = raw;
+
D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
+
if (!raw->next_in_ino) {
/* Inode-less node. Clean marker, snapshot or something like that */
/* FIXME: If it's something that needs to be copied, including something
up(&c->alloc_sem);
goto eraseit_lock;
}
-
- inum = jffs2_raw_ref_to_inum(raw);
- D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
+
+ ic = jffs2_raw_ref_to_ic(raw);
+
+ /* We need to hold the inocache. Either the erase_completion_lock or
+ the inocache_lock are sufficient; we trade down since the inocache_lock
+ causes less contention. */
+ spin_lock(&c->inocache_lock);
spin_unlock(&c->erase_completion_lock);
- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
/* Three possibilities:
1. Inode is already in-core. We must iget it and do proper
3. Inode is not in-core, node is not pristine. We must iget()
and take the slow path.
*/
- spin_lock(&c->inocache_lock);
- ic = jffs2_get_ino_cache(c, inum);
-
- /* This should never fail unless I'm particularly stupid.
- So we don't check before dereferencing it */
switch(ic->state) {
case INO_STATE_CHECKEDABSENT:
ic->state = INO_STATE_GC;
else {
D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
- inum));
+ ic->ino));
}
break;
case INO_STATE_PRESENT:
- case INO_STATE_UNCHECKED:
- /* It's in-core or hasn't been checked. GC must iget() it. */
+ /* It's in-core. GC must iget() it. */
break;
+ case INO_STATE_UNCHECKED:
case INO_STATE_CHECKING:
+ case INO_STATE_GC:
/* Should never happen. We should have finished checking
- by the time we actually start doing any GC. */
+ by the time we actually start doing any GC, and since
+ we're holding the alloc_sem, no other garbage collection
+ can happen.
+ */
+ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+ ic->ino, ic->state);
+ up(&c->alloc_sem);
+ spin_unlock(&c->inocache_lock);
BUG();
-
- case INO_STATE_GC:
- /* Should never happen. We are holding the alloc_sem,
- no other garbage collection can happen. Note that we
- do depend on this later when deciding to do a simple
- node copy */
- BUG();
-
case INO_STATE_READING:
/* Someone's currently trying to read it. We must wait for
them to finish and then go through the full iget() route
up(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
- inum, ic->state));
+ ic->ino, ic->state));
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
/* And because we dropped the alloc_sem we must start again from the
beginning. Ponder chance of livelock here -- we're returning success
A: Small enough that I don't care :)
*/
return 0;
-
}
- spin_unlock(&c->inocache_lock);
-
/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
node intact, and we don't have to muck about with the fragtree etc.
because we know it's not in-core. If it _was_ in-core, we go through
all the iget() crap anyway */
if (ic->state == INO_STATE_GC) {
+ spin_unlock(&c->inocache_lock);
+
ret = jffs2_garbage_collect_pristine(c, ic, raw);
- jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
- if (ret != -EBADFD)
+ spin_lock(&c->inocache_lock);
+ ic->state = INO_STATE_CHECKEDABSENT;
+ wake_up(&c->inocache_wq);
+
+ if (ret != -EBADFD) {
+ spin_unlock(&c->inocache_lock);
goto release_sem;
+ }
- /* Fall through if it wanted us to */
+ /* Fall through if it wanted us to, with inocache_lock held */
}
- ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
+ /* Prevent the fairly unlikely race where the gcblock is
+ entirely obsoleted by the final close of a file which had
+ the only valid nodes in the block, followed by erasure,
+ followed by freeing of the ic because the erased block(s)
+ held _all_ the nodes of that inode.... never been seen but
+ it's vaguely possible. */
+
+ inum = ic->ino;
+ nlink = ic->nlink;
+ spin_unlock(&c->inocache_lock);
+
+ f = jffs2_gc_fetch_inode(c, inum, nlink);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ if (!f)
+ return 0;
+
+ ret = jffs2_garbage_collect_live(c, jeb, raw, f);
+
+ jffs2_gc_release_inode(c, f);
release_sem:
up(&c->alloc_sem);
return ret;
}
-
static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
- struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
+ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
{
- struct jffs2_inode_info *f;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *fn = NULL;
struct jffs2_full_dirent *fd;
uint32_t start = 0, end = 0, nrfrags = 0;
- struct inode *inode;
int ret = 0;
- inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
- if (is_bad_inode(inode)) {
- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
- /* NB. This will happen again. We need to do something appropriate here. */
- up(&c->alloc_sem);
- iput(inode);
- return -EIO;
- }
-
- f = JFFS2_INODE_INFO(inode);
down(&f->sem);
/* Now we have the lock for this inode. Check that it's still the one at the head
of the list. */
+ spin_lock(&c->erase_completion_lock);
+
+ if (c->gcblock != jeb) {
+ spin_unlock(&c->erase_completion_lock);
+ D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
+ goto upnout;
+ }
if (ref_obsolete(raw)) {
+ spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
/* They'll call again */
goto upnout;
}
+ spin_unlock(&c->erase_completion_lock);
+
/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
if (f->metadata && f->metadata->raw == raw) {
fn = f->metadata;
if (frag->node && frag->node->raw == raw) {
fn = frag->node;
end = frag->ofs + frag->size;
-#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */
- if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) {
- if (fn->frags > 1) {
- printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags);
- mark_ref_normal(raw);
- }
- /* A hole node which isn't multi-page should be garbage-collected
- and merged anyway, so we just check for the frag size here,
- rather than mucking around with actually reading the node
- and checking the compression type, which is the real way
- to tell a hole node. */
- if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) {
- printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
- ref_offset(raw));
- mark_ref_normal(raw);
- }
-
- if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
- printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
- ref_offset(raw), frag->ofs, frag->ofs+frag->size);
- mark_ref_normal(raw);
- }
- }
-#endif
if (!nrfrags++)
start = frag->ofs;
if (nrfrags == frag->node->frags)
}
if (fn) {
if (ref_flags(raw) == REF_PRISTINE) {
- ret = jffs2_garbage_collect_pristine(c, ic, raw);
+ ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
if (!ret) {
/* Urgh. Return it sensibly. */
- frag->node->raw = ic->nodes;
+ frag->node->raw = f->inocache->nodes;
}
if (ret != -EBADFD)
goto upnout;
}
upnout:
up(&f->sem);
- iput(inode);
return ret;
}
size_t retlen;
int ret;
uint32_t phys_ofs, alloclen;
- uint32_t crc;
+ uint32_t crc, rawlen;
int retried = 0;
D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+ rawlen = ref_totlen(c, c->gcblock, raw);
+
/* Ask for a small amount of space (or the totlen if smaller) because we
don't want to force wastage of the end of a block if splitting would
work. */
- ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
- &phys_ofs, &alloclen);
+ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN,
+ rawlen), &phys_ofs, &alloclen);
if (ret)
return ret;
- if (alloclen < raw->totlen) {
+ if (alloclen < rawlen) {
/* Doesn't fit untouched. We'll go the old route and split it */
return -EBADFD;
}
- node = kmalloc(raw->totlen, GFP_KERNEL);
+ node = kmalloc(rawlen, GFP_KERNEL);
if (!node)
return -ENOMEM;
- ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
- if (!ret && retlen != raw->totlen)
+ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+ if (!ret && retlen != rawlen)
ret = -EIO;
if (ret)
goto out_node;
/* OK, all the CRCs are good; this node can just be copied as-is. */
retry:
nraw->flash_offset = phys_ofs;
- nraw->totlen = raw->totlen;
+ nraw->__totlen = rawlen;
nraw->next_phys = NULL;
- ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
+ ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
- if (ret || (retlen != raw->totlen)) {
+ if (ret || (retlen != rawlen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
- raw->totlen, phys_ofs, ret, retlen);
+ rawlen, phys_ofs, ret, retlen);
if (retlen) {
/* Doesn't belong to any inode */
nraw->next_in_ino = NULL;
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
- ret = jffs2_reserve_space_gc(c, raw->totlen, &phys_ofs, &dummy);
+ ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
if (!ret) {
D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
return -ENOMEM;
}
- ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
+ ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
if (ret) {
printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
kfree(mdata);
delete a 'real' dirent with the same name that's still
somewhere else on the flash. */
if (!jffs2_can_mark_obsolete(c)) {
- struct jffs2_raw_dirent rd;
+ struct jffs2_raw_dirent *rd;
struct jffs2_raw_node_ref *raw;
int ret;
size_t retlen;
int name_len = strlen(fd->name);
uint32_t name_crc = crc32(0, fd->name, name_len);
- char *namebuf = NULL;
+ uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
+
+ rd = kmalloc(rawlen, GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
/* Prevent the erase code from nicking the obsolete node refs while
we're looking at them. I really don't like this extra lock but
down(&c->erase_free_sem);
for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
+
/* We only care about obsolete ones */
if (!(ref_obsolete(raw)))
continue;
+ /* Any dirent with the same name is going to have the same length... */
+ if (ref_totlen(c, NULL, raw) != rawlen)
+ continue;
+
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
if ((raw->flash_offset & ~(c->sector_size-1)) ==
(fd->raw->flash_offset & ~(c->sector_size-1)))
continue;
- /* This is an obsolete node belonging to the same directory */
- ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
+ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
+
+ /* This is an obsolete node belonging to the same directory, and it's of the right
+ length. We need to take a closer look...*/
+ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
if (ret) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw));
+ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
/* If we can't read it, we don't need to continue to obsolete it. Continue */
continue;
}
- if (retlen != sizeof(struct jffs2_unknown_node)) {
+ if (retlen != rawlen) {
printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
- retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
+ retlen, rawlen, ref_offset(raw));
continue;
}
- if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT ||
- PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len))
- continue;
- /* OK, it's a dirent node, it's the right length. We have to take a
- closer look at it... */
- ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd);
- if (ret) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw));
- /* If we can't read it, we don't need to continune to obsolete it. Continue */
+ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
continue;
- }
- if (retlen != sizeof(rd)) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
- retlen, sizeof(rd), ref_offset(raw));
- continue;
- }
/* If the name CRC doesn't match, skip */
- if (je32_to_cpu(rd.name_crc) != name_crc)
+ if (je32_to_cpu(rd->name_crc) != name_crc)
continue;
+
/* If the name length doesn't match, or it's another deletion dirent, skip */
- if (rd.nsize != name_len || !je32_to_cpu(rd.ino))
+ if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
continue;
/* OK, check the actual name now */
- if (!namebuf) {
- namebuf = kmalloc(name_len + 1, GFP_KERNEL);
- if (!namebuf) {
- up(&c->erase_free_sem);
- return -ENOMEM;
- }
- }
- /* We read the extra byte before it so it's a word-aligned read */
- ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf);
- if (ret) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw));
- /* If we can't read it, we don't need to continune to obsolete it. Continue */
- continue;
- }
- if (retlen != name_len+1) {
- printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
- retlen, name_len+1, ref_offset(raw));
- continue;
- }
- if (memcmp(namebuf+1, fd->name, name_len))
+ if (memcmp(rd->name, fd->name, name_len))
continue;
/* OK. The name really does match. There really is still an older node on
the flash which our deletion dirent obsoletes. So we have to write out
a new deletion dirent to replace it */
-
- if (namebuf)
- kfree(namebuf);
-
up(&c->erase_free_sem);
+
+ D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
+ ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
+ kfree(rd);
+
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
up(&c->erase_free_sem);
-
- if (namebuf)
- kfree(namebuf);
+ kfree(rd);
}
/* No need for it any more. Just mark it obsolete and remove it from the list */
je32_to_cpu(ri.ino));
});
+ /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
+ mark_ref_normal(new_fn->raw);
+
for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
frag; frag = frag_next(frag)) {
if (frag->ofs > fn->size + fn->ofs)
uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
int ret = 0;
unsigned char *comprbuf = NULL, *writebuf;
- struct page *pg;
+ unsigned long pg;
unsigned char *pg_ptr;
- /* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f);
-
+
memset(&ri, 0, sizeof(ri));
D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
* page OK. We'll actually write it out again in commit_write, which is a little
* suboptimal, but at least we're correct.
*/
-#ifdef __ECOS
- pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
-#else
- pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
-#endif
- if (IS_ERR(pg)) {
- printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
- return PTR_ERR(pg);
+ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+
+ if (IS_ERR(pg_ptr)) {
+ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
+ return PTR_ERR(pg_ptr);
}
- pg_ptr = (char *)kmap(pg);
- comprbuf = kmalloc(end - start, GFP_KERNEL);
offset = start;
while(offset < orig_end) {
uint32_t datalen;
uint32_t cdatalen;
- char comprtype = JFFS2_COMPR_NONE;
+ uint16_t comprtype = JFFS2_COMPR_NONE;
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
- if (comprbuf) {
- comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
- }
- if (comprtype) {
- writebuf = comprbuf;
- } else {
- datalen = cdatalen;
- }
+ comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
+
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
ri.offset = cpu_to_je32(offset);
ri.csize = cpu_to_je32(cdatalen);
ri.dsize = cpu_to_je32(datalen);
- ri.compr = comprtype;
+ ri.compr = comprtype & 0xff;
+ ri.usercompr = (comprtype >> 8) & 0xff;
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
- ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
+ ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
- new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, ALLOC_GC);
+ new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+
+ jffs2_free_comprbuf(comprbuf, writebuf);
if (IS_ERR(new_fn)) {
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
f->metadata = NULL;
}
}
- if (comprbuf) kfree(comprbuf);
- kunmap(pg);
- /* XXX: Does the page get freed automatically? */
- /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
- page_cache_release(pg);
+ jffs2_gc_release_page(c, pg_ptr, &pg);
return ret;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: ioctl.c,v 1.7 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $
*
*/
{
/* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
will include compression support etc. */
- return -EINVAL;
+ return -ENOTTY;
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: malloc.c,v 1.25 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $
*
*/
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.c,v 1.80 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $
*
*/
/* Put a new tmp_dnode_info into the list, keeping the list in
order of increasing version
*/
-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
{
struct jffs2_tmp_dnode_info **prev = list;
cond_resched();
/* FIXME: point() */
- err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
+ err = jffs2_flash_read(c, (ref_offset(ref)),
+ min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
+ &retlen, (void *)&node);
if (err) {
printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
goto free_out;
/* Check we've managed to read at least the common node header */
- if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
+ if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
printk(KERN_WARNING "short read in get_inode_nodes()\n");
err = -EIO;
goto free_out;
/* If we've never checked the CRCs on this node, check them now. */
if (ref_flags(ref) == REF_UNCHECKED) {
- uint32_t crc;
+ uint32_t crc, len;
struct jffs2_eraseblock *jeb;
crc = crc32(0, &node, sizeof(node.i)-8);
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
- jeb->used_size += ref->totlen;
- jeb->unchecked_size -= ref->totlen;
- c->used_size += ref->totlen;
- c->unchecked_size -= ref->totlen;
+ len = ref_totlen(c, jeb, ref);
+
+ jeb->used_size += len;
+ jeb->unchecked_size -= len;
+ c->used_size += len;
+ c->unchecked_size -= len;
/* If node covers at least a whole page, or if it starts at the
beginning of a page and runs to the end of the file, or if
default:
if (ref_flags(ref) == REF_UNCHECKED) {
struct jffs2_eraseblock *jeb;
+ uint32_t len;
printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
je16_to_cpu(node.u.nodetype), ref_offset(ref));
/* Mark the node as having been checked and fix the accounting accordingly */
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[ref->flash_offset / c->sector_size];
- jeb->used_size += ref->totlen;
- jeb->unchecked_size -= ref->totlen;
- c->used_size += ref->totlen;
- c->unchecked_size -= ref->totlen;
+ len = ref_totlen(c, jeb, ref);
+
+ jeb->used_size += len;
+ jeb->unchecked_size -= len;
+ c->used_size += len;
+ c->unchecked_size -= len;
mark_ref_normal(ref);
spin_unlock(&c->erase_completion_lock);
jffs2_free_node_frag(frag);
frag = parent;
+
+ cond_resched();
}
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.h,v 1.104 2003/10/08 11:45:11 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.119 2004/05/26 12:28:12 gleixner Exp $
*
*/
#define D2(x)
#endif
+#define JFFS2_NATIVE_ENDIAN
+
+/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
+ whatever OS we're actually running on here too. */
+
+#if defined(JFFS2_NATIVE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){x})
+#define cpu_to_je32(x) ((jint32_t){x})
+#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
+
+#define je16_to_cpu(x) ((x).v16)
+#define je32_to_cpu(x) ((x).v32)
+#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
+#elif defined(JFFS2_BIG_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (be16_to_cpu(x.v16))
+#define je32_to_cpu(x) (be32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
+#elif defined(JFFS2_LITTLE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (le16_to_cpu(x.v16))
+#define je32_to_cpu(x) (le32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
+#else
+#error wibble
+#endif
+
/*
This is all we need to keep in-core for each raw node during normal
operation. As and when we do read_inode on a particular inode, we can
word so you know when you've got there :) */
struct jffs2_raw_node_ref *next_phys;
uint32_t flash_offset;
- uint32_t totlen;
-
+ uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
+};
+
/* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
- to play with. So we set the least significant bit to 1 to
- signify that the node is obsoleted by later nodes.
- */
+ to play with, which indicate the node's status; see below: */
#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
#define REF_PRISTINE 2 /* Completely clean. GC without looking */
#define ref_offset(ref) ((ref)->flash_offset & ~3)
#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
-};
/*
Used for keeping track of deletion nodes &c, which can only be marked
BUG(); \
} \
if (ref_flags(ref2) == REF_UNCHECKED) \
- my_unchecked_size += ref2->totlen; \
+ my_unchecked_size += ref_totlen(c, jeb, ref2); \
else if (!ref_obsolete(ref2)) \
- my_used_size += ref2->totlen; \
+ my_used_size += ref_totlen(c, jeb, ref2); \
if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
} \
} while(0)
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_node_ref *ref)
+{
+ uint32_t ref_end;
+
+ if (ref->next_phys)
+ ref_end = ref_offset(ref->next_phys);
+ else {
+ if (!jeb)
+ jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+ /* Last node in block. Use free_space */
+ BUG_ON(ref != jeb->last_node);
+ ref_end = jeb->offset + c->sector_size - jeb->free_size;
+ }
+ return ref_end - ref_offset(ref);
+}
+
+static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
+ struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_node_ref *ref)
+{
+ uint32_t ret;
+
+ D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
+ printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
+ jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
+ BUG();
+ })
+
+#if 1
+ ret = ref->__totlen;
+#else
+ /* This doesn't actually work yet */
+ ret = __ref_totlen(c, jeb, ref);
+ if (ret != ref->__totlen) {
+ printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+ ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+ ret, ref->__totlen);
+ if (!jeb)
+ jeb = &c->blocks[ref->flash_offset / c->sector_size];
+ paranoia_failed_dump(jeb);
+ BUG();
+ }
+#endif
+ return ret;
+}
+
+
#define ALLOC_NORMAL 0 /* Normal allocation */
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
#define PAD(x) (((x)+3)&~3)
-static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
{
while(raw->next_in_ino) {
raw = raw->next_in_ino;
}
- return ((struct jffs2_inode_cache *)raw)->ino;
+ return ((struct jffs2_inode_cache *)raw);
}
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
/* nodelist.c */
D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
/* nodemgmt.c */
+int jffs2_thread_should_wake(struct jffs2_sb_info *c);
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
/* read.c */
-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+ struct jffs2_full_dnode *fd, unsigned char *buf,
+ int ofs, int len);
int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *buf, uint32_t offset, uint32_t len);
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
-
-/* compr.c */
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
- uint32_t *datalen, uint32_t *cdatalen);
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
- unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
-
/* scan.c */
int jffs2_scan_medium(struct jffs2_sb_info *c);
void jffs2_rotate_lists(struct jffs2_sb_info *c);
/* erase.c */
void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
#ifdef CONFIG_JFFS2_FS_NAND
/* wbuf.c */
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
#endif
-/* compr_zlib.c */
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
-
#endif /* __JFFS2_NODELIST_H__ */
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
*
*/
if (list_empty(&c->free_list)) {
- DECLARE_WAITQUEUE(wait, current);
-
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_list)) {
struct jffs2_eraseblock *ejeb;
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
return -ENOSPC;
}
- /* Make sure this can't deadlock. Someone has to start the erases
- of erase_pending blocks */
-#ifdef __ECOS
- /* In eCos, we don't have a handy kernel thread doing the erases for
- us. We do them ourselves right now. */
- jffs2_erase_pending_blocks(c);
-#else
- set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&c->erase_wait, &wait);
- D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
- c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
- list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
- if (!list_empty(&c->erase_pending_list)) {
- D1(printk(KERN_DEBUG "Triggering pending erases\n"));
- jffs2_erase_pending_trigger(c);
- }
+
spin_unlock(&c->erase_completion_lock);
- schedule();
- remove_wait_queue(&c->erase_wait, &wait);
+ /* Don't wait for it; just erase one right now */
+ jffs2_erase_pending_blocks(c, 1);
spin_lock(&c->erase_completion_lock);
- if (signal_pending(current)) {
- return -EINTR;
- }
-#endif
+
/* An erase may have failed, decreasing the
amount of free space available. So we must
restart from the beginning */
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
{
struct jffs2_eraseblock *jeb;
- uint32_t len = new->totlen;
+ uint32_t len;
jeb = &c->blocks[new->flash_offset / c->sector_size];
+ len = ref_totlen(c, jeb, new);
+
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
spin_lock(&c->erase_completion_lock);
if (ref_flags(ref) == REF_UNCHECKED) {
- D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
+ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
- ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
- D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
- jeb->unchecked_size -= ref->totlen;
- c->unchecked_size -= ref->totlen;
+ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+ jeb->unchecked_size -= ref_totlen(c, jeb, ref);
+ c->unchecked_size -= ref_totlen(c, jeb, ref);
} else {
- D1(if (unlikely(jeb->used_size < ref->totlen)) {
+ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
- ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
- D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
- jeb->used_size -= ref->totlen;
- c->used_size -= ref->totlen;
+ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+ jeb->used_size -= ref_totlen(c, jeb, ref);
+ c->used_size -= ref_totlen(c, jeb, ref);
}
// Take care, that wasted size is taken into concern
- if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
+ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
- addedsize = ref->totlen;
- jeb->dirty_size += ref->totlen;
- c->dirty_size += ref->totlen;
+ addedsize = ref_totlen(c, jeb, ref);
+ jeb->dirty_size += ref_totlen(c, jeb, ref);
+ c->dirty_size += ref_totlen(c, jeb, ref);
/* Convert wasted space to dirty, if not a bad block */
if (jeb->wasted_size) {
} else {
D1(printk("Wasting\n"));
addedsize = 0;
- jeb->wasted_size += ref->totlen;
- c->wasted_size += ref->totlen;
+ jeb->wasted_size += ref_totlen(c, jeb, ref);
+ c->wasted_size += ref_totlen(c, jeb, ref);
}
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
if (jffs2_wbuf_dirty(c)) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
-#if 0 /* This check was added to allow us to find places where we added nodes to the lists
- after dropping the alloc_sem, and it did that just fine. But it also caused us to
- lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
- have needed to. So I suspect it's outlived its usefulness. Thomas? */
-
- /* We've changed the rules slightly. After
- writing a node you now mustn't drop the
- alloc_sem before you've finished all the
- list management - this is so that when we
- get here, we know that no other nodes have
- been written, and the above check on wbuf
- is valid - wbuf_len is nonzero IFF the node
- which obsoletes this node is still in the
- wbuf.
-
- So we BUG() if that new rule is broken, to
- make sure we catch it and fix it.
- */
- if (!down_trylock(&c->alloc_sem)) {
- up(&c->alloc_sem);
- printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
- BUG();
- }
-#endif
} else {
if (jiffies & 127) {
/* Most of the time, we just erase it immediately. Otherwise we
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
- if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
+ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
+ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
return;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
return;
}
/* XXX FIXME: This is ugly now */
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
+
+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
+{
+ int ret = 0;
+ uint32_t dirty;
+
+ if (c->unchecked_size) {
+ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
+ c->unchecked_size, c->checked_ino));
+ return 1;
+ }
+
+ /* dirty_size contains blocks on erase_pending_list
+ * those blocks are counted in c->nr_erasing_blocks.
+ * If one block is actually erased, it is not longer counted as dirty_space
+ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+ * with c->nr_erasing_blocks * c->sector_size again.
+ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+ * This helps us to force gc and pick eventually a clean block to spread the load.
+ */
+ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
+
+ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
+ (dirty > c->nospc_dirty_size))
+ ret = 1;
+
+ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
+ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+
+ return ret;
+}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: os-linux.h,v 1.37 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $
*
*/
#define kstatfs statfs
#endif
+struct kstatfs;
+struct kvec;
+
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
#endif
-/* Hmmm. P'raps generic code should only ever see versions of signal
- functions which do the locking automatically? */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) && !defined(__rh_config_h__)
-#define current_sig_lock current->sigmask_lock
-#else
-#define current_sig_lock current->sighand->siglock
-#endif
-
#define sleep_on_spinunlock(wq, s) \
do { \
DECLARE_WAITQUEUE(__wait, current); \
#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
-#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
-#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
+#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
#define jffs2_nand_flash_setup(c) (0)
#define jffs2_nand_flash_cleanup(c) do {} while(0)
#define jffs2_wbuf_dirty(c) (0)
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
-struct kstatfs;
-struct kvec;
/* wbuf.c */
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
void jffs2_wbuf_timeout(unsigned long data);
void jffs2_wbuf_process(void *data);
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
#endif /* NAND */
+/* erase.c */
+static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+{
+ OFNI_BS_2SFFJ(c)->s_dirt = 1;
+}
+
/* background.c */
int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
void jffs2_write_super (struct super_block *);
int jffs2_remount_fs (struct super_block *, int *, char *);
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f);
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+ int inum, int nlink);
+
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
+ struct jffs2_inode_info *f,
+ unsigned long offset,
+ unsigned long *priv);
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+ unsigned char *pg,
+ unsigned long *priv);
+int jffs2_flash_setup(struct jffs2_sb_info *c);
+void jffs2_flash_cleanup(struct jffs2_sb_info *c);
+
/* writev.c */
int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen);
-/* super.c */
-
#endif /* __JFFS2_OS_LINUX_H__ */
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $
*
*/
#include <linux/mtd/mtd.h>
#include <linux/compiler.h>
#include "nodelist.h"
+#include "compr.h"
-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+ struct jffs2_full_dnode *fd, unsigned char *buf,
+ int ofs, int len)
{
struct jffs2_raw_inode *ri;
size_t readlen;
if (ri->compr != JFFS2_COMPR_NONE) {
D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
- ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+ ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
if (ret) {
printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
goto out_decomprbuf;
D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
frag->ofs+fragofs, frag->ofs+fragofs+readlen,
ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
- ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+ ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
D2(printk(KERN_DEBUG "node read done\n"));
if (ret) {
D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
}
buf[f->metadata->size]=0;
- ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
+ ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
up(&f->sem);
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: readinode.c,v 1.107 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $
*
*/
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
}
}
+
+static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
+{
+ struct jffs2_node_frag *frag;
+ int bitched = 0;
+
+ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+
+ struct jffs2_full_dnode *fn = frag->node;
+ if (!fn || !fn->raw)
+ continue;
+
+ if (ref_flags(fn->raw) == REF_PRISTINE) {
+
+ if (fn->frags > 1) {
+ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
+ bitched = 1;
+ }
+ /* A hole node which isn't multi-page should be garbage-collected
+ and merged anyway, so we just check for the frag size here,
+ rather than mucking around with actually reading the node
+ and checking the compression type, which is the real way
+ to tell a hole node. */
+ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
+ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
+ ref_offset(fn->raw));
+ bitched = 1;
+ }
+
+ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
+ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
+ ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
+ bitched = 1;
+ }
+ }
+ }
+
+ if (bitched) {
+ struct jffs2_node_frag *thisfrag;
+
+ printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
+ thisfrag = frag_first(&f->fragtree);
+ while (thisfrag) {
+ if (!thisfrag->node) {
+ printk("Frag @0x%x-0x%x; node-less hole\n",
+ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+ } else if (!thisfrag->node->raw) {
+ printk("Frag @0x%x-0x%x; raw-less hole\n",
+ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+ } else {
+ printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
+ thisfrag->ofs, thisfrag->size + thisfrag->ofs,
+ ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
+ thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
+ }
+ thisfrag = frag_next(thisfrag);
+ }
+ }
+ return bitched;
+}
#endif /* D1 */
static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
mark_ref_normal(next->node->raw);
}
}
+ D2(if (jffs2_sanitycheck_fragtree(f)) {
+ printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
+ return 0;
+ })
D2(jffs2_print_frag_list(f));
return 0;
}
}
}
spin_unlock(&c->inocache_lock);
+
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
fn = tn->fn;
if (f->metadata) {
- if (tn->version > mdata_ver) {
+ if (likely(tn->version >= mdata_ver)) {
D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
jffs2_mark_node_obsolete(c, f->metadata->raw);
jffs2_free_full_dnode(f->metadata);
mdata_ver = 0;
} else {
- D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n",
- ref_offset(f->metadata->raw), tn->version, mdata_ver));
+ /* This should never happen. */
+ printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
+ ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
jffs2_mark_node_obsolete(c, fn->raw);
jffs2_free_full_dnode(fn);
+ /* Fill in latest_node from the metadata, not this one we're about to free... */
+ fn = f->metadata;
goto next_tn;
}
}
tn_list = tn->next;
jffs2_free_tmp_dnode_info(tn);
}
+ D1(jffs2_sanitycheck_fragtree(f));
+
if (!fn) {
/* No data nodes for this inode. */
if (f->inocache->ino != 1) {
void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
struct jffs2_full_dirent *fd, *fds;
- /* I don't think we care about the potential race due to reading this
- without f->sem. It can never get undeleted. */
- int deleted = f->inocache && !f->inocache->nlink;
-
- /* If it's a deleted inode, grab the alloc_sem. This prevents
- jffs2_garbage_collect_pass() from deciding that it wants to
- garbage collect one of the nodes we're just about to mark
- obsolete -- by the time we drop alloc_sem and return, all
- the nodes are marked obsolete, and jffs2_g_c_pass() won't
- call iget() for the inode in question.
-
- We also used to do this to keep the temporary BUG() in
- jffs2_mark_node_obsolete() from triggering.
- */
- if(deleted)
- down(&c->alloc_sem);
+ int deleted;
down(&f->sem);
+ deleted = f->inocache && !f->inocache->nlink;
if (f->metadata) {
if (deleted)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
up(&f->sem);
-
- if(deleted)
- up(&c->alloc_sem);
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: scan.c,v 1.104 2003/10/11 14:52:48 dwmw2 Exp $
+ * $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $
*
*/
#include <linux/kernel.h>
uint32_t hdr_crc, buf_ofs, buf_len;
int err;
int noise = 0;
- int wasempty = 0;
- uint32_t empty_start = 0;
#ifdef CONFIG_JFFS2_FS_NAND
int cleanmarkerfound = 0;
#endif
switch (ret) {
case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
case 1: return BLK_STATE_ALLDIRTY;
- case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
- case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret;
}
}
noise = 10;
+scan_more:
while(ofs < jeb->offset + c->sector_size) {
D1(ACCT_PARANOIA_CHECK(jeb));
node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
- uint32_t inbuf_ofs = ofs - buf_ofs + 4;
- uint32_t scanend;
+ uint32_t inbuf_ofs;
+ uint32_t empty_start;
empty_start = ofs;
ofs += 4;
- /* If scanning empty space after only a cleanmarker, don't
- bother scanning the whole block */
- if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
- jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
- scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
- else
- scanend = buf_len;
-
D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
- while (inbuf_ofs < scanend) {
- if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
- goto emptyends;
+ more_empty:
+ inbuf_ofs = ofs - buf_ofs;
+ while (inbuf_ofs < buf_len) {
+ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
+ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
+ empty_start, ofs);
+ DIRTY_SPACE(ofs-empty_start);
+ goto scan_more;
+ }
inbuf_ofs+=4;
ofs += 4;
}
/* Ran off end. */
- D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
+ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
- if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
- c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size)
+ /* If we're only checking the beginning of a block with a cleanmarker,
+ bail now */
+ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
+ c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
+ D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
return BLK_STATE_CLEANMARKER;
- wasempty = 1;
- continue;
- } else if (wasempty) {
- emptyends:
- printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
- DIRTY_SPACE(ofs-empty_start);
- wasempty = 0;
- continue;
+ }
+
+ /* See how much more there is to read in this eraseblock... */
+ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+ if (!buf_len) {
+ /* No more to read. Break out of main loop without marking
+ this range of empty space as dirty (because it's not) */
+ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
+ empty_start));
+ break;
+ }
+ D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
+ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+ if (err)
+ return err;
+ buf_ofs = ofs;
+ goto more_empty;
}
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
marker_ref->next_in_ino = NULL;
marker_ref->next_phys = NULL;
marker_ref->flash_offset = ofs | REF_NORMAL;
- marker_ref->totlen = c->cleanmarker_size;
+ marker_ref->__totlen = c->cleanmarker_size;
jeb->first_node = jeb->last_node = marker_ref;
USED_SPACE(PAD(c->cleanmarker_size));
}
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
- && (!jeb->first_node || jeb->first_node->next_in_ino) )
+ && (!jeb->first_node || !jeb->first_node->next_in_ino) )
return BLK_STATE_CLEANMARKER;
/* move blocks with max 4 byte dirty space to cleanlist */
if (ic)
return ic;
+ if (ino > c->highest_ino)
+ c->highest_ino = ino;
+
ic = jffs2_alloc_inode_cache();
if (!ic) {
printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
ic->nodes = (void *)ic;
jffs2_add_ino_cache(c, ic);
if (ino == 1)
- ic->nlink=1;
+ ic->nlink = 1;
return ic;
}
/* Wheee. It worked */
raw->flash_offset = ofs | REF_UNCHECKED;
- raw->totlen = PAD(je32_to_cpu(ri->totlen));
+ raw->__totlen = PAD(je32_to_cpu(ri->totlen));
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
return -ENOMEM;
}
- raw->totlen = PAD(je32_to_cpu(rd->totlen));
+ raw->__totlen = PAD(je32_to_cpu(rd->totlen));
raw->flash_offset = ofs | REF_PRISTINE;
raw->next_phys = NULL;
raw->next_in_ino = ic->nodes;
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: super.c,v 1.96 2004/07/13 08:57:30 dwmw2 Exp $
*
*/
#include <linux/mtd/mtd.h>
#include <linux/ctype.h>
#include <linux/namei.h>
+#include "compr.h"
#include "nodelist.h"
static void jffs2_put_super(struct super_block *);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
kfree(c->blocks);
- jffs2_nand_flash_cleanup(c);
+ jffs2_flash_cleanup(c);
kfree(c->inocache_list);
if (c->mtd->sync)
c->mtd->sync(c->mtd);
int ret;
printk(KERN_INFO "JFFS2 version 2.2."
-#ifdef CONFIG_FS_JFFS2_NAND
+#ifdef CONFIG_JFFS2_FS_NAND
" (NAND)"
#endif
" (C) 2001-2003 Red Hat, Inc.\n");
printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
return -ENOMEM;
}
- ret = jffs2_zlib_init();
+#ifdef CONFIG_JFFS2_PROC
+ ret = jffs2_proc_init();
if (ret) {
- printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+ printk(KERN_ERR "JFFS2 error: Failed to initialise proc interface\n");
+ goto out;
+ }
+#endif
+ ret = jffs2_compressors_init();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
goto out;
}
ret = jffs2_create_slab_caches();
if (ret) {
printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
- goto out_zlib;
+ goto out_compressors;
}
ret = register_filesystem(&jffs2_fs_type);
if (ret) {
out_slab:
jffs2_destroy_slab_caches();
- out_zlib:
- jffs2_zlib_exit();
+ out_compressors:
+ jffs2_compressors_exit();
+#ifdef CONFIG_JFFS2_PROC
+ jffs2_proc_exit();
+#endif
out:
return ret;
}
{
unregister_filesystem(&jffs2_fs_type);
jffs2_destroy_slab_caches();
- jffs2_zlib_exit();
+ jffs2_compressors_exit();
+#ifdef CONFIG_JFFS2_PROC
+ jffs2_proc_exit();
+#endif
kmem_cache_destroy(jffs2_inode_cachep);
}
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $
+ * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $
*
*/
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
*
* Created by David Woodhouse <dwmw2@redhat.com>
+ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: wbuf.c,v 1.53 2003/10/11 11:46:09 dwmw2 Exp $
+ * $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
#endif
/* max. erase failures before we mark a block bad */
-#define MAX_ERASE_FAILURES 5
+#define MAX_ERASE_FAILURES 2
/* two seconds timeout for timed wbuf-flushing */
#define WBUF_FLUSH_TIMEOUT 2 * HZ
first_raw = &jeb->first_node;
while (*first_raw &&
(ref_obsolete(*first_raw) ||
- (ref_offset(*first_raw) + (*first_raw)->totlen) < c->wbuf_ofs)) {
+ (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
ref_offset(*first_raw), ref_flags(*first_raw),
- (ref_offset(*first_raw) + (*first_raw)->totlen),
+ (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
c->wbuf_ofs));
first_raw = &(*first_raw)->next_phys;
}
}
start = ref_offset(*first_raw);
- end = ref_offset(*first_raw) + (*first_raw)->totlen;
+ end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
/* Find the last node to be recovered */
raw = first_raw;
while ((*raw)) {
if (!ref_obsolete(*raw))
- end = ref_offset(*raw) + (*raw)->totlen;
+ end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
raw = &(*raw)->next_phys;
}
return;
raw2->flash_offset = ofs | REF_OBSOLETE;
- raw2->totlen = (*first_raw)->totlen;
+ raw2->__totlen = ref_totlen(c, jeb, *first_raw);
raw2->next_phys = NULL;
raw2->next_in_ino = NULL;
raw = first_raw;
while (*raw) {
+ uint32_t rawlen = ref_totlen(c, jeb, *raw);
+
D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
- (*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs));
+ rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
if (ref_obsolete(*raw)) {
/* Shouldn't really happen much */
- new_jeb->dirty_size += (*raw)->totlen;
- new_jeb->free_size -= (*raw)->totlen;
- c->dirty_size += (*raw)->totlen;
+ new_jeb->dirty_size += rawlen;
+ new_jeb->free_size -= rawlen;
+ c->dirty_size += rawlen;
} else {
- new_jeb->used_size += (*raw)->totlen;
- new_jeb->free_size -= (*raw)->totlen;
- jeb->dirty_size += (*raw)->totlen;
- jeb->used_size -= (*raw)->totlen;
- c->dirty_size += (*raw)->totlen;
+ new_jeb->used_size += rawlen;
+ new_jeb->free_size -= rawlen;
+ jeb->dirty_size += rawlen;
+ jeb->used_size -= rawlen;
+ c->dirty_size += rawlen;
}
- c->free_size -= (*raw)->totlen;
+ c->free_size -= rawlen;
(*raw)->flash_offset = ofs | ref_flags(*raw);
- ofs += (*raw)->totlen;
+ ofs += rawlen;
new_jeb->last_node = *raw;
raw = &(*raw)->next_phys;
padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
+ } else {
+ /* Pad with JFFS2_DIRTY_BITMASK */
+ memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
}
}
/* else jffs2_flash_writev has actually filled in the rest of the
return ret;
}
- /* Adjusting free size of next block only, if it's called from fsync ! */
- if (pad == 2) {
- D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
- spin_lock(&c->erase_completion_lock);
- if (!c->nextblock)
- BUG();
+ spin_lock(&c->erase_completion_lock);
+
+ /* Adjust free size of the block if we padded. */
+ if (pad) {
+ struct jffs2_eraseblock *jeb;
+
+ jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
+
+ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
+ (jeb==c->nextblock)?"next":"", jeb->offset));
+
/* wbuf_pagesize - wbuf_len is the amount of space that's to be
padded. If there is less free space in the block than that,
something screwed up */
- if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
+ if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
- c->nextblock->offset, c->nextblock->free_size);
+ jeb->offset, jeb->free_size);
BUG();
}
- c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
+ jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
- c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
+ jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
- spin_unlock(&c->erase_completion_lock);
}
/* Stick any now-obsoleted blocks on the erase_pending_list */
- spin_lock(&c->erase_completion_lock);
jffs2_refile_wbuf_blocks(c);
jffs2_clear_wbuf_ino_list(c);
spin_unlock(&c->erase_completion_lock);
old_wbuf_ofs = c->wbuf_ofs;
old_wbuf_len = c->wbuf_len;
- while (old_wbuf_len &&
- old_wbuf_ofs == c->wbuf_ofs) {
+ if (c->unchecked_size) {
+ /* GC won't make any progress for a while */
+ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
+ ret = __jffs2_flush_wbuf(c, 2);
+ } else while (old_wbuf_len &&
+ old_wbuf_ofs == c->wbuf_ofs) {
up(&c->alloc_sem);
size_t retlen;
int oob_size;
- oob_size = c->mtd->oobsize;
-
/* allocate a buffer for all oob data in this sector */
+ oob_size = c->mtd->oobsize;
len = 4 * oob_size;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
goto out;
}
- /* Special check for first two pages */
- for (page = 0; page < 2 * oob_size; page += oob_size) {
- /* Check for bad block marker */
- if (buf[page+c->badblock_pos] != 0xff) {
- D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
- /* Return 2 for bad and 3 for failed block
- bad goes to list_bad and failed to list_erase */
- ret = (!page) ? 2 : 3;
+ /* Special check for first page */
+ for(i = 0; i < oob_size ; i++) {
+ /* Yeah, we know about the cleanmarker. */
+ if (mode && i >= c->fsdata_pos &&
+ i < c->fsdata_pos + c->fsdata_len)
+ continue;
+
+ if (buf[i] != 0xFF) {
+ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+ buf[page+i], page+i, jeb->offset));
+ ret = 1;
goto out;
}
- for(i = 0; i < oob_size ; i++) {
- /* Yeah, we know about the cleanmarker. */
- if (mode && i >= c->fsdata_pos &&
- i < c->fsdata_pos+c->fsdata_len)
- continue;
-
- if (buf[page+i] != 0xFF) {
- D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
- buf[page+i], page+i, jeb->offset));
- ret = 1;
- goto out;
- }
- }
- /* only the first page can contain a cleanmarker !*/
- mode = 0;
- }
+ }
/* we know, we are aligned :) */
- for (; page < len; page += sizeof(long)) {
+ for (page = oob_size; page < len; page += sizeof(long)) {
unsigned long dat = *(unsigned long *)(&buf[page]);
if(dat != -1) {
ret = 1;
int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_unknown_node n;
- unsigned char buf[32];
+ unsigned char buf[2 * NAND_MAX_OOBSIZE];
unsigned char *p;
int ret, i, cnt, retval = 0;
size_t retlen, offset;
/* Loop through the physical blocks */
for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
+ /* Check first if the block is bad. */
+ if (c->mtd->block_isbad (c->mtd, offset)) {
+ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
+ return 2;
+ }
/*
* We read oob data from page 0 and 1 of the block.
* page 0 contains cleanmarker and badblock info
return -EIO;
}
- /* Check for bad block marker */
- if (buf[c->badblock_pos] != 0xff) {
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
- jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos));
- return 2;
- }
-
- /* Check for failure counter in the second page */
- if (buf[c->badblock_pos + oob_size] != 0xff) {
- D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
- return 3;
- }
-
/* Check cleanmarker only on the first physical block */
if (!cnt) {
n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
}
/*
- * We try to get the failure count of this block.
- */
-int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
-
- unsigned char buf[16];
- int ret;
- size_t retlen;
- int oob_size;
-
- oob_size = c->mtd->oobsize;
-
- ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
-
- if (ret) {
- D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
- return ret;
- }
-
- if (retlen < oob_size) {
- D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
- return -EIO;
- }
-
- jeb->bad_count = buf[c->badblock_pos];
- return 0;
-}
-
-/*
- * On NAND we try to mark this block bad. We try to write how often
- * the block was erased and mark it finaly bad, if the count
- * is > MAX_ERASE_FAILURES. We read this information on mount !
- * jeb->bad_count contains the count before this erase.
+ * On NAND we try to mark this block bad. If the block was erased more
+ * than MAX_ERASE_FAILURES we mark it finaly bad.
* Don't care about failures. This block remains on the erase-pending
* or badblock list as long as nobody manipulates the flash with
* a bootloader or something like that.
*/
-int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
{
- unsigned char buf = 0x0;
int ret;
- size_t retlen;
/* if the count is < max, we try to write the counter to the 2nd page oob area */
- if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
- buf = (unsigned char)jeb->bad_count;
- c->badblock_pos += c->mtd->oobblock;
- }
-
- ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
+ if( ++jeb->bad_count < MAX_ERASE_FAILURES)
+ return 0;
+
+ if (!c->mtd->block_markbad)
+ return 1; // What else can we do?
+
+ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
+ ret = c->mtd->block_markbad(c->mtd, bad_offset);
if (ret) {
D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
return ret;
}
- if (retlen != 1) {
- D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
- return ret;
- }
- return 0;
+ return 1;
}
-#define JFFS2_OOB_ECCPOS0 0
-#define JFFS2_OOB_ECCPOS1 1
-#define JFFS2_OOB_ECCPOS2 2
-#define JFFS2_OOB_ECCPOS3 3
-#define JFFS2_OOB_ECCPOS4 6
-#define JFFS2_OOB_ECCPOS5 7
-
-#define NAND_JFFS2_OOB8_FSDAPOS 6
-#define NAND_JFFS2_OOB16_FSDAPOS 8
-#define NAND_JFFS2_OOB8_FSDALEN 2
#define NAND_JFFS2_OOB16_FSDALEN 8
-static struct nand_oobinfo jffs2_oobinfo_swecc = {
- .useecc = 1,
- .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
- JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
-};
-
static struct nand_oobinfo jffs2_oobinfo_docecc = {
- .useecc = 1,
+ .useecc = MTD_NANDECC_PLACE,
+ .eccbytes = 6,
.eccpos = {0,1,2,3,4,5}
};
-
-int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
+int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
{
+ struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
+
+ /* Do this only, if we have an oob buffer */
+ if (!c->mtd->oobsize)
+ return 0;
+
/* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_size = 0;
- /* Initialise write buffer */
- c->wbuf_pagesize = c->mtd->oobblock;
- c->wbuf_ofs = 0xFFFFFFFF;
-
- /* FIXME: If we had a generic way of describing the hardware's
- use of OOB area, we could perhaps make this generic too. */
- switch(c->mtd->ecctype) {
- case MTD_ECC_SW:
- D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
- c->oobinfo = &jffs2_oobinfo_swecc;
- if (c->mtd->oobsize == 8) {
- c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
- c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
- } else {
- c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
+ /* Should we use autoplacement ? */
+ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
+ D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
+ /* Get the position of the free bytes */
+ if (!oinfo->oobfree[0][0]) {
+ printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
+ return -ENOSPC;
+ }
+ c->fsdata_pos = oinfo->oobfree[0][0];
+ c->fsdata_len = oinfo->oobfree[0][1];
+ if (c->fsdata_len > 8)
+ c->fsdata_len = 8;
+ } else {
+ /* This is just a legacy fallback and should go away soon */
+ switch(c->mtd->ecctype) {
+ case MTD_ECC_RS_DiskOnChip:
+ printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
+ c->oobinfo = &jffs2_oobinfo_docecc;
+ c->fsdata_pos = 6;
c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
+ c->badblock_pos = 15;
+ break;
+
+ default:
+ D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
+ return -EINVAL;
}
- c->badblock_pos = NAND_BADBLOCK_POS;
- break;
-
- case MTD_ECC_RS_DiskOnChip:
- D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
- c->oobinfo = &jffs2_oobinfo_docecc;
- c->fsdata_pos = 6;
- c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
- c->badblock_pos = 15;
- break;
-
- default:
- printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
- c->mtd->ecctype);
- return -EINVAL;
}
+ return 0;
+}
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
+{
+ int res;
+
+ /* Initialise write buffer */
+ c->wbuf_pagesize = c->mtd->oobblock;
+ c->wbuf_ofs = 0xFFFFFFFF;
+
+
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
+ res = jffs2_nand_set_oobinfo(c);
+
#ifdef BREAKME
if (!brokenbuf)
brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
}
memset(brokenbuf, 0xdb, c->wbuf_pagesize);
#endif
- return 0;
+ return res;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: write.c,v 1.75 2003/10/08 11:45:11 dwmw2 Exp $
+ * $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
#include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
+#include "compr.h"
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
memset(ic, 0, sizeof(*ic));
- init_MUTEX_LOCKED(&f->sem);
f->inocache = ic;
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
fn->raw = raw;
raw->flash_offset = flash_ofs;
- raw->totlen = PAD(sizeof(*ri)+datalen);
+ raw->__totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
fd->raw = raw;
raw->flash_offset = flash_ofs;
- raw->totlen = PAD(sizeof(*rd)+namelen);
+ raw->__totlen = PAD(sizeof(*rd)+namelen);
raw->next_phys = NULL;
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
- (alloc_mode==ALLOC_GC)?0:fd->ino);
+ (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
if (ret || (retlen != sizeof(*rd) + namelen)) {
printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
sizeof(*rd)+namelen, flash_ofs, ret, retlen);
while(writelen) {
struct jffs2_full_dnode *fn;
unsigned char *comprbuf = NULL;
- unsigned char comprtype = JFFS2_COMPR_NONE;
+ uint16_t comprtype = JFFS2_COMPR_NONE;
uint32_t phys_ofs, alloclen;
uint32_t datalen, cdatalen;
int retried = 0;
break;
}
down(&f->sem);
- datalen = writelen;
- cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
+ datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
+ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
- comprbuf = kmalloc(cdatalen, GFP_KERNEL);
- if (comprbuf) {
- comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen);
- }
- if (comprtype == JFFS2_COMPR_NONE) {
- /* Either compression failed, or the allocation of comprbuf failed */
- if (comprbuf)
- kfree(comprbuf);
- comprbuf = buf;
- datalen = cdatalen;
- }
- /* Now comprbuf points to the data to be written, be it compressed or not.
- comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
- that the comprbuf doesn't need to be kfree()d.
- */
+ comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
ri->offset = cpu_to_je32(offset);
ri->csize = cpu_to_je32(cdatalen);
ri->dsize = cpu_to_je32(datalen);
- ri->compr = comprtype;
+ ri->compr = comprtype & 0xff;
+ ri->usercompr = (comprtype >> 8 ) & 0xff;
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
- if (comprtype != JFFS2_COMPR_NONE)
- kfree(comprbuf);
+ jffs2_free_comprbuf(comprbuf, buf);
if (IS_ERR(fn)) {
ret = PTR_ERR(fn);
uint32_t alloclen, phys_ofs;
int ret;
- rd = jffs2_alloc_raw_dirent();
- if (!rd)
- return -ENOMEM;
+ if (1 /* alternative branch needs testing */ ||
+ !jffs2_can_mark_obsolete(c)) {
+ /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
- ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
- if (ret) {
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd)
+ return -ENOMEM;
+
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
+ if (ret) {
+ jffs2_free_raw_dirent(rd);
+ return ret;
+ }
+
+ down(&dir_f->sem);
+
+ /* Build a deletion node */
+ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+ rd->pino = cpu_to_je32(dir_f->inocache->ino);
+ rd->version = cpu_to_je32(++dir_f->highest_version);
+ rd->ino = cpu_to_je32(0);
+ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+ rd->type = DT_UNKNOWN;
+ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
+
+ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
+
jffs2_free_raw_dirent(rd);
- return ret;
- }
- down(&dir_f->sem);
+ if (IS_ERR(fd)) {
+ jffs2_complete_reservation(c);
+ up(&dir_f->sem);
+ return PTR_ERR(fd);
+ }
- /* Build a deletion node */
- rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
- rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
- rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
- rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+ /* File it. This will mark the old one obsolete. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+ } else {
+ struct jffs2_full_dirent **prev = &dir_f->dents;
+ uint32_t nhash = full_name_hash(name, namelen);
- rd->pino = cpu_to_je32(dir_f->inocache->ino);
- rd->version = cpu_to_je32(++dir_f->highest_version);
- rd->ino = cpu_to_je32(0);
- rd->mctime = cpu_to_je32(get_seconds());
- rd->nsize = namelen;
- rd->type = DT_UNKNOWN;
- rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
- rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
+ down(&dir_f->sem);
- fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
-
- jffs2_free_raw_dirent(rd);
+ while ((*prev) && (*prev)->nhash <= nhash) {
+ if ((*prev)->nhash == nhash &&
+ !memcmp((*prev)->name, name, namelen) &&
+ !(*prev)->name[namelen]) {
+ struct jffs2_full_dirent *this = *prev;
- if (IS_ERR(fd)) {
- jffs2_complete_reservation(c);
+ D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
+ this->ino, ref_offset(this->raw)));
+
+ *prev = this->next;
+ jffs2_mark_node_obsolete(c, (this->raw));
+ jffs2_free_full_dirent(this);
+ break;
+ }
+ prev = &((*prev)->next);
+ }
up(&dir_f->sem);
- return PTR_ERR(fd);
}
- /* File it. This will mark the old one obsolete. */
- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-
- up(&dir_f->sem);
-
/* dead_f is NULL if this was a rename not a real unlink */
/* Also catch the !f->inocache case, where there was a dirent
pointing to an inode which didn't exist. */
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $
+ * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $
*
*/
#define OCP_VENDOR_ARM 0x0004
#define OCP_VENDOR_FREESCALE 0x1057
#define OCP_VENDOR_IBM 0x1014
+#define OCP_VENDOR_MARVELL 0x11ab
#define OCP_VENDOR_MOTOROLA OCP_VENDOR_FREESCALE
#define OCP_VENDOR_XILINX 0x10ee
#define OCP_VENDOR_UNKNOWN 0xFFFF
#define OCP_FUNC_16550 0x0031
#define OCP_FUNC_IIC 0x0032
#define OCP_FUNC_USB 0x0033
+#define OCP_FUNC_MPSC 0x0034
+#define OCP_FUNC_COMM_MPSC 0x0035
+#define OCP_FUNC_SDMA 0x0036
/* Memory devices 0x0090 - 0x009F */
#define OCP_FUNC_MAL 0x0090
#define OCP_FUNC_PERFMON 0x00D2 /* Performance Monitor */
#define OCP_FUNC_RGMII 0x00D3
#define OCP_FUNC_TAH 0x00D4
+#define OCP_FUNC_I2C 0x00D5 /* I2C Controller */
+#define OCP_FUNC_BRG 0x00D6 /* Baud Rate Generator */
+#define OCP_FUNC_PIC 0x00D7 /* Programmable Interrupt Controller */
/* Network 0x0200 - 0x02FF */
#define OCP_FUNC_EMAC 0x0200
-#define OCP_FUNC_GFAR 0x0201 /* TSEC & FEC */
+#define OCP_FUNC_ENET 0x0201 /* TSEC & FEC */
+#define OCP_FUNC_COMM_EMAC 0x0202
+#define OCP_FUNC_GFAR 0x0203 /* TSEC & FEC */
/* Bridge devices 0xE00 - 0xEFF */
#define OCP_FUNC_OPB 0x0E00
+#define OCP_FUNC_HB 0x0E01 /* Host bridge */
#define OCP_FUNC_UNKNOWN 0xFFFF
#if defined(CONFIG_EV64260)
#include <platforms/ev64260.h>
+#elif defined(CONFIG_DMV182)
+#include <platforms/dmv182_serial.h>
#elif defined(CONFIG_GEMINI)
#include <platforms/gemini_serial.h>
#elif defined(CONFIG_POWERPMC250)
[0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \
} })
+#define CPU_MASK_CPU0 \
+((cpumask_t) { { \
+ [0] = 1UL \
+} })
+
#define cpus_addr(src) ((src).bits)
#define cpumask_scnprintf(buf, len, src) \
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
- * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $
+ * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $
*
*/
#define JFFS2_COMPR_COPY 0x04
#define JFFS2_COMPR_DYNRUBIN 0x05
#define JFFS2_COMPR_ZLIB 0x06
+#define JFFS2_COMPR_LZO 0x07
+#define JFFS2_COMPR_LZARI 0x08
/* Compatibility flags. */
#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
#define JFFS2_NODE_ACCURATE 0x2000
uint16_t v16;
} __attribute__((packed)) jint16_t;
-#define JFFS2_NATIVE_ENDIAN
-
-/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
- whatever OS we're actually running on here too. */
-
-#if defined(JFFS2_NATIVE_ENDIAN)
-#define cpu_to_je16(x) ((jint16_t){x})
-#define cpu_to_je32(x) ((jint32_t){x})
-#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
-
-#define je16_to_cpu(x) ((x).v16)
-#define je32_to_cpu(x) ((x).v32)
-#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
-#elif defined(JFFS2_BIG_ENDIAN)
-#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
-#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
-#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
-
-#define je16_to_cpu(x) (be16_to_cpu(x.v16))
-#define je32_to_cpu(x) (be32_to_cpu(x.v32))
-#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
-#elif defined(JFFS2_LITTLE_ENDIAN)
-#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
-#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
-#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
-
-#define je16_to_cpu(x) (le16_to_cpu(x.v16))
-#define je32_to_cpu(x) (le32_to_cpu(x.v32))
-#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
-#else
-#error wibble
-#endif
-
struct jffs2_unknown_node
{
/* All start like this */
-/* $Id: jffs2_fs_i.h,v 1.15 2002/11/12 09:42:49 dwmw2 Exp $ */
+/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */
#ifndef _JFFS2_FS_I
#define _JFFS2_FS_I
uint16_t flags;
uint8_t usercompr;
+#if !defined (__ECOS)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
struct inode vfs_inode;
#endif
+#endif
};
#endif /* _JFFS2_FS_I */
/* defines only for the constants which don't work well as enums */
#define ATA_TAG_POISON 0xfafbfcfdU
+#undef PORT_UNKNOWN
enum {
/* various global constants */
LIBATA_MAX_PRD = ATA_MAX_PRD / 2,
/* Common Flash Interface structures
* See http://support.intel.com/design/flash/technote/index.htm
- * $Id: cfi.h,v 1.35 2003/05/28 15:37:32 dwmw2 Exp $
+ * $Id: cfi.h,v 1.44 2004/07/13 22:32:52 dwmw2 Exp $
*/
#ifndef __MTD_CFI_H__
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/mtd/flashchip.h>
+#include <linux/mtd/map.h>
#include <linux/mtd/cfi_endian.h>
-/*
- * You can optimize the code size and performance by defining only
- * the geometry(ies) available on your hardware.
- * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width)
- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
- *
- * By default, all (known) geometries are supported.
- */
-
-#ifndef CONFIG_MTD_CFI_GEOMETRY
-
-/* The default case - support all but 64-bit, which has
- a performance penalty */
-
-#define CFIDEV_INTERLEAVE_1 (1)
-#define CFIDEV_INTERLEAVE_2 (2)
-#define CFIDEV_INTERLEAVE_4 (4)
-
-#define CFIDEV_BUSWIDTH_1 (1)
-#define CFIDEV_BUSWIDTH_2 (2)
-#define CFIDEV_BUSWIDTH_4 (4)
-
-typedef __u32 cfi_word;
-
-#else
-
-/* Explicitly configured buswidth/interleave support */
-
#ifdef CONFIG_MTD_CFI_I1
-#define CFIDEV_INTERLEAVE_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_I2
-#define CFIDEV_INTERLEAVE_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_I4
-#define CFIDEV_INTERLEAVE_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_I8
-#define CFIDEV_INTERLEAVE_8 (8)
-#endif
-
-#ifdef CONFIG_MTD_CFI_B1
-#define CFIDEV_BUSWIDTH_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_B2
-#define CFIDEV_BUSWIDTH_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_B4
-#define CFIDEV_BUSWIDTH_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_B8
-#define CFIDEV_BUSWIDTH_8 (8)
-#endif
-
-/* pick the largest necessary */
-#ifdef CONFIG_MTD_CFI_B8
-typedef __u64 cfi_word;
-
-/* This only works if asm/io.h is included first */
-#ifndef __raw_readll
-#define __raw_readll(addr) (*(volatile __u64 *)(addr))
-#endif
-#ifndef __raw_writell
-#define __raw_writell(v, addr) (*(volatile __u64 *)(addr) = (v))
-#endif
-#define CFI_WORD_64
-#else /* CONFIG_MTD_CFI_B8 */
-/* All others can use 32-bits. It's probably more efficient than
- the smaller types anyway */
-typedef __u32 cfi_word;
-#endif /* CONFIG_MTD_CFI_B8 */
-
-#endif
-
-/*
- * The following macros are used to select the code to execute:
- * cfi_buswidth_is_*()
- * cfi_interleave_is_*()
- * [where * is either 1, 2, 4, or 8]
- * Those macros should be used with 'if' statements. If only one of few
- * geometry arrangements are selected, they expand to constants thus allowing
- * the compiler (most of them being 0) to optimize away all the unneeded code,
- * while still validating the syntax (which is not possible with embedded
- * #if ... #endif constructs).
- * The exception to this is the 64-bit versions, which need an extension
- * to the cfi_word type, and cause compiler warnings about shifts being
- * out of range.
- */
-
-#ifdef CFIDEV_INTERLEAVE_1
-# ifdef CFIDEV_INTERLEAVE
-# undef CFIDEV_INTERLEAVE
-# define CFIDEV_INTERLEAVE (cfi->interleave)
-# else
-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
-# endif
-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
+#define cfi_interleave(cfi) 1
+#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
#else
-# define cfi_interleave_is_1() (0)
+#define cfi_interleave_is_1(cfi) (0)
#endif
-#ifdef CFIDEV_INTERLEAVE_2
-# ifdef CFIDEV_INTERLEAVE
-# undef CFIDEV_INTERLEAVE
-# define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I2
+# ifdef cfi_interleave
+# undef cfi_interleave
+# define cfi_interleave(cfi) ((cfi)->interleave)
# else
-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
+# define cfi_interleave(cfi) 2
# endif
-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
+#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
#else
-# define cfi_interleave_is_2() (0)
+#define cfi_interleave_is_2(cfi) (0)
#endif
-#ifdef CFIDEV_INTERLEAVE_4
-# ifdef CFIDEV_INTERLEAVE
-# undef CFIDEV_INTERLEAVE
-# define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I4
+# ifdef cfi_interleave
+# undef cfi_interleave
+# define cfi_interleave(cfi) ((cfi)->interleave)
# else
-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
+# define cfi_interleave(cfi) 4
# endif
-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
+#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
#else
-# define cfi_interleave_is_4() (0)
+#define cfi_interleave_is_4(cfi) (0)
#endif
-#ifdef CFIDEV_INTERLEAVE_8
-# ifdef CFIDEV_INTERLEAVE
-# undef CFIDEV_INTERLEAVE
-# define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I8
+# ifdef cfi_interleave
+# undef cfi_interleave
+# define cfi_interleave(cfi) ((cfi)->interleave)
# else
-# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
+# define cfi_interleave(cfi) 8
# endif
-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
+#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
#else
-# define cfi_interleave_is_8() (0)
+#define cfi_interleave_is_8(cfi) (0)
#endif
-#ifndef CFIDEV_INTERLEAVE
-#error You must define at least one interleave to support!
+static inline int cfi_interleave_supported(int i)
+{
+ switch (i) {
+#ifdef CONFIG_MTD_CFI_I1
+ case 1:
#endif
-
-#ifdef CFIDEV_BUSWIDTH_1
-# ifdef CFIDEV_BUSWIDTH
-# undef CFIDEV_BUSWIDTH
-# define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
-# endif
-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
-#else
-# define cfi_buswidth_is_1() (0)
+#ifdef CONFIG_MTD_CFI_I2
+ case 2:
#endif
-
-#ifdef CFIDEV_BUSWIDTH_2
-# ifdef CFIDEV_BUSWIDTH
-# undef CFIDEV_BUSWIDTH
-# define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
-# endif
-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
-#else
-# define cfi_buswidth_is_2() (0)
+#ifdef CONFIG_MTD_CFI_I4
+ case 4:
#endif
-
-#ifdef CFIDEV_BUSWIDTH_4
-# ifdef CFIDEV_BUSWIDTH
-# undef CFIDEV_BUSWIDTH
-# define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
-# endif
-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
-#else
-# define cfi_buswidth_is_4() (0)
+#ifdef CONFIG_MTD_CFI_I8
+ case 8:
#endif
+ return 1;
-#ifdef CFIDEV_BUSWIDTH_8
-# ifdef CFIDEV_BUSWIDTH
-# undef CFIDEV_BUSWIDTH
-# define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
-# endif
-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
-#else
-# define cfi_buswidth_is_8() (0)
-#endif
+ default:
+ return 0;
+ }
+}
-#ifndef CFIDEV_BUSWIDTH
-#error You must define at least one bus width to support!
-#endif
/* NB: these values must represents the number of bytes needed to meet the
* device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes.
/* Basic Query Structure */
struct cfi_ident {
- __u8 qry[3];
- __u16 P_ID;
- __u16 P_ADR;
- __u16 A_ID;
- __u16 A_ADR;
- __u8 VccMin;
- __u8 VccMax;
- __u8 VppMin;
- __u8 VppMax;
- __u8 WordWriteTimeoutTyp;
- __u8 BufWriteTimeoutTyp;
- __u8 BlockEraseTimeoutTyp;
- __u8 ChipEraseTimeoutTyp;
- __u8 WordWriteTimeoutMax;
- __u8 BufWriteTimeoutMax;
- __u8 BlockEraseTimeoutMax;
- __u8 ChipEraseTimeoutMax;
- __u8 DevSize;
- __u16 InterfaceDesc;
- __u16 MaxBufWriteSize;
- __u8 NumEraseRegions;
- __u32 EraseRegionInfo[0]; /* Not host ordered */
+ uint8_t qry[3];
+ uint16_t P_ID;
+ uint16_t P_ADR;
+ uint16_t A_ID;
+ uint16_t A_ADR;
+ uint8_t VccMin;
+ uint8_t VccMax;
+ uint8_t VppMin;
+ uint8_t VppMax;
+ uint8_t WordWriteTimeoutTyp;
+ uint8_t BufWriteTimeoutTyp;
+ uint8_t BlockEraseTimeoutTyp;
+ uint8_t ChipEraseTimeoutTyp;
+ uint8_t WordWriteTimeoutMax;
+ uint8_t BufWriteTimeoutMax;
+ uint8_t BlockEraseTimeoutMax;
+ uint8_t ChipEraseTimeoutMax;
+ uint8_t DevSize;
+ uint16_t InterfaceDesc;
+ uint16_t MaxBufWriteSize;
+ uint8_t NumEraseRegions;
+ uint32_t EraseRegionInfo[0]; /* Not host ordered */
} __attribute__((packed));
/* Extended Query Structure for both PRI and ALT */
struct cfi_extquery {
- __u8 pri[3];
- __u8 MajorVersion;
- __u8 MinorVersion;
+ uint8_t pri[3];
+ uint8_t MajorVersion;
+ uint8_t MinorVersion;
} __attribute__((packed));
/* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
struct cfi_pri_intelext {
- __u8 pri[3];
- __u8 MajorVersion;
- __u8 MinorVersion;
- __u32 FeatureSupport;
- __u8 SuspendCmdSupport;
- __u16 BlkStatusRegMask;
- __u8 VccOptimal;
- __u8 VppOptimal;
- __u8 NumProtectionFields;
- __u16 ProtRegAddr;
- __u8 FactProtRegSize;
- __u8 UserProtRegSize;
+ uint8_t pri[3];
+ uint8_t MajorVersion;
+ uint8_t MinorVersion;
+ uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
+ block follows - FIXME - not currently supported */
+ uint8_t SuspendCmdSupport;
+ uint16_t BlkStatusRegMask;
+ uint8_t VccOptimal;
+ uint8_t VppOptimal;
+ uint8_t NumProtectionFields;
+ uint16_t ProtRegAddr;
+ uint8_t FactProtRegSize;
+ uint8_t UserProtRegSize;
+} __attribute__((packed));
+
+/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
+
+struct cfi_pri_amdstd {
+ uint8_t pri[3];
+ uint8_t MajorVersion;
+ uint8_t MinorVersion;
+ uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
+ uint8_t EraseSuspend;
+ uint8_t BlkProt;
+ uint8_t TmpBlkUnprotect;
+ uint8_t BlkProtUnprot;
+ uint8_t SimultaneousOps;
+ uint8_t BurstMode;
+ uint8_t PageMode;
+ uint8_t VppMin;
+ uint8_t VppMax;
+ uint8_t TopBottom;
} __attribute__((packed));
struct cfi_pri_query {
- __u8 NumFields;
- __u32 ProtField[1]; /* Not host ordered */
+ uint8_t NumFields;
+ uint32_t ProtField[1]; /* Not host ordered */
} __attribute__((packed));
struct cfi_bri_query {
- __u8 PageModeReadCap;
- __u8 NumFields;
- __u32 ConfField[1]; /* Not host ordered */
+ uint8_t PageModeReadCap;
+ uint8_t NumFields;
+ uint32_t ConfField[1]; /* Not host ordered */
} __attribute__((packed));
#define P_ID_NONE 0
#define P_ID_AMD_STD 2
#define P_ID_INTEL_STD 3
#define P_ID_AMD_EXT 4
+#define P_ID_ST_ADV 32
#define P_ID_MITSUBISHI_STD 256
#define P_ID_MITSUBISHI_EXT 257
+#define P_ID_SST_PAGE 258
#define P_ID_RESERVED 65535
#define CFI_MODE_JEDEC 0
struct cfi_private {
- __u16 cmdset;
+ uint16_t cmdset;
void *cmdset_priv;
int interleave;
int device_type;
int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */
int addr_unlock1;
int addr_unlock2;
- int fast_prog;
struct mtd_info *(*cmdset_setup)(struct map_info *);
struct cfi_ident *cfiq; /* For now only one. We insist that all devs
must be of the same type. */
struct flchip chips[0]; /* per-chip data structure for each chip */
};
-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
-
/*
* Returns the command address according to the given geometry.
*/
-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
{
return (cmd_ofs * type) * interleave;
}
/*
- * Transforms the CFI command for the given geometry (bus width & interleave.
+ * Transforms the CFI command for the given geometry (bus width & interleave).
+ * It looks too long to be inline, but in the common case it should almost all
+ * get optimised away.
*/
-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
+static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
{
- cfi_word val = 0;
-
- if (cfi_buswidth_is_1()) {
- /* 1 x8 device */
- val = cmd;
- } else if (cfi_buswidth_is_2()) {
- if (cfi_interleave_is_1()) {
- /* 1 x16 device in x16 mode */
- val = cpu_to_cfi16(cmd);
- } else if (cfi_interleave_is_2()) {
- /* 2 (x8, x16 or x32) devices in x8 mode */
- val = cpu_to_cfi16((cmd << 8) | cmd);
- }
- } else if (cfi_buswidth_is_4()) {
- if (cfi_interleave_is_1()) {
- /* 1 x32 device in x32 mode */
- val = cpu_to_cfi32(cmd);
- } else if (cfi_interleave_is_2()) {
- /* 2 x16 device in x16 mode */
- val = cpu_to_cfi32((cmd << 16) | cmd);
- } else if (cfi_interleave_is_4()) {
- /* 4 (x8, x16 or x32) devices in x8 mode */
- val = (cmd << 16) | cmd;
- val = cpu_to_cfi32((val << 8) | val);
- }
-#ifdef CFI_WORD_64
- } else if (cfi_buswidth_is_8()) {
- if (cfi_interleave_is_1()) {
- /* 1 x64 device in x64 mode */
- val = cpu_to_cfi64(cmd);
- } else if (cfi_interleave_is_2()) {
- /* 2 x32 device in x32 mode */
- val = cmd;
- val = cpu_to_cfi64((val << 32) | val);
- } else if (cfi_interleave_is_4()) {
- /* 4 (x16, x32 or x64) devices in x16 mode */
- val = (cmd << 16) | cmd;
- val = cpu_to_cfi64((val << 32) | val);
- } else if (cfi_interleave_is_8()) {
- /* 8 (x8, x16 or x32) devices in x8 mode */
- val = (cmd << 8) | cmd;
- val = (val << 16) | val;
- val = (val << 32) | val;
- val = cpu_to_cfi64(val);
- }
-#endif /* CFI_WORD_64 */
- }
- return val;
-}
-#define CMD(x) cfi_build_cmd((x), map, cfi)
-
-/*
- * Read a value according to the bus width.
- */
-
-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
-{
- if (cfi_buswidth_is_1()) {
- return map_read8(map, addr);
- } else if (cfi_buswidth_is_2()) {
- return map_read16(map, addr);
- } else if (cfi_buswidth_is_4()) {
- return map_read32(map, addr);
- } else if (cfi_buswidth_is_8()) {
- return map_read64(map, addr);
+ map_word val = { {0} };
+ int wordwidth, words_per_bus, chip_mode, chips_per_word;
+ unsigned long onecmd;
+ int i;
+
+ /* We do it this way to give the compiler a fighting chance
+ of optimising away all the crap for 'bankwidth' larger than
+ an unsigned long, in the common case where that support is
+ disabled */
+ if (map_bankwidth_is_large(map)) {
+ wordwidth = sizeof(unsigned long);
+ words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
} else {
- return 0;
+ wordwidth = map_bankwidth(map);
+ words_per_bus = 1;
+ }
+
+ chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+ chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+ /* First, determine what the bit-pattern should be for a single
+ device, according to chip mode and endianness... */
+ switch (chip_mode) {
+ default: BUG();
+ case 1:
+ onecmd = cmd;
+ break;
+ case 2:
+ onecmd = cpu_to_cfi16(cmd);
+ break;
+ case 4:
+ onecmd = cpu_to_cfi32(cmd);
+ break;
}
-}
-/*
- * Write a value according to the bus width.
- */
+ /* Now replicate it across the size of an unsigned long, or
+ just to the bus width as appropriate */
+ switch (chips_per_word) {
+ default: BUG();
+#if BITS_PER_LONG >= 64
+ case 8:
+ onecmd |= (onecmd << (chip_mode * 32));
+#endif
+ case 4:
+ onecmd |= (onecmd << (chip_mode * 16));
+ case 2:
+ onecmd |= (onecmd << (chip_mode * 8));
+ case 1:
+ ;
+ }
-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
-{
- if (cfi_buswidth_is_1()) {
- map_write8(map, val, addr);
- } else if (cfi_buswidth_is_2()) {
- map_write16(map, val, addr);
- } else if (cfi_buswidth_is_4()) {
- map_write32(map, val, addr);
- } else if (cfi_buswidth_is_8()) {
- map_write64(map, val, addr);
+ /* And finally, for the multi-word case, replicate it
+ in all words in the structure */
+ for (i=0; i < words_per_bus; i++) {
+ val.x[i] = onecmd;
}
+
+ return val;
}
+#define CMD(x) cfi_build_cmd((x), map, cfi)
/*
* Sends a CFI command to a bank of flash for the given geometry.
* If prev_val is non-null, it will be set to the value at the command address,
* before the command was written.
*/
-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
+static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
struct map_info *map, struct cfi_private *cfi,
- int type, cfi_word *prev_val)
+ int type, map_word *prev_val)
{
- cfi_word val;
- __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
+ map_word val;
+ uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
val = cfi_build_cmd(cmd, map, cfi);
if (prev_val)
- *prev_val = cfi_read(map, addr);
+ *prev_val = map_read(map, addr);
- cfi_write(map, val, addr);
+ map_write(map, val, addr);
return addr - base;
}
-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
+static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
{
- if (cfi_buswidth_is_1()) {
- return map_read8(map, addr);
- } else if (cfi_buswidth_is_2()) {
- return cfi16_to_cpu(map_read16(map, addr));
- } else if (cfi_buswidth_is_4()) {
- return cfi32_to_cpu(map_read32(map, addr));
- } else if (cfi_buswidth_is_8()) {
- return cfi64_to_cpu(map_read64(map, addr));
+ map_word val = map_read(map, addr);
+
+ if (map_bankwidth_is_1(map)) {
+ return val.x[0];
+ } else if (map_bankwidth_is_2(map)) {
+ return cfi16_to_cpu(val.x[0]);
} else {
- return 0;
+ /* No point in a 64-bit byteswap since that would just be
+ swapping the responses from different chips, and we are
+ only interested in one chip (a representative sample) */
+ return cfi32_to_cpu(val.x[0]);
}
}
spin_unlock_bh(mutex);
}
+struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
+ const char* name);
+
+struct cfi_fixup {
+ uint16_t mfr;
+ uint16_t id;
+ void (*fixup)(struct map_info *map, void* param);
+ void* param;
+};
+
+#define CFI_MFR_ANY 0xffff
+#define CFI_ID_ANY 0xffff
+
+void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups);
+
#endif /* __MTD_CFI_H__ */
-
-/* Linux driver for Disk-On-Chip 2000 */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2000.h,v 1.17 2003/06/12 01:20:46 gerg Exp $ */
+/*
+ * Linux driver for Disk-On-Chip devices
+ *
+ * Copyright (C) 1999 Machine Vision Holdings, Inc.
+ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
+ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
+ * Copyright (C) 2002-2003 SnapGear Inc
+ *
+ * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $
+ *
+ * Released under GPL
+ */
#ifndef __MTD_DOC2000_H__
#define __MTD_DOC2000_H__
#include <linux/mtd/mtd.h>
+#include <asm/semaphore.h>
#define DoC_Sig1 0
#define DoC_Sig2 1
* Others use readb/writeb
*/
#if defined(__arm__)
-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
-#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
+#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
+#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x8000
#elif defined(__ppc__)
-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
-#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
+#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
+#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x4000
#else
#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg))
#define DOC_MODE_MDWREN 0x04
#define DOC_ChipID_Doc2k 0x20
+#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
#define DOC_ChipID_DocMil 0x30
#define DOC_ChipID_DocMilPlus32 0x40
#define DOC_ChipID_DocMilPlus16 0x41
#define MAX_FLOORS 4
#define MAX_CHIPS 4
-#define MAX_FLOORS_MIL 4
+#define MAX_FLOORS_MIL 1
#define MAX_CHIPS_MIL 1
-#define MAX_FLOORS_MPLUS 1
+#define MAX_FLOORS_MPLUS 2
#define MAX_CHIPS_MPLUS 1
#define ADDR_COLUMN 1
unsigned long physadr;
unsigned long virtadr;
unsigned long totlen;
- char ChipID; /* Type of DiskOnChip */
+ unsigned char ChipID; /* Type of DiskOnChip */
int ioreg;
unsigned long mfr; /* Flash IDs - only one type of flash per device */
*
* (C) 2000 Red Hat. GPLd.
*
- * $Id: flashchip.h,v 1.9 2003/04/30 11:15:22 dwmw2 Exp $
+ * $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $
*
*/
/* NOTE: confusingly, this can be used to refer to more than one chip at a time,
- if they're interleaved. */
+ if they're interleaved. This can even refer to individual partitions on
+ the same physical chip when present. */
struct flchip {
unsigned long start; /* Offset within the map */
int write_suspended:1;
int erase_suspended:1;
+ unsigned long in_progress_block_addr;
spinlock_t *mutex;
spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */
int word_write_time;
int buffer_write_time;
int erase_time;
+
+ void *priv;
};
+/* This is used to handle contention on write/erase operations
+ between partitions of the same physical chip. */
+struct flchip_shared {
+ spinlock_t lock;
+ struct flchip *writing;
+ struct flchip *erasing;
+};
#endif /* __MTD_FLASHCHIP_H__ */
/*
- * $Id: ftl.h,v 1.5 2001/06/02 20:35:51 dwmw2 Exp $
+ * $Id: ftl.h,v 1.6 2003/01/24 13:20:04 dwmw2 Exp $
*
* Derived from (and probably identical to):
* ftl.h 1.7 1999/10/25 20:23:17
/*
* (C) 2001, 2001 Red Hat, Inc.
* GPL'd
- * $Id: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $
+ * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $
*/
#ifndef __LINUX_MTD_GEN_PROBE_H__
#include <linux/mtd/flashchip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
+#include <asm/bitops.h>
struct chip_probe {
char *name;
int (*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 *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
*
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
*
- * $Id: inftl.h,v 1.3 2003/05/23 11:35:34 dwmw2 Exp $
+ * $Id: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown Exp $
*/
#ifndef __MTD_INFTL_H__
#define __MTD_INFTL_H__
+#ifndef __KERNEL__
+#error This is a kernel header. Perhaps include nftl-user.h instead?
+#endif
+
#include <linux/mtd/blktrans.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
-#define OSAK_VERSION 0x5120
-#define PERCENTUSED 98
-
-#define SECTORSIZE 512
+#include <mtd/inftl-user.h>
#ifndef INFTL_MAJOR
-#define INFTL_MAJOR 93 /* FIXME */
+#define INFTL_MAJOR 94
#endif
#define INFTL_PARTN_BITS 4
-/* Block Control Information */
-
-struct inftl_bci {
- __u8 ECCsig[6];
- __u8 Status;
- __u8 Status1;
-} __attribute__((packed));
-
-struct inftl_unithead1 {
- __u16 virtualUnitNo;
- __u16 prevUnitNo;
- __u8 ANAC;
- __u8 NACs;
- __u8 parityPerField;
- __u8 discarded;
-} __attribute__((packed));
-
-struct inftl_unithead2 {
- __u8 parityPerField;
- __u8 ANAC;
- __u16 prevUnitNo;
- __u16 virtualUnitNo;
- __u8 NACs;
- __u8 discarded;
-} __attribute__((packed));
-
-struct inftl_unittail {
- __u8 Reserved[4];
- __u16 EraseMark;
- __u16 EraseMark1;
-} __attribute__((packed));
-
-union inftl_uci {
- struct inftl_unithead1 a;
- struct inftl_unithead2 b;
- struct inftl_unittail c;
-};
-
-struct inftl_oob {
- struct inftl_bci b;
- union inftl_uci u;
-};
-
-
-/* INFTL Media Header */
-
-struct INFTLPartition {
- __u32 virtualUnits;
- __u32 firstUnit;
- __u32 lastUnit;
- __u32 flags;
- __u32 spareUnits;
- __u32 Reserved0;
- __u32 Reserved1;
-} __attribute__((packed));
-
-struct INFTLMediaHeader {
- char bootRecordID[8];
- __u32 NoOfBootImageBlocks;
- __u32 NoOfBinaryPartitions;
- __u32 NoOfBDTLPartitions;
- __u32 BlockMultiplierBits;
- __u32 FormatFlags;
- __u32 OsakVersion;
- __u32 PercentUsed;
- struct INFTLPartition Partitions[4];
-} __attribute__((packed));
-
-/* Partition flag types */
-#define INFTL_BINARY 0x20000000
-#define INFTL_BDTL 0x40000000
-#define INFTL_LAST 0x80000000
-
-
#ifdef __KERNEL__
struct INFTLrecord {
struct mtd_blktrans_dev mbd;
- __u16 MediaUnit, SpareMediaUnit;
+ __u16 MediaUnit;
__u32 EraseSize;
struct INFTLMediaHeader MediaHdr;
int usecount;
unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr;
+ struct nand_oobinfo oobinfo;
};
int INFTL_mount(struct INFTLrecord *s);
/* Overhauled routines for dealing with different mmap regions of flash */
-/* $Id: map.h,v 1.34 2003/05/28 12:42:22 dwmw2 Exp $ */
+/* $Id: map.h,v 1.43 2004/07/14 13:30:27 dwmw2 Exp $ */
#ifndef __LINUX_MTD_MAP_H__
#define __LINUX_MTD_MAP_H__
#include <linux/config.h>
#include <linux/types.h>
#include <linux/list.h>
+#include <asm/unaligned.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/bug.h>
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+#define map_bankwidth(map) 1
+#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
+#define map_bankwidth_is_large(map) (0)
+#define map_words(map) (1)
+#define MAX_MAP_BANKWIDTH 1
+#else
+#define map_bankwidth_is_1(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+# ifdef map_bankwidth
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# else
+# define map_bankwidth(map) 2
+# define map_bankwidth_is_large(map) (0)
+# define map_words(map) (1)
+# endif
+#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 2
+#else
+#define map_bankwidth_is_2(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+# ifdef map_bankwidth
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# else
+# define map_bankwidth(map) 4
+# define map_bankwidth_is_large(map) (0)
+# define map_words(map) (1)
+# endif
+#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 4
+#else
+#define map_bankwidth_is_4(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+# ifdef map_bankwidth
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# if BITS_PER_LONG < 64
+# undef map_bankwidth_is_large
+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+# undef map_words
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# endif
+# else
+# define map_bankwidth(map) 8
+# define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# endif
+#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 8
+#else
+#define map_bankwidth_is_8(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+# ifdef map_bankwidth
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# undef map_bankwidth_is_large
+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+# undef map_words
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# else
+# define map_bankwidth(map) 16
+# define map_bankwidth_is_large(map) (1)
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# endif
+#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 16
+#else
+#define map_bankwidth_is_16(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+# ifdef map_bankwidth
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# undef map_bankwidth_is_large
+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+# undef map_words
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# else
+# define map_bankwidth(map) 32
+# define map_bankwidth_is_large(map) (1)
+# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long))
+# endif
+#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 32
+#else
+#define map_bankwidth_is_32(map) (0)
+#endif
+
+#ifndef map_bankwidth
+#error "No bus width supported. What's the point?"
+#endif
+
+static inline int map_bankwidth_supported(int w)
+{
+ switch (w) {
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+ case 1:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+ case 2:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+ case 4:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+ case 8:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+ case 16:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+ case 32:
+#endif
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
+
+typedef union {
+ unsigned long x[MAX_MAP_LONGS];
+} map_word;
/* The map stuff is very simple. You fill in your struct map_info with
a handful of routines for accessing the device, making sure they handle
paging etc. correctly if your device needs it. Then you pass it off
- to a chip driver which deals with a mapped device - generally either
- do_cfi_probe() or do_ram_probe(), either of which will return a
- struct mtd_info if they liked what they saw. At which point, you
- fill in the mtd->module with your own module address, and register
- it.
+ to a chip probe routine -- either JEDEC or CFI probe or both -- via
+ do_map_probe(). If a chip is recognised, the probe code will invoke the
+ appropriate chip driver (if present) and return a struct mtd_info.
+ At which point, you fill in the mtd->module with your own module
+ address, and register it with the MTD core code. Or you could partition
+ it and register the partitions instead, or keep it for your own private
+ use; whatever.
The mtd->priv field will point to the struct map_info, and any further
private data required by the chip driver is linked from the
unsigned long virt;
void *cached;
- int buswidth; /* in octets */
+ int bankwidth; /* in octets. This isn't necessarily the width
+ of actual bus cycles -- it's the repeat interval
+ in bytes, before you are talking to the first chip again.
+ */
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
- u8 (*read8)(struct map_info *, unsigned long);
- u16 (*read16)(struct map_info *, unsigned long);
- u32 (*read32)(struct map_info *, unsigned long);
- u64 (*read64)(struct map_info *, unsigned long);
- /* If it returned a 'long' I'd call it readl.
- * It doesn't.
- * I won't.
- * dwmw2 */
-
+ map_word (*read)(struct map_info *, unsigned long);
void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
- void (*write8)(struct map_info *, u8, unsigned long);
- void (*write16)(struct map_info *, u16, unsigned long);
- void (*write32)(struct map_info *, u32, unsigned long);
- void (*write64)(struct map_info *, u64, unsigned long);
+
+ void (*write)(struct map_info *, const map_word, unsigned long);
void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
/* We can perhaps put in 'point' and 'unpoint' methods, if we really
want to enable XIP for non-linear mappings. Not yet though. */
#endif
+ /* It's possible for the map driver to use cached memory in its
+ copy_from implementation (and _only_ with copy_from). However,
+ when the chip driver knows some flash area has changed contents,
+ it will signal it to the map driver through this routine to let
+ the map driver invalidate the corresponding cache as needed.
+ If there is no cache to care about this can be set to NULL. */
+ void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
+
/* set_vpp() must handle being reentered -- enable, enable, disable
must leave it enabled. */
void (*set_vpp)(struct map_info *, int);
#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
-#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-#define map_read8(map, ofs) (map)->read8(map, ofs)
-#define map_read16(map, ofs) (map)->read16(map, ofs)
-#define map_read32(map, ofs) (map)->read32(map, ofs)
-#define map_read64(map, ofs) (map)->read64(map, ofs)
-#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
-#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs)
-#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs)
-#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs)
-#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs)
-#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+#define INVALIDATE_CACHED_RANGE(map, from, size) \
+ do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
-extern void simple_map_init(struct map_info *);
-#define map_is_linear(map) (map->phys != NO_XIP)
-#else
-static inline u8 map_read8(struct map_info *map, unsigned long ofs)
+static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
{
- return __raw_readb(map->virt + ofs);
+ int i;
+ for (i=0; i<map_words(map); i++) {
+ if (val1.x[i] != val2.x[i])
+ return 0;
+ }
+ return 1;
}
-static inline u16 map_read16(struct map_info *map, unsigned long ofs)
+static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
{
- return __raw_readw(map->virt + ofs);
+ map_word r;
+ int i;
+
+ for (i=0; i<map_words(map); i++) {
+ r.x[i] = val1.x[i] & val2.x[i];
+ }
+ return r;
}
-static inline u32 map_read32(struct map_info *map, unsigned long ofs)
+static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
{
- return __raw_readl(map->virt + ofs);
+ map_word r;
+ int i;
+
+ for (i=0; i<map_words(map); i++) {
+ r.x[i] = val1.x[i] | val2.x[i];
+ }
+ return r;
}
+#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
-static inline u64 map_read64(struct map_info *map, unsigned long ofs)
+static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
{
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
- BUG();
+ int i;
+
+ for (i=0; i<map_words(map); i++) {
+ if (val1.x[i] & val2.x[i])
+ return 1;
+ }
return 0;
-#else
- return __raw_readll(map->virt + ofs);
-#endif
}
-static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs)
+static inline map_word map_word_load(struct map_info *map, const void *ptr)
{
- __raw_writeb(datum, map->virt + ofs);
- mb();
+ map_word r;
+
+ if (map_bankwidth_is_1(map))
+ r.x[0] = *(unsigned char *)ptr;
+ else if (map_bankwidth_is_2(map))
+ r.x[0] = get_unaligned((uint16_t *)ptr);
+ else if (map_bankwidth_is_4(map))
+ r.x[0] = get_unaligned((uint32_t *)ptr);
+#if BITS_PER_LONG >= 64
+ else if (map_bankwidth_is_8(map))
+ r.x[0] = get_unaligned((uint64_t *)ptr);
+#endif
+ else if (map_bankwidth_is_large(map))
+ memcpy(r.x, ptr, map->bankwidth);
+
+ return r;
}
-static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs)
+static inline map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
{
- __raw_writew(datum, map->virt + ofs);
- mb();
+ int i;
+
+ if (map_bankwidth_is_large(map)) {
+ char *dest = (char *)&orig;
+ memcpy(dest+start, buf, len);
+ } else {
+ for (i=start; i < start+len; i++) {
+ int bitpos;
+#ifdef __LITTLE_ENDIAN
+ bitpos = i*8;
+#else /* __BIG_ENDIAN */
+ bitpos = (map_bankwidth(map)-1-i)*8;
+#endif
+ orig.x[0] &= ~(0xff << bitpos);
+ orig.x[0] |= buf[i] << bitpos;
+ }
+ }
+ return orig;
}
-static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs)
+static inline map_word map_word_ff(struct map_info *map)
{
- __raw_writel(datum, map->virt + ofs);
- mb();
+ map_word r;
+ int i;
+
+ for (i=0; i<map_words(map); i++) {
+ r.x[i] = ~0UL;
+ }
+ return r;
+}
+static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
+{
+ map_word r;
+
+ if (map_bankwidth_is_1(map))
+ r.x[0] = __raw_readb(map->virt + ofs);
+ else if (map_bankwidth_is_2(map))
+ r.x[0] = __raw_readw(map->virt + ofs);
+ else if (map_bankwidth_is_4(map))
+ r.x[0] = __raw_readl(map->virt + ofs);
+#if BITS_PER_LONG >= 64
+ else if (map_bankwidth_is_8(map))
+ r.x[0] = __raw_readq(map->virt + ofs);
+#endif
+ else if (map_bankwidth_is_large(map))
+ memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
+
+ return r;
}
-static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs)
+static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
{
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
- BUG();
-#else
- __raw_writell(datum, map->virt + ofs);
+ if (map_bankwidth_is_1(map))
+ __raw_writeb(datum.x[0], map->virt + ofs);
+ else if (map_bankwidth_is_2(map))
+ __raw_writew(datum.x[0], map->virt + ofs);
+ else if (map_bankwidth_is_4(map))
+ __raw_writel(datum.x[0], map->virt + ofs);
+#if BITS_PER_LONG >= 64
+ else if (map_bankwidth_is_8(map))
+ __raw_writeq(datum.x[0], map->virt + ofs);
+#endif
+ else if (map_bankwidth_is_large(map))
+ memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
mb();
-#endif /* CFI_B8 */
}
-static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- memcpy_fromio(to, map->virt + from, len);
+ if (map->cached)
+ memcpy(to, (char *)map->cached + from, len);
+ else
+ memcpy_fromio(to, map->virt + from, len);
}
-static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(map->virt + to, from, len);
}
-#define simple_map_init(map) do { } while (0)
+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
+#define map_read(map, ofs) (map)->read(map, ofs)
+#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
+extern void simple_map_init(struct map_info *);
+#define map_is_linear(map) (map->phys != NO_XIP)
+
+#else
+#define map_read(map, ofs) inline_map_read(map, ofs)
+#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
+
+
+#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
#define map_is_linear(map) (1)
#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
-
-/* $Id: mtd.h,v 1.45 2003/05/20 21:56:40 dwmw2 Exp $ */
+/*
+ * $Id: mtd.h,v 1.54 2004/07/15 01:13:12 dwmw2 Exp $
+ *
+ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
+ *
+ * Released under GPL
+ */
#ifndef __MTD_MTD_H__
#define __MTD_MTD_H__
-#ifdef __KERNEL__
+#ifndef __KERNEL__
+#error This is a kernel header. Perhaps include mtd-user.h instead?
+#endif
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/uio.h>
-#endif /* __KERNEL__ */
-
-struct erase_info_user {
- u_int32_t start;
- u_int32_t length;
-};
-
-struct mtd_oob_buf {
- u_int32_t start;
- u_int32_t length;
- unsigned char __user *ptr;
-};
+#include <mtd/mtd-abi.h>
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
#define MAX_MTD_DEVICES 16
-
-
-#define MTD_ABSENT 0
-#define MTD_RAM 1
-#define MTD_ROM 2
-#define MTD_NORFLASH 3
-#define MTD_NANDFLASH 4
-#define MTD_PEROM 5
-#define MTD_OTHER 14
-#define MTD_UNKNOWN 15
-
-
-
-#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
-#define MTD_SET_BITS 2 // Bits can be set
-#define MTD_ERASEABLE 4 // Has an erase function
-#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
-#define MTD_VOLATILE 16 // Set for RAMs
-#define MTD_XIP 32 // eXecute-In-Place possible
-#define MTD_OOB 64 // Out-of-band data (NAND flash)
-#define MTD_ECC 128 // Device capable of automatic ECC
-
-// Some common devices / combinations of capabilities
-#define MTD_CAP_ROM 0
-#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
-#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
-#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
-#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
-
-
-// Types of automatic ECC/Checksum available
-#define MTD_ECC_NONE 0 // No automatic ECC available
-#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
-#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
-
-struct mtd_info_user {
- u_char type;
- u_int32_t flags;
- u_int32_t size; // Total size of the MTD
- u_int32_t erasesize;
- u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
- u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
- u_int32_t ecctype;
- u_int32_t eccsize;
-};
-
-struct region_info_user {
- u_int32_t offset; /* At which this region starts,
- * from the beginning of the MTD */
- u_int32_t erasesize; /* For this region */
- u_int32_t numblocks; /* Number of blocks in this region */
- u_int32_t regionindex;
-};
-
-#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
-#define MEMERASE _IOW('M', 2, struct erase_info_user)
-#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
-#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
-#define MEMLOCK _IOW('M', 5, struct erase_info_user)
-#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
-#define MEMGETREGIONCOUNT _IOR('M', 7, int)
-#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
-#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
-
-struct nand_oobinfo {
- int useecc;
- int eccpos[6];
-};
-
-
-#ifndef __KERNEL__
-
-typedef struct mtd_info_user mtd_info_t;
-typedef struct erase_info_user erase_info_t;
-typedef struct region_info_user region_info_t;
-typedef struct nand_oobinfo nand_oobinfo_t;
-
- /* User-space ioctl definitions */
-
-#else /* __KERNEL__ */
-
-
#define MTD_ERASE_PENDING 0x01
#define MTD_ERASING 0x02
#define MTD_ERASE_SUSPEND 0x04
#define MTD_ERASE_DONE 0x08
#define MTD_ERASE_FAILED 0x10
+/* If the erase fails, fail_addr might indicate exactly which block failed. If
+ fail_addr = 0xffffffff, the failure was not at the device level or was not
+ specific to any particular block. */
struct erase_info {
struct mtd_info *mtd;
u_int32_t addr;
u_int32_t len;
+ u_int32_t fail_addr;
u_long time;
u_long retries;
u_int dev;
u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
+ u_int32_t oobavail; // Number of bytes in OOB area available for fs
u_int32_t ecctype;
u_int32_t eccsize;
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
+ /* Bad block management functions */
+ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
+
void *priv;
struct module *owner;
#endif /* CONFIG_MTD_DEBUG */
-#endif /* __KERNEL__ */
-
#endif /* __MTD_MTD_H__ */
* Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de>
*
- * $Id: nand.h,v 1.25 2003/05/21 15:15:02 dwmw2 Exp $
+ * $Id: nand.h,v 1.63 2004/07/07 16:29:43 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
* NAND_YAFFS_OOB
* 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL
* Split manufacturer and device ID structures
+ *
+ * 02-08-2004 tglx added option field to nand structure for chip anomalities
+ * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id
+ * update of nand_chip structure description
*/
#ifndef __LINUX_MTD_NAND_H
#define __LINUX_MTD_NAND_H
#include <linux/config.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
struct mtd_info;
-/*
- * Searches for a NAND device
+/* Scan and identify a NAND device */
+extern int nand_scan (struct mtd_info *mtd, int max_chips);
+/* Free resources held by the NAND device */
+extern void nand_release (struct mtd_info *mtd);
+
+/* Read raw data from the device without ECC */
+extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
+
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS 8
+
+/* This constant declares the max. oobsize / page, which
+ * is supported now. If you add a chip with bigger oobsize/page
+ * adjust this accordingly.
*/
-extern int nand_scan (struct mtd_info *mtd);
+#define NAND_MAX_OOBSIZE 64
/*
* Constants for hardware specific CLE/ALE/NCE function
*/
+/* 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
/*
* Standard NAND flash commands
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff
+/* Extended commands for large page devices */
+#define NAND_CMD_READSTART 0x30
+#define NAND_CMD_CACHEDPROG 0x15
+
+/* Status bits */
+#define NAND_STATUS_FAIL 0x01
+#define NAND_STATUS_FAIL_N1 0x02
+#define NAND_STATUS_TRUE_READY 0x20
+#define NAND_STATUS_READY 0x40
+#define NAND_STATUS_WP 0x80
+
/*
* Constants for ECC_MODES
- *
- * NONE: No ECC
- * SOFT: Software ECC 3 byte ECC per 256 Byte data
- * HW3_256: Hardware ECC 3 byte ECC per 256 Byte data
- * HW3_512: Hardware ECC 3 byte ECC per 512 Byte data
- *
- *
-*/
+ */
+
+/* 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 3 byte ECC per 512 Byte data */
#define NAND_ECC_HW6_512 4
-#define NAND_ECC_DISKONCHIP 5
+/* Hardware ECC 8 byte ECC per 512 Byte data */
+#define NAND_ECC_HW8_512 6
/*
* Constants for Hardware ECC
*/
+/* Reset Hardware ECC for read */
#define NAND_ECC_READ 0
+/* Reset Hardware ECC for write */
#define NAND_ECC_WRITE 1
-
+/* Enable Hardware ECC before syndrom is read back from flash */
+#define NAND_ECC_READSYN 2
+
+/* Option constants for bizarre disfunctionality and real
+* features
+*/
+/* 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
+
+/* Options valid for Samsung large page devices */
+#define NAND_SAMSUNG_LP_OPTIONS \
+ (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
+
+/* Macros to identify the above */
+#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
+#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
+#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
+#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+
+/* Mask to zero out the chip options, which come from the id table */
+#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
+
+/* Non chip related options */
+/* Use a flash based bad block table. This option is passed to the
+ * default bad block table function. */
+#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
+
+
+/* Options set by nand scan */
+/* Nand scan has allocated oob_buf */
+#define NAND_OOBBUF_ALLOC 0x40000000
+/* Nand scan has allocated data_buf */
+#define NAND_DATABUF_ALLOC 0x80000000
+
+
/*
+ * nand_state_t - chip states
* Enumeration for NAND flash chip state
*/
typedef enum {
FL_READING,
FL_WRITING,
FL_ERASING,
- FL_SYNCING
+ FL_SYNCING,
+ FL_CACHEDPRG,
} nand_state_t;
-/*
- * NAND Private Flash Chip Data
- *
- * Structure overview:
- *
- * IO_ADDR_R - address to read the 8 I/O lines of the flash device
- *
- * IO_ADDR_W - address to write the 8 I/O lines of the flash device
- *
- * hwcontrol - hardwarespecific function for accesing control-lines
- *
- * dev_ready - hardwarespecific function for accesing device ready/busy line
- *
- * waitfunc - hardwarespecific function for wait on ready
- *
- * calculate_ecc - function for ecc calculation or readback from ecc hardware
- *
- * correct_data - function for ecc correction, matching to ecc generator (sw/hw)
- *
- * enable_hwecc - function to enable (reset) hardware ecc generator
- *
- * eccmod - mode of ecc: see constants
- *
- * eccsize - databytes used per ecc-calculation
- *
- * chip_delay - chip dependent delay for transfering data from array to read regs (tR)
- *
- * chip_lock - spinlock used to protect access to this structure
- *
- * wq - wait queue to sleep on if a NAND operation is in progress
- *
- * state - give the current state of the NAND device
- *
- * page_shift - number of address bits in a page (column address bits)
- *
- * data_buf - data buffer passed to/from MTD user modules
- *
- * data_cache - data cache for redundant page access and shadow for
- * ECC failure
- *
- * cache_page - number of last valid page in page_cache
+/**
+ * struct nand_chip - NAND Private Flash Chip Data
+ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
+ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
+ * @read_byte: [REPLACEABLE] read one byte from the chip
+ * @write_byte: [REPLACEABLE] write one byte to the chip
+ * @read_word: [REPLACEABLE] read one word from the chip
+ * @write_word: [REPLACEABLE] write one word to the chip
+ * @write_buf: [REPLACEABLE] write data from the buffer to the chip
+ * @read_buf: [REPLACEABLE] read data from the chip into the buffer
+ * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
+ * @select_chip: [REPLACEABLE] select chip nr
+ * @block_bad: [REPLACEABLE] check, if the block is bad
+ * @block_markbad: [REPLACEABLE] mark the block bad
+ * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
+ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
+ * If set to NULL no access to ready/busy is available and the ready/busy information
+ * is read from the chip status register
+ * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
+ * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
+ * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware
+ * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
+ * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
+ * be provided if a hardware ECC is available
+ * @erase_cmd: [INTERN] erase command write function, selectable due to AND support
+ * @scan_bbt: [REPLACEABLE] function to scan bad block table
+ * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
+ * @eccsize: [INTERN] databytes used per ecc-calculation
+ * @eccsteps: [INTERN] number of ecc calculation steps per page
+ * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
+ * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
+ * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
+ * @state: [INTERN] the current state of the NAND device
+ * @page_shift: [INTERN] number of address bits in a page (column address bits)
+ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
+ * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
+ * @chip_shift: [INTERN] number of address bits in one chip
+ * @data_buf: [INTERN] internal buffer for one page + oob
+ * @oob_buf: [INTERN] oob buffer for one eraseblock
+ * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
+ * @data_poi: [INTERN] pointer to a data buffer
+ * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
+ * special functionality. See the defines for further explanation
+ * @badblockpos: [INTERN] position of the bad block marker in the oob area
+ * @numchips: [INTERN] number of physical chips
+ * @chipsize: [INTERN] the size of one chip for multichip arrays
+ * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
+ * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
+ * @autooob: [REPLACEABLE] the default (auto)placement scheme
+ * @bbt: [INTERN] bad block table pointer
+ * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
+ * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
+ * @priv: [OPTIONAL] pointer to private chip date
*/
+
struct nand_chip {
unsigned long IO_ADDR_R;
unsigned long IO_ADDR_W;
- void (*hwcontrol)(int cmd);
- int (*dev_ready)(void);
+
+ u_char (*read_byte)(struct mtd_info *mtd);
+ void (*write_byte)(struct mtd_info *mtd, u_char byte);
+ u16 (*read_word)(struct mtd_info *mtd);
+ void (*write_word)(struct mtd_info *mtd, u16 word);
+
+ void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
+ void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
+ int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
+ void (*select_chip)(struct mtd_info *mtd, int chip);
+ int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
+ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+ void (*hwcontrol)(struct mtd_info *mtd, int cmd);
+ int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
- void (*calculate_ecc)(const u_char *dat, u_char *ecc_code);
- int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc);
- void (*enable_hwecc)(int mode);
+ int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+ int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+ void (*enable_hwecc)(struct mtd_info *mtd, int mode);
+ void (*erase_cmd)(struct mtd_info *mtd, int page);
+ int (*scan_bbt)(struct mtd_info *mtd);
int eccmode;
int eccsize;
+ int eccsteps;
int chip_delay;
- spinlock_t chip_lock;
+ spinlock_t chip_lock;
wait_queue_head_t wq;
nand_state_t state;
int page_shift;
+ int phys_erase_shift;
+ int bbt_erase_shift;
+ int chip_shift;
u_char *data_buf;
+ u_char *oob_buf;
+ int oobdirty;
u_char *data_poi;
+ unsigned int options;
+ int badblockpos;
+ int numchips;
+ unsigned long chipsize;
+ int pagemask;
+ int pagebuf;
+ struct nand_oobinfo *autooob;
+ uint8_t *bbt;
+ struct nand_bbt_descr *bbt_td;
+ struct nand_bbt_descr *bbt_md;
+ void *priv;
};
/*
#define NAND_MFR_SAMSUNG 0xec
#define NAND_MFR_FUJITSU 0x04
#define NAND_MFR_NATIONAL 0x8f
+#define NAND_MFR_RENESAS 0x07
+#define NAND_MFR_STMICRO 0x20
-/*
- * NAND Flash Device ID Structure
- *
- * Structure overview:
- *
- * name - Identify the device type
- *
- * id - device ID code
- *
- * chipshift - total number of address bits for the device which
- * is used to calculate address offsets and the total
- * number of bytes the device is capable of.
- *
- * page256 - denotes if flash device has 256 byte pages or not.
- *
- * pageadrlen - number of bytes minus one needed to hold the
- * complete address into the flash array. Keep in
- * mind that when a read or write is done to a
- * specific address, the address is input serially
- * 8 bits at a time. This structure member is used
- * by the read/write routines as a loop index for
- * shifting the address out 8 bits at a time.
- *
- * erasesize - size of an erase block in the flash device.
+/**
+ * struct nand_flash_dev - NAND Flash Device ID Structure
+ *
+ * @name: Identify the device type
+ * @id: device ID code
+ * @pagesize: Pagesize in bytes. Either 256 or 512 or 0
+ * If the pagesize is 0, then the real pagesize
+ * and the eraseize are determined from the
+ * extended id bytes in the chip
+ * @erasesize: Size of an erase block in the flash device.
+ * @chipsize: Total chipsize in Mega Bytes
+ * @options: Bitfield to store chip relevant options
*/
struct nand_flash_dev {
- char * name;
+ char *name;
int id;
- int chipshift;
+ unsigned long pagesize;
+ unsigned long chipsize;
unsigned long erasesize;
- char page256;
+ unsigned long options;
};
-/*
- * NAND Flash Manufacturer ID Structure
- *
- * name - Manufacturer name
- *
- * id - manufacturer ID code of device.
+/**
+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
+ * @name: Manufacturer name
+ * @id: manufacturer ID code of device.
*/
struct nand_manufacturers {
int id;
extern struct nand_flash_dev nand_flash_ids[];
extern struct nand_manufacturers nand_manuf_ids[];
+/**
+ * struct nand_bbt_descr - bad block table descriptor
+ * @options: options for this descriptor
+ * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
+ * when bbt is searched, then we store the found bbts pages here.
+ * Its an array and supports up to 8 chips now
+ * @offs: offset of the pattern in the oob area of the page
+ * @veroffs: offset of the bbt version counter in the oob are of the page
+ * @version: version read from the bbt page during scan
+ * @len: length of the pattern, if 0 no pattern check is performed
+ * @maxblocks: maximum number of blocks to search for a bbt. This number of
+ * blocks is reserved at the end of the device where the tables are
+ * written.
+ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
+ * bad) block in the stored bbt
+ * @pattern: pattern to identify bad block table or factory marked good /
+ * bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+ int options;
+ int pages[NAND_MAX_CHIPS];
+ int offs;
+ int veroffs;
+ uint8_t version[NAND_MAX_CHIPS];
+ int len;
+ int maxblocks;
+ int reserved_block_code;
+ uint8_t *pattern;
+};
+
+/* 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
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE 0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS 4
+
+extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
+extern int nand_default_bbt (struct mtd_info *mtd);
+extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
+extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
+
/*
* Constants for oob configuration
*/
-#define NAND_BADBLOCK_POS 5
+#define NAND_SMALL_BADBLOCK_POS 5
+#define NAND_LARGE_BADBLOCK_POS 0
#endif /* __LINUX_MTD_NAND_H */
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
- * $Id: nand_ecc.h,v 1.2 2003/02/20 13:34:20 sjhill Exp $
+ * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown 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
* This file is the header for the ECC algorithm.
*/
-/*
- * Creates non-inverted ECC code from line parity
- */
-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code);
+#ifndef __MTD_NAND_ECC_H__
+#define __MTD_NAND_ECC_H__
+
+struct mtd_info;
/*
* Calculate 3 byte ECC code for 256 byte block
*/
-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);
/*
* 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);
+
+#endif /* __MTD_NAND_ECC_H__ */
/*
- * $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $
+ * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
*
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
*/
#include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
-/* Block Control Information */
-
-struct nftl_bci {
- unsigned char ECCSig[6];
- __u8 Status;
- __u8 Status1;
-}__attribute__((packed));
-
-/* Unit Control Information */
-
-struct nftl_uci0 {
- __u16 VirtUnitNum;
- __u16 ReplUnitNum;
- __u16 SpareVirtUnitNum;
- __u16 SpareReplUnitNum;
-} __attribute__((packed));
-
-struct nftl_uci1 {
- __u32 WearInfo;
- __u16 EraseMark;
- __u16 EraseMark1;
-} __attribute__((packed));
-
-struct nftl_uci2 {
- __u16 FoldMark;
- __u16 FoldMark1;
- __u32 unused;
-} __attribute__((packed));
-
-union nftl_uci {
- struct nftl_uci0 a;
- struct nftl_uci1 b;
- struct nftl_uci2 c;
-};
-
-struct nftl_oob {
- struct nftl_bci b;
- union nftl_uci u;
-};
-
-/* NFTL Media Header */
-
-struct NFTLMediaHeader {
- char DataOrgID[6];
- __u16 NumEraseUnits;
- __u16 FirstPhysicalEUN;
- __u32 FormattedSize;
- unsigned char UnitSizeFactor;
-} __attribute__((packed));
-
-#define MAX_ERASE_ZONES (8192 - 512)
-
-#define ERASE_MARK 0x3c69
-#define SECTOR_FREE 0xff
-#define SECTOR_USED 0x55
-#define SECTOR_IGNORE 0x11
-#define SECTOR_DELETED 0x00
-
-#define FOLD_MARK_IN_PROGRESS 0x5555
-
-#define ZONE_GOOD 0xff
-#define ZONE_BAD_ORIGINAL 0
-#define ZONE_BAD_MARKED 7
-
-#ifdef __KERNEL__
+#include <mtd/nftl-user.h>
/* these info are used in ReplUnitTable */
#define BLOCK_NIL 0xffff /* last block of a chain */
unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr;
+ struct nand_oobinfo oobinfo;
};
int NFTL_mount(struct NFTLrecord *s);
#define MAX_SECTORS_PER_UNIT 64
#define NFTL_PARTN_BITS 4
-#endif /* __KERNEL__ */
-
#endif /* __MTD_NFTL_H__ */
*
* This code is GPL
*
- * $Id: partitions.h,v 1.14 2003/05/20 21:56:29 dwmw2 Exp $
+ * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $
*/
#ifndef MTD_PARTITIONS_H
#define MTDPART_SIZ_FULL (0)
-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
+int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *);
/*
* reason (mips != alpha!)
*/
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1)
+#elif defined(CONFIG_SBC8560)
+/*
+ * WindRiver did something similarly broken on their SBC8560 board. The
+ * UART tristates its IRQ output while OUT2 is clear, but they pulled
+ * the interrupt line _up_ instead of down, so if we register the IRQ
+ * while the UART is in that state, we die in an IRQ storm. */
+#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
/* PPC CPM type number */
#define PORT_CPM 58
+/* Marvell MPSC for PPC & MIPS */
+#define PORT_MPSC 59
+
#ifdef __KERNEL__
#include <linux/config.h>
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
struct page *page = NULL;
swp_entry_t *entry;
- static const swp_entry_t unswapped = { 0 };
if (sgp != SGP_WRITE &&
((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode))
while (!(entry = shmem_swp_entry(info, index, &page))) {
if (sgp == SGP_READ)
- return (swp_entry_t *) &unswapped;
+ return shmem_swp_map(ZERO_PAGE(0));
/*
* Test free_blocks against 1 not 0, since we have 1 data
* page (and perhaps indirect index pages) yet to allocate: