E: tigran@veritas.com
W: http://www.ocston.org/~tigran
D: BFS filesystem
-D: Intel P6 CPU microcode update support
+D: Intel IA32 CPU microcode update support
D: Various kernel patches
S: United Kingdom
kernel source. Pay attention when you recompile your kernel ;-).
Also, be sure to upgrade to the latest pcmcia-cs release.
-Intel P6 microcode
-------------------
+Intel IA32 microcode
+--------------------
-A driver has been added to allow updating of Intel P6 microcode,
+A driver has been added to allow updating of Intel IA32 microcode,
accessible as both a devfs regular file and as a normal (misc)
character device. If you are not using devfs you may need to:
as root before you can use this. You'll probably also want to
get the user-space microcode_ctl utility to use with this.
+If you have compiled the driver as a module you may need to add
+the following line:
+
+alias char-major-10-184 microcode
+
+to your /etc/modules.conf file.
+
Networking
==========
Toshiba Linux utilities website at:
http://www.buzzard.org.uk/toshiba/
-/dev/cpu/microcode - Intel P6 CPU microcode support
+/dev/cpu/microcode - Intel IA32 CPU microcode support
CONFIG_MICROCODE
If you say Y here and also to "/dev file system support" in the
'File systems' section, you will be able to update the microcode on
- Intel processors in the P6 family, e.g. Pentium Pro, Pentium II,
- Pentium III, Xeon etc. You will obviously need the actual microcode
- binary data itself which is not shipped with the Linux kernel.
+ Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II,
+ Pentium III, Pentium 4, Xeon etc. You will obviously need the actual
+ microcode binary data itself which is not shipped with the Linux kernel.
For latest news and information on obtaining all the required
ingredients for this driver, check:
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called microcode.o. If you want to compile it as
- a module, say M here and read Documentation/modules.txt.
+ a module, say M here and read Documentation/modules.txt. If you use
+ modprobe or kmod you may also want to add the line
+ 'alias char-major-10-184 microcode' to your /etc/modules.conf file.
/dev/cpu/*/msr - Model-specific register support
CONFIG_X86_MSR
struct my_card_header *hp;
/* Examine the header to see if we wish
- * to except the data. But synchronize
+ * to accept the data. But synchronize
* the DMA transfer with the CPU first
* so that we see updated contents.
*/
vertical blanking data interfaces are also provided.
</para>
</chapter>
- <chapter>
+ <chapter id="radio">
<title>Radio Devices</title>
<para>
There are a wide variety of radio interfaces available for PC's, and these
Here is the new interface:
- void copy_user_page(void *from, void *to, unsigned long address)
+ void copy_user_page(void *to, void *from, unsigned long address)
void clear_user_page(void *to, unsigned long address)
These two routines store data in user anonymous or COW
0x22 all scsi/sg.h
'1' 00-1F <linux/timepps.h> PPS kit from Ulrich Windl
<ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>
-'6' 00-10 <asm-i386/processor.h> Intel P6 microcode update driver
- <tigran@veritas.com>
+'6' 00-10 <asm-i386/processor.h> Intel IA32 microcode update driver
+ <mailto:tigran@veritas.com>
'8' all SNP8023 advanced NIC card
<mailto:mcr@solidum.com>
'A' 00-1F linux/apm_bios.h
7.6 Compilation flags
7.7 Miscellaneous variables
8 New-style variables
+ 8.1 New variables
+ 8.2 Converting to old-style
9 Compatibility with Linux Kernel 2.2
10 Credits
old-style variables. This is because Rules.make processes only the
old-style variables.
+See section 8.2 ("Converting to old-style") for examples.
+
--- 6.4 Rules.make section
options still control whether or not its $(O_TARGET) goes into
vmlinux. See the $(M_OBJS) example below.
+ Sometimes the ordering of all $(OX_OBJS) files before all
+ $(O_OBJS) files can be a problem, particularly if both
+ $(O_OBJS) files and $(OX_OBJS) files contain __initcall
+ declarations where order is important. To avoid this imposed
+ ordering, the use of $(OX_OBJS) can be dropped altogether and
+ $(MIX_OBJS) used instead.
+
+ If this approach is used, then:
+ - All objects to be linked into vmlinux should be listed in
+ $(O_OBJS) in the desired order.
+ - All objects to be created as modules should be listed in
+ $(M_OBJS)
+ - All objects that export symbols should also be listed in
+ $(MIX_OBJS).
+
+ This has the same effect as maintaining the
+ exported/non-exported split, except that there is more control
+ over the ordering of object files in vmlinux.
+
--- 7.3 Library file goals
$(LD) -r -o $@ $(sb-objs)
+ As is mentioned in section 7.2 ("Object file goals"),
+ $(MIX_OBJS) can also be used simply to list all objects that
+ export any symbols. If this approach is taken, then
+ $(O_OBJS), $(L_OBJS), $(M_OBJS) and $(MI_OBJS) should simply
+ lists all of the vmlinux object files, library object files,
+ module object files and intermediate module files
+ respectively. Duplication between $(MI_OBJS) and $(MIX_OBJS)
+ is not a problem.
--- 7.6 Compilation flags
people define most variables using "new style" but then fall back to
"old style" for a few lines.
+--- 8.1 New variables
+
obj-y obj-m obj-n obj-
These variables replace $(O_OBJS), $(OX_OBJS), $(M_OBJS),
This means nls should be added to (subdir-y) and $(subdir-m) if
CONFIG_NFS = y.
+--- 8.2 Converting to old-style
+
+ The following example is taken from drivers/usb/Makefile.
+ Note that this uses MIX_OBJS to avoid the need for OX_OBJS and
+ MX_OBJS and thus to maintain the ordering of objects in $(obj-y)
+
+ # Translate to Rules.make lists.
+ multi-used := $(filter $(list-multi), $(obj-y) $(obj-m))
+ multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs))
+ active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m))
+
+ O_OBJS := $(obj-y)
+ M_OBJS := $(obj-m)
+ MIX_OBJS := $(filter $(export-objs), $(active-objs))
+
+ An example for libraries from drivers/acorn/scsi/Makefile:
+
+ # Translate to Rules.make lists.
+
+ L_OBJS := $(filter-out $(export-objs), $(obj-y))
+ LX_OBJS := $(filter $(export-objs), $(obj-y))
+ M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
+ MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+
+ As ordering is not so important in libraries, this still uses
+ LX_OBJS and MX_OBJS, though (presumably) it could be changed to
+ use MIX_OBJS as follows:
+
+ active-objs := $(sort $(obj-y) $(obj-m))
+ L_OBJS := $(obj-y)
+ M_OBJS := $(obj-m)
+ MIX_OBJS := $(filter $(export-objs), $(active-objs))
+
+
+ which is clearly shorted and arguably clearer.
=== 9 Compatibility with Linux Kernel 2.2
W: http://sourceforge.net/projects/gkernel/
S: Maintained
-INTEL P6 MICROCODE UPDATE SUPPORT
+INTEL IA32 MICROCODE UPDATE SUPPORT
P: Tigran Aivazian
M: tigran@veritas.com
S: Maintained
*(vip)CIA_IOC_PCI_W1_BASE = 0x40000000 | 1;
*(vip)CIA_IOC_PCI_W1_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vip)CIA_IOC_PCI_T1_BASE = 0;
+ *(vip)CIA_IOC_PCI_T1_BASE = 0 >> 2;
*(vip)CIA_IOC_PCI_W2_BASE = 0x80000000 | 1;
*(vip)CIA_IOC_PCI_W2_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vip)CIA_IOC_PCI_T2_BASE = 0x40000000;
+ *(vip)CIA_IOC_PCI_T2_BASE = 0x40000000 >> 2;
*(vip)CIA_IOC_PCI_W3_BASE = 0;
}
{
if (dev->class >> 8 != PCI_CLASS_STORAGE_IDE)
return;
- dev->resource[1].start |= 2;
- dev->resource[1].end = dev->resource[1].start;
- pci_claim_resource(dev, 0);
- pci_claim_resource(dev, 1);
+ dev->resource[0].flags = 0;
+ dev->resource[1].flags = 0;
}
static void __init
/* Check that we have a scatter-gather arena that fits. */
hose = pdev ? pdev->sysdata : pci_isa_hose;
arena = hose->sg_isa;
- if (arena && arena->dma_base + arena->size <= mask)
+ if (arena && arena->dma_base + arena->size - 1 <= mask)
return 1;
arena = hose->sg_pci;
- if (arena && arena->dma_base + arena->size <= mask)
+ if (arena && arena->dma_base + arena->size - 1 <= mask)
return 1;
return 0;
ev67 := ev67-
endif
-OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o \
- checksum.o csum_partial_copy.o $(ev67)strlen.o \
- $(ev67)strcat.o $(ev6)strcpy.o $(ev67)strncat.o $(ev6)strncpy.o \
- stxcpy.o stxncpy.o $(ev67)strchr.o strrchr.o memchr.o \
- $(ev6)copy_user.o $(ev6)clear_user.o $(ev6)strncpy_from_user.o \
- $(ev67)strlen_user.o $(ev6)csum_ipv6_magic.o strcasecmp.o fpreg.o \
+OBJS = __divqu.o __remqu.o __divlu.o __remlu.o \
+ $(ev6)memset.o \
+ $(ev6)memcpy.o \
+ memmove.o \
+ io.o \
+ checksum.o \
+ csum_partial_copy.o \
+ $(ev67)strlen.o \
+ $(ev67)strcat.o \
+ strcpy.o \
+ $(ev67)strncat.o \
+ strncpy.o \
+ $(ev6)stxcpy.o \
+ $(ev6)stxncpy.o \
+ $(ev67)strchr.o \
+ strrchr.o \
+ $(ev6)memchr.o \
+ $(ev6)copy_user.o \
+ $(ev6)clear_user.o \
+ $(ev6)strncpy_from_user.o \
+ $(ev67)strlen_user.o \
+ $(ev6)csum_ipv6_magic.o \
+ strcasecmp.o \
+ fpreg.o \
callback_srm.o srm_puts.o srm_printk.o
lib.a: $(OBJS)
*
* This file contains network checksum routines that are better done
* in an architecture-specific manner due to speed..
+ * Comments in other versions indicate that the algorithms are from RFC1071
+ *
+ * accellerated versions (and 21264 assembly versions ) contributed by
+ * Rick Gorton <rick.gorton@alpha-processor.com>
*/
#include <linux/string.h>
static inline unsigned short from64to16(unsigned long x)
{
- /* add up 32-bit words for 33 bits */
- x = (x & 0xffffffff) + (x >> 32);
- /* add up 16-bit and 17-bit words for 17+c bits */
- x = (x & 0xffff) + (x >> 16);
- /* add up 16-bit and 2-bit for 16+c bit */
- x = (x & 0xffff) + (x >> 16);
- /* add up carry.. */
- x = (x & 0xffff) + (x >> 16);
- return x;
+ /* Using extract instructions is a bit more efficient
+ than the original shift/bitmask version. */
+
+ union {
+ unsigned long ul;
+ unsigned int ui[2];
+ unsigned short us[4];
+ } in_v, tmp_v, out_v;
+
+ in_v.ul = x;
+ tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
+
+ /* Since the bits of tmp_v.sh[3] are going to always be zero,
+ we don't have to bother to add that in. */
+ out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
+ + (unsigned long) tmp_v.us[2];
+
+ /* Similarly, out_v.us[2] is always zero for the final add. */
+ return out_v.us[0] + out_v.us[1];
}
/*
* csum_partial_copy - do IP checksumming and copy
*
* (C) Copyright 1996 Linus Torvalds
+ * accellerated versions (and 21264 assembly versions ) contributed by
+ * Rick Gorton <rick.gorton@alpha-processor.com>
*
* Don't look at this too closely - you'll go mad. The things
* we do for performance..
})
+static inline unsigned short from64to16(unsigned long x)
+{
+ /* Using extract instructions is a bit more efficient
+ than the original shift/bitmask version. */
+
+ union {
+ unsigned long ul;
+ unsigned int ui[2];
+ unsigned short us[4];
+ } in_v, tmp_v, out_v;
+
+ in_v.ul = x;
+ tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
+
+ /* Since the bits of tmp_v.sh[3] are going to always be zero,
+ we don't have to bother to add that in. */
+ out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
+ + (unsigned long) tmp_v.us[2];
+
+ /* Similarly, out_v.us[2] is always zero for the final add. */
+ return out_v.us[0] + out_v.us[1];
+}
+
+
+
/*
* Ok. This isn't fun, but this is the EASY case.
*/
soff, doff, len-8, checksum,
partial_dest, errp);
}
- /* 64 -> 33 bits */
- checksum = (checksum & 0xffffffff) + (checksum >> 32);
- /* 33 -> < 32 bits */
- checksum = (checksum & 0xffff) + (checksum >> 16);
- /* 32 -> 16 bits */
- checksum = (checksum & 0xffff) + (checksum >> 16);
- checksum = (checksum & 0xffff) + (checksum >> 16);
+ checksum = from64to16 (checksum);
}
return checksum;
}
--- /dev/null
+/*
+ * arch/alpha/lib/ev6-memchr.S
+ *
+ * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
+ *
+ * Finds characters in a memory area. Optimized for the Alpha:
+ *
+ * - memory accessed as aligned quadwords only
+ * - uses cmpbge to compare 8 bytes in parallel
+ * - does binary search to find 0 byte in last
+ * quadword (HAKMEM needed 12 instructions to
+ * do this instead of the 9 instructions that
+ * binary search needs).
+ *
+ * For correctness consider that:
+ *
+ * - only minimum number of quadwords may be accessed
+ * - the third argument is an unsigned long
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ * Compiler Writer's Guide for the Alpha 21264
+ * abbreviated as 'CWG' in other comments here
+ * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ * E - either cluster
+ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ * Try not to change the actual algorithm if possible for consistency.
+ */
+
+ .set noreorder
+ .set noat
+
+ .align 4
+ .globl memchr
+ .ent memchr
+memchr:
+ .frame $30,0,$26,0
+ .prologue 0
+
+ # Hack -- if someone passes in (size_t)-1, hoping to just
+ # search til the end of the address space, we will overflow
+ # below when we find the address of the last byte. Given
+ # that we will never have a 56-bit address space, cropping
+ # the length is the easiest way to avoid trouble.
+ zap $18, 0x80, $5 # U : Bound length
+ beq $18, $not_found # U :
+ ldq_u $1, 0($16) # L : load first quadword Latency=3
+ and $17, 0xff, $17 # E : L L U U : 00000000000000ch
+
+ insbl $17, 1, $2 # U : 000000000000ch00
+ cmpult $18, 9, $4 # E : small (< 1 quad) string?
+ or $2, $17, $17 # E : 000000000000chch
+ lda $3, -1($31) # E : U L L U
+
+ sll $17, 16, $2 # U : 00000000chch0000
+ addq $16, $5, $5 # E : Max search address
+ or $2, $17, $17 # E : 00000000chchchch
+ sll $17, 32, $2 # U : U L L U : chchchch00000000
+
+ or $2, $17, $17 # E : chchchchchchchch
+ extql $1, $16, $7 # U : $7 is upper bits
+ beq $4, $first_quad # U :
+ ldq_u $6, -1($5) # L : L U U L : eight or less bytes to search Latency=3
+
+ extqh $6, $16, $6 # U : 2 cycle stall for $6
+ mov $16, $0 # E :
+ nop # E :
+ or $7, $6, $1 # E : L U L U $1 = quadword starting at $16
+
+ # Deal with the case where at most 8 bytes remain to be searched
+ # in $1. E.g.:
+ # $18 = 6
+ # $1 = ????c6c5c4c3c2c1
+$last_quad:
+ negq $18, $6 # E :
+ xor $17, $1, $1 # E :
+ srl $3, $6, $6 # U : $6 = mask of $18 bits set
+ cmpbge $31, $1, $2 # E : L U L U
+
+ nop
+ nop
+ and $2, $6, $2 # E :
+ beq $2, $not_found # U : U L U L
+
+$found_it:
+#if defined(__alpha_fix__) && defined(__alpha_cix__)
+ /*
+ * Since we are guaranteed to have set one of the bits, we don't
+ * have to worry about coming back with a 0x40 out of cttz...
+ */
+ cttz $2, $3 # U0 :
+ addq $0, $3, $0 # E : All done
+ nop # E :
+ ret # L0 : L U L U
+#else
+ /*
+ * Slow and clunky. It can probably be improved.
+ * An exercise left for others.
+ */
+ negq $2, $3 # E :
+ and $2, $3, $2 # E :
+ and $2, 0x0f, $1 # E :
+ addq $0, 4, $3 # E :
+
+ cmoveq $1, $3, $0 # E : Latency 2, extra map cycle
+ nop # E : keep with cmov
+ and $2, 0x33, $1 # E :
+ addq $0, 2, $3 # E : U L U L : 2 cycle stall on $0
+
+ cmoveq $1, $3, $0 # E : Latency 2, extra map cycle
+ nop # E : keep with cmov
+ and $2, 0x55, $1 # E :
+ addq $0, 1, $3 # E : U L U L : 2 cycle stall on $0
+
+ cmoveq $1, $3, $0 # E : Latency 2, extra map cycle
+ nop
+ nop
+ ret # L0 : L U L U
+#endif
+
+ # Deal with the case where $18 > 8 bytes remain to be
+ # searched. $16 may not be aligned.
+ .align 4
+$first_quad:
+ andnot $16, 0x7, $0 # E :
+ insqh $3, $16, $2 # U : $2 = 0000ffffffffffff ($16<0:2> ff)
+ xor $1, $17, $1 # E :
+ or $1, $2, $1 # E : U L U L $1 = ====ffffffffffff
+
+ cmpbge $31, $1, $2 # E :
+ bne $2, $found_it # U :
+ # At least one byte left to process.
+ ldq $1, 8($0) # L :
+ subq $5, 1, $18 # E : U L U L
+
+ addq $0, 8, $0 # E :
+ # Make $18 point to last quad to be accessed (the
+ # last quad may or may not be partial).
+ andnot $18, 0x7, $18 # E :
+ cmpult $0, $18, $2 # E :
+ beq $2, $final # U : U L U L
+
+ # At least two quads remain to be accessed.
+
+ subq $18, $0, $4 # E : $4 <- nr quads to be processed
+ and $4, 8, $4 # E : odd number of quads?
+ bne $4, $odd_quad_count # U :
+ # At least three quads remain to be accessed
+ mov $1, $4 # E : L U L U : move prefetched value to correct reg
+
+ .align 4
+$unrolled_loop:
+ ldq $1, 8($0) # L : prefetch $1
+ xor $17, $4, $2 # E :
+ cmpbge $31, $2, $2 # E :
+ bne $2, $found_it # U : U L U L
+
+ addq $0, 8, $0 # E :
+ nop # E :
+ nop # E :
+ nop # E :
+
+$odd_quad_count:
+ xor $17, $1, $2 # E :
+ ldq $4, 8($0) # L : prefetch $4
+ cmpbge $31, $2, $2 # E :
+ addq $0, 8, $6 # E :
+
+ bne $2, $found_it # U :
+ cmpult $6, $18, $6 # E :
+ addq $0, 8, $0 # E :
+ nop # E :
+
+ bne $6, $unrolled_loop # U :
+ mov $4, $1 # E : move prefetched value into $1
+ nop # E :
+ nop # E :
+
+$final: subq $5, $0, $18 # E : $18 <- number of bytes left to do
+ nop # E :
+ nop # E :
+ bne $18, $last_quad # U :
+
+$not_found:
+ mov $31, $0 # E :
+ nop # E :
+ nop # E :
+ ret # L0 :
+
+ .end memchr
--- /dev/null
+/*
+ * arch/alpha/lib/ev6-memcpy.S
+ * 21264 version by Rick Gorton <rick.gorton@alpha-processor.com>
+ *
+ * Reasonably optimized memcpy() routine for the Alpha 21264
+ *
+ * - memory accessed as aligned quadwords only
+ * - uses bcmpge to compare 8 bytes in parallel
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ * Compiler Writer's Guide for the Alpha 21264
+ * abbreviated as 'CWG' in other comments here
+ * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ * E - either cluster
+ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ *
+ * Temp usage notes:
+ * $1,$2, - scratch
+ */
+
+ .set noreorder
+ .set noat
+
+ .align 4
+ .globl memcpy
+ .ent memcpy
+memcpy:
+ .frame $30,0,$26,0
+ .prologue 0
+
+ mov $16, $0 # E : copy dest to return
+ ble $18, $nomoredata # U : done with the copy?
+ xor $16, $17, $1 # E : are source and dest alignments the same?
+ and $1, 7, $1 # E : are they the same mod 8?
+
+ bne $1, $misaligned # U : Nope - gotta do this the slow way
+ /* source and dest are same mod 8 address */
+ and $16, 7, $1 # E : Are both 0mod8?
+ beq $1, $both_0mod8 # U : Yes
+ nop # E :
+
+ /*
+ * source and dest are same misalignment. move a byte at a time
+ * until a 0mod8 alignment for both is reached.
+ * At least one byte more to move
+ */
+
+$head_align:
+ ldbu $1, 0($17) # L : grab a byte
+ subq $18, 1, $18 # E : count--
+ addq $17, 1, $17 # E : src++
+ stb $1, 0($16) # L :
+ addq $16, 1, $16 # E : dest++
+ and $16, 7, $1 # E : Are we at 0mod8 yet?
+ ble $18, $nomoredata # U : done with the copy?
+ bne $1, $head_align # U :
+
+$both_0mod8:
+ cmple $18, 127, $1 # E : Can we unroll the loop?
+ bne $1, $no_unroll # U :
+ and $16, 63, $1 # E : get mod64 alignment
+ beq $1, $do_unroll # U : no single quads to fiddle
+
+$single_head_quad:
+ ldq $1, 0($17) # L : get 8 bytes
+ subq $18, 8, $18 # E : count -= 8
+ addq $17, 8, $17 # E : src += 8
+ nop # E :
+
+ stq $1, 0($16) # L : store
+ addq $16, 8, $16 # E : dest += 8
+ and $16, 63, $1 # E : get mod64 alignment
+ bne $1, $single_head_quad # U : still not fully aligned
+
+$do_unroll:
+ addq $16, 64, $7 # E : Initial (+1 trip) wh64 address
+ cmple $18, 63, $1 # E : Can we go through the unrolled loop?
+ bne $1, $tail_quads # U : Nope
+ nop # E :
+
+$unroll_body:
+ wh64 ($7) # L1 : memory subsystem hint: 64 bytes at
+ # ($7) are about to be over-written
+ ldq $6, 0($17) # L0 : bytes 0..7
+ nop # E :
+ nop # E :
+
+ ldq $4, 8($17) # L : bytes 8..15
+ ldq $5, 16($17) # L : bytes 16..23
+ addq $7, 64, $7 # E : Update next wh64 address
+ nop # E :
+
+ ldq $3, 24($17) # L : bytes 24..31
+ addq $16, 64, $1 # E : fallback value for wh64
+ nop # E :
+ nop # E :
+
+ addq $17, 32, $17 # E : src += 32 bytes
+ stq $6, 0($16) # L : bytes 0..7
+ nop # E :
+ nop # E :
+
+ stq $4, 8($16) # L : bytes 8..15
+ stq $5, 16($16) # L : bytes 16..23
+ subq $18, 192, $2 # E : At least two more trips to go?
+ nop # E :
+
+ stq $3, 24($16) # L : bytes 24..31
+ addq $16, 32, $16 # E : dest += 32 bytes
+ nop # E :
+ nop # E :
+
+ ldq $6, 0($17) # L : bytes 0..7
+ ldq $4, 8($17) # L : bytes 8..15
+ cmovlt $2, $1, $7 # E : Latency 2, extra map slot - Use
+ # fallback wh64 address if < 2 more trips
+ nop # E :
+
+ ldq $5, 16($17) # L : bytes 16..23
+ ldq $3, 24($17) # L : bytes 24..31
+ addq $16, 32, $16 # E : dest += 32
+ subq $18, 64, $18 # E : count -= 64
+
+ addq $17, 32, $17 # E : src += 32
+ stq $6, -32($16) # L : bytes 0..7
+ stq $4, -24($16) # L : bytes 8..15
+ cmple $18, 63, $1 # E : At least one more trip?
+
+ stq $5, -16($16) # L : bytes 16..23
+ stq $3, -8($16) # L : bytes 24..31
+ nop # E :
+ beq $1, $unroll_body
+
+$tail_quads:
+$no_unroll:
+ .align 4
+ subq $18, 8, $18 # E : At least a quad left?
+ blt $18, $less_than_8 # U : Nope
+ nop # E :
+ nop # E :
+
+$move_a_quad:
+ ldq $1, 0($17) # L : fetch 8
+ subq $18, 8, $18 # E : count -= 8
+ addq $17, 8, $17 # E : src += 8
+ nop # E :
+
+ stq $1, 0($16) # L : store 8
+ addq $16, 8, $16 # E : dest += 8
+ bge $18, $move_a_quad # U :
+ nop # E :
+
+$less_than_8:
+ .align 4
+ addq $18, 8, $18 # E : add back for trailing bytes
+ ble $18, $nomoredata # U : All-done
+ nop # E :
+ nop # E :
+
+ /* Trailing bytes */
+$tail_bytes:
+ subq $18, 1, $18 # E : count--
+ ldbu $1, 0($17) # L : fetch a byte
+ addq $17, 1, $17 # E : src++
+ nop # E :
+
+ stb $1, 0($16) # L : store a byte
+ addq $16, 1, $16 # E : dest++
+ bgt $18, $tail_bytes # U : more to be done?
+ nop # E :
+
+ /* branching to exit takes 3 extra cycles, so replicate exit here */
+ ret $31, ($26), 1 # L0 :
+ nop # E :
+ nop # E :
+ nop # E :
+
+$misaligned:
+ mov $0, $4 # E : dest temp
+ and $0, 7, $1 # E : dest alignment mod8
+ beq $1, $dest_0mod8 # U : life doesnt totally suck
+ nop
+
+$aligndest:
+ ble $18, $nomoredata # U :
+ ldbu $1, 0($17) # L : fetch a byte
+ subq $18, 1, $18 # E : count--
+ addq $17, 1, $17 # E : src++
+
+ stb $1, 0($4) # L : store it
+ addq $4, 1, $4 # E : dest++
+ and $4, 7, $1 # E : dest 0mod8 yet?
+ bne $1, $aligndest # U : go until we are aligned.
+
+ /* Source has unknown alignment, but dest is known to be 0mod8 */
+$dest_0mod8:
+ subq $18, 8, $18 # E : At least a quad left?
+ blt $18, $misalign_tail # U : Nope
+ ldq_u $3, 0($17) # L : seed (rotating load) of 8 bytes
+ nop # E :
+
+$mis_quad:
+ ldq_u $16, 8($17) # L : Fetch next 8
+ extql $3, $17, $3 # U : masking
+ extqh $16, $17, $1 # U : masking
+ bis $3, $1, $1 # E : merged bytes to store
+
+ subq $18, 8, $18 # E : count -= 8
+ addq $17, 8, $17 # E : src += 8
+ stq $1, 0($4) # L : store 8 (aligned)
+ mov $16, $3 # E : "rotate" source data
+
+ addq $4, 8, $4 # E : dest += 8
+ bge $18, $mis_quad # U : More quads to move
+ nop
+ nop
+
+$misalign_tail:
+ addq $18, 8, $18 # E : account for tail stuff
+ ble $18, $nomoredata # U :
+ nop
+ nop
+
+$misalign_byte:
+ ldbu $1, 0($17) # L : fetch 1
+ subq $18, 1, $18 # E : count--
+ addq $17, 1, $17 # E : src++
+ nop # E :
+
+ stb $1, 0($4) # L : store
+ addq $4, 1, $4 # E : dest++
+ bgt $18, $misalign_byte # U : more to go?
+ nop
+
+
+$nomoredata:
+ ret $31, ($26), 1 # L0 :
+ nop # E :
+ nop # E :
+ nop # E :
+
+ .end memcpy
+
+/* For backwards module compatability. */
+__memcpy = memcpy
+.globl __memcpy
--- /dev/null
+/*
+ * arch/alpha/lib/ev6-memset.S
+ *
+ * This is an efficient (and relatively small) implementation of the C library
+ * "memset()" function for the 21264 implementation of Alpha.
+ *
+ * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ * Compiler Writer's Guide for the Alpha 21264
+ * abbreviated as 'CWG' in other comments here
+ * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ * E - either cluster
+ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ * The algorithm for the leading and trailing quadwords remains the same,
+ * however the loop has been unrolled to enable better memory throughput,
+ * and the code has been replicated for each of the entry points: __memset
+ * and __memsetw to permit better scheduling to eliminate the stalling
+ * encountered during the mask replication.
+ * A future enhancement might be to put in a byte store loop for really
+ * small (say < 32 bytes) memset()s. Whether or not that change would be
+ * a win in the kernel would depend upon the contextual usage.
+ * WARNING: Maintaining this is going to be more work than the above version,
+ * as fixes will need to be made in multiple places. The performance gain
+ * is worth it.
+ */
+
+ .set noat
+ .set noreorder
+.text
+ .globl __memset
+ .globl __memsetw
+ .globl __constant_c_memset
+ .globl memset
+
+ .ent __memset
+.align 5
+__memset:
+memset:
+ .frame $30,0,$26,0
+ .prologue 0
+
+ /*
+ * Serious stalling happens. The only way to mitigate this is to
+ * undertake a major re-write to interleave the constant materialization
+ * with other parts of the fall-through code. This is important, even
+ * though it makes maintenance tougher.
+ * Do this later.
+ */
+ and $17,255,$1 # E : 00000000000000ch
+ insbl $17,1,$2 # U : 000000000000ch00
+ bis $16,$16,$0 # E : return value
+ ble $18,end_b # U : zero length requested?
+
+ addq $18,$16,$6 # E : max address to write to
+ bis $1,$2,$17 # E : 000000000000chch
+ insbl $1,2,$3 # U : 0000000000ch0000
+ insbl $1,3,$4 # U : 00000000ch000000
+
+ or $3,$4,$3 # E : 00000000chch0000
+ inswl $17,4,$5 # U : 0000chch00000000
+ xor $16,$6,$1 # E : will complete write be within one quadword?
+ inswl $17,6,$2 # U : chch000000000000
+
+ or $17,$3,$17 # E : 00000000chchchch
+ or $2,$5,$2 # E : chchchch00000000
+ bic $1,7,$1 # E : fit within a single quadword?
+ and $16,7,$3 # E : Target addr misalignment
+
+ or $17,$2,$17 # E : chchchchchchchch
+ beq $1,within_quad_b # U :
+ nop # E :
+ beq $3,aligned_b # U : target is 0mod8
+
+ /*
+ * Target address is misaligned, and won't fit within a quadword
+ */
+ ldq_u $4,0($16) # L : Fetch first partial
+ bis $16,$16,$5 # E : Save the address
+ insql $17,$16,$2 # U : Insert new bytes
+ subq $3,8,$3 # E : Invert (for addressing uses)
+
+ addq $18,$3,$18 # E : $18 is new count ($3 is negative)
+ mskql $4,$16,$4 # U : clear relevant parts of the quad
+ subq $16,$3,$16 # E : $16 is new aligned destination
+ bis $2,$4,$1 # E : Final bytes
+
+ nop
+ stq_u $1,0($5) # L : Store result
+ nop
+ nop
+
+.align 4
+aligned_b:
+ /*
+ * We are now guaranteed to be quad aligned, with at least
+ * one partial quad to write.
+ */
+
+ sra $18,3,$3 # U : Number of remaining quads to write
+ and $18,7,$18 # E : Number of trailing bytes to write
+ bis $16,$16,$5 # E : Save dest address
+ beq $3,no_quad_b # U : tail stuff only
+
+ /*
+ * it's worth the effort to unroll this and use wh64 if possible
+ * Lifted a bunch of code from clear_user.S
+ * At this point, entry values are:
+ * $16 Current destination address
+ * $5 A copy of $16
+ * $6 The max quadword address to write to
+ * $18 Number trailer bytes
+ * $3 Number quads to write
+ */
+
+ and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop)
+ subq $3, 16, $4 # E : Only try to unroll if > 128 bytes
+ subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64)
+ blt $4, loop_b # U :
+
+ /*
+ * We know we've got at least 16 quads, minimum of one trip
+ * through unrolled loop. Do a quad at a time to get us 0mod64
+ * aligned.
+ */
+
+ nop # E :
+ nop # E :
+ nop # E :
+ beq $1, $bigalign_b # U :
+
+$alignmod64_b:
+ stq $17, 0($5) # L :
+ subq $3, 1, $3 # E : For consistency later
+ addq $1, 8, $1 # E : Increment towards zero for alignment
+ addq $5, 8, $4 # E : Initial wh64 address (filler instruction)
+
+ nop
+ nop
+ addq $5, 8, $5 # E : Inc address
+ blt $1, $alignmod64_b # U :
+
+$bigalign_b:
+ /*
+ * $3 - number quads left to go
+ * $5 - target address (aligned 0mod64)
+ * $17 - mask of stuff to store
+ * Scratch registers available: $7, $2, $4, $1
+ * we know that we'll be taking a minimum of one trip through
+ * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle
+ * Assumes the wh64 needs to be for 2 trips through the loop in the future
+ * The wh64 is issued on for the starting destination address for trip +2
+ * through the loop, and if there are less than two trips left, the target
+ * address will be for the current trip.
+ */
+
+$do_wh64_b:
+ wh64 ($4) # L1 : memory subsystem write hint
+ subq $3, 24, $2 # E : For determining future wh64 addresses
+ stq $17, 0($5) # L :
+ nop # E :
+
+ addq $5, 128, $4 # E : speculative target of next wh64
+ stq $17, 8($5) # L :
+ stq $17, 16($5) # L :
+ addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr)
+
+ stq $17, 24($5) # L :
+ stq $17, 32($5) # L :
+ cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle
+ nop
+
+ stq $17, 40($5) # L :
+ stq $17, 48($5) # L :
+ subq $3, 16, $2 # E : Repeat the loop at least once more?
+ nop
+
+ stq $17, 56($5) # L :
+ addq $5, 64, $5 # E :
+ subq $3, 8, $3 # E :
+ bge $2, $do_wh64_b # U :
+
+ nop
+ nop
+ nop
+ beq $3, no_quad_b # U : Might have finished already
+
+.align 4
+ /*
+ * Simple loop for trailing quadwords, or for small amounts
+ * of data (where we can't use an unrolled loop and wh64)
+ */
+loop_b:
+ stq $17,0($5) # L :
+ subq $3,1,$3 # E : Decrement number quads left
+ addq $5,8,$5 # E : Inc address
+ bne $3,loop_b # U : more?
+
+no_quad_b:
+ /*
+ * Write 0..7 trailing bytes.
+ */
+ nop # E :
+ beq $18,end_b # U : All done?
+ ldq $7,0($5) # L :
+ mskqh $7,$6,$2 # U : Mask final quad
+
+ insqh $17,$6,$4 # U : New bits
+ bis $2,$4,$1 # E : Put it all together
+ stq $1,0($5) # L : And back to memory
+ ret $31,($26),1 # L0 :
+
+within_quad_b:
+ ldq_u $1,0($16) # L :
+ insql $17,$16,$2 # U : New bits
+ mskql $1,$16,$4 # U : Clear old
+ bis $2,$4,$2 # E : New result
+
+ mskql $2,$6,$4 # U :
+ mskqh $1,$6,$2 # U :
+ bis $2,$4,$1 # E :
+ stq_u $1,0($16) # L :
+
+end_b:
+ nop
+ nop
+ nop
+ ret $31,($26),1 # L0 :
+ .end __memset
+
+ /*
+ * This is the original body of code, prior to replication and
+ * rescheduling. Leave it here, as there may be calls to this
+ * entry point.
+ */
+.align 4
+ .ent __memset
+__constant_c_memset:
+ .frame $30,0,$26,0
+ .prologue 0
+
+ addq $18,$16,$6 # E : max address to write to
+ bis $16,$16,$0 # E : return value
+ xor $16,$6,$1 # E : will complete write be within one quadword?
+ ble $18,end # U : zero length requested?
+
+ bic $1,7,$1 # E : fit within a single quadword
+ beq $1,within_one_quad # U :
+ and $16,7,$3 # E : Target addr misalignment
+ beq $3,aligned # U : target is 0mod8
+
+ /*
+ * Target address is misaligned, and won't fit within a quadword
+ */
+ ldq_u $4,0($16) # L : Fetch first partial
+ bis $16,$16,$5 # E : Save the address
+ insql $17,$16,$2 # U : Insert new bytes
+ subq $3,8,$3 # E : Invert (for addressing uses)
+
+ addq $18,$3,$18 # E : $18 is new count ($3 is negative)
+ mskql $4,$16,$4 # U : clear relevant parts of the quad
+ subq $16,$3,$16 # E : $16 is new aligned destination
+ bis $2,$4,$1 # E : Final bytes
+
+ nop
+ stq_u $1,0($5) # L : Store result
+ nop
+ nop
+
+.align 4
+aligned:
+ /*
+ * We are now guaranteed to be quad aligned, with at least
+ * one partial quad to write.
+ */
+
+ sra $18,3,$3 # U : Number of remaining quads to write
+ and $18,7,$18 # E : Number of trailing bytes to write
+ bis $16,$16,$5 # E : Save dest address
+ beq $3,no_quad # U : tail stuff only
+
+ /*
+ * it's worth the effort to unroll this and use wh64 if possible
+ * Lifted a bunch of code from clear_user.S
+ * At this point, entry values are:
+ * $16 Current destination address
+ * $5 A copy of $16
+ * $6 The max quadword address to write to
+ * $18 Number trailer bytes
+ * $3 Number quads to write
+ */
+
+ and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop)
+ subq $3, 16, $4 # E : Only try to unroll if > 128 bytes
+ subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64)
+ blt $4, loop # U :
+
+ /*
+ * We know we've got at least 16 quads, minimum of one trip
+ * through unrolled loop. Do a quad at a time to get us 0mod64
+ * aligned.
+ */
+
+ nop # E :
+ nop # E :
+ nop # E :
+ beq $1, $bigalign # U :
+
+$alignmod64:
+ stq $17, 0($5) # L :
+ subq $3, 1, $3 # E : For consistency later
+ addq $1, 8, $1 # E : Increment towards zero for alignment
+ addq $5, 8, $4 # E : Initial wh64 address (filler instruction)
+
+ nop
+ nop
+ addq $5, 8, $5 # E : Inc address
+ blt $1, $alignmod64 # U :
+
+$bigalign:
+ /*
+ * $3 - number quads left to go
+ * $5 - target address (aligned 0mod64)
+ * $17 - mask of stuff to store
+ * Scratch registers available: $7, $2, $4, $1
+ * we know that we'll be taking a minimum of one trip through
+ * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle
+ * Assumes the wh64 needs to be for 2 trips through the loop in the future
+ * The wh64 is issued on for the starting destination address for trip +2
+ * through the loop, and if there are less than two trips left, the target
+ * address will be for the current trip.
+ */
+
+$do_wh64:
+ wh64 ($4) # L1 : memory subsystem write hint
+ subq $3, 24, $2 # E : For determining future wh64 addresses
+ stq $17, 0($5) # L :
+ nop # E :
+
+ addq $5, 128, $4 # E : speculative target of next wh64
+ stq $17, 8($5) # L :
+ stq $17, 16($5) # L :
+ addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr)
+
+ stq $17, 24($5) # L :
+ stq $17, 32($5) # L :
+ cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle
+ nop
+
+ stq $17, 40($5) # L :
+ stq $17, 48($5) # L :
+ subq $3, 16, $2 # E : Repeat the loop at least once more?
+ nop
+
+ stq $17, 56($5) # L :
+ addq $5, 64, $5 # E :
+ subq $3, 8, $3 # E :
+ bge $2, $do_wh64 # U :
+
+ nop
+ nop
+ nop
+ beq $3, no_quad # U : Might have finished already
+
+.align 4
+ /*
+ * Simple loop for trailing quadwords, or for small amounts
+ * of data (where we can't use an unrolled loop and wh64)
+ */
+loop:
+ stq $17,0($5) # L :
+ subq $3,1,$3 # E : Decrement number quads left
+ addq $5,8,$5 # E : Inc address
+ bne $3,loop # U : more?
+
+no_quad:
+ /*
+ * Write 0..7 trailing bytes.
+ */
+ nop # E :
+ beq $18,end # U : All done?
+ ldq $7,0($5) # L :
+ mskqh $7,$6,$2 # U : Mask final quad
+
+ insqh $17,$6,$4 # U : New bits
+ bis $2,$4,$1 # E : Put it all together
+ stq $1,0($5) # L : And back to memory
+ ret $31,($26),1 # L0 :
+
+within_one_quad:
+ ldq_u $1,0($16) # L :
+ insql $17,$16,$2 # U : New bits
+ mskql $1,$16,$4 # U : Clear old
+ bis $2,$4,$2 # E : New result
+
+ mskql $2,$6,$4 # U :
+ mskqh $1,$6,$2 # U :
+ bis $2,$4,$1 # E :
+ stq_u $1,0($16) # L :
+
+end:
+ nop
+ nop
+ nop
+ ret $31,($26),1 # L0 :
+ .end __constant_c_memset
+
+ /*
+ * This is a replicant of the __constant_c_memset code, rescheduled
+ * to mask stalls. Note that entry point names also had to change
+ */
+ .align 5
+ .ent __memsetw
+
+__memsetw:
+ .frame $30,0,$26,0
+ .prologue 0
+
+ inswl $17,0,$5 # U : 000000000000c1c2
+ inswl $17,2,$2 # U : 00000000c1c20000
+ bis $16,$16,$0 # E : return value
+ addq $18,$16,$6 # E : max address to write to
+
+ ble $18, end_w # U : zero length requested?
+ inswl $17,4,$3 # U : 0000c1c200000000
+ inswl $17,6,$4 # U : c1c2000000000000
+ xor $16,$6,$1 # E : will complete write be within one quadword?
+
+ or $2,$5,$2 # E : 00000000c1c2c1c2
+ or $3,$4,$17 # E : c1c2c1c200000000
+ bic $1,7,$1 # E : fit within a single quadword
+ and $16,7,$3 # E : Target addr misalignment
+
+ or $17,$2,$17 # E : c1c2c1c2c1c2c1c2
+ beq $1,within_quad_w # U :
+ nop
+ beq $3,aligned_w # U : target is 0mod8
+
+ /*
+ * Target address is misaligned, and won't fit within a quadword
+ */
+ ldq_u $4,0($16) # L : Fetch first partial
+ bis $16,$16,$5 # E : Save the address
+ insql $17,$16,$2 # U : Insert new bytes
+ subq $3,8,$3 # E : Invert (for addressing uses)
+
+ addq $18,$3,$18 # E : $18 is new count ($3 is negative)
+ mskql $4,$16,$4 # U : clear relevant parts of the quad
+ subq $16,$3,$16 # E : $16 is new aligned destination
+ bis $2,$4,$1 # E : Final bytes
+
+ nop
+ stq_u $1,0($5) # L : Store result
+ nop
+ nop
+
+.align 4
+aligned_w:
+ /*
+ * We are now guaranteed to be quad aligned, with at least
+ * one partial quad to write.
+ */
+
+ sra $18,3,$3 # U : Number of remaining quads to write
+ and $18,7,$18 # E : Number of trailing bytes to write
+ bis $16,$16,$5 # E : Save dest address
+ beq $3,no_quad_w # U : tail stuff only
+
+ /*
+ * it's worth the effort to unroll this and use wh64 if possible
+ * Lifted a bunch of code from clear_user.S
+ * At this point, entry values are:
+ * $16 Current destination address
+ * $5 A copy of $16
+ * $6 The max quadword address to write to
+ * $18 Number trailer bytes
+ * $3 Number quads to write
+ */
+
+ and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop)
+ subq $3, 16, $4 # E : Only try to unroll if > 128 bytes
+ subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64)
+ blt $4, loop_w # U :
+
+ /*
+ * We know we've got at least 16 quads, minimum of one trip
+ * through unrolled loop. Do a quad at a time to get us 0mod64
+ * aligned.
+ */
+
+ nop # E :
+ nop # E :
+ nop # E :
+ beq $1, $bigalign_w # U :
+
+$alignmod64_w:
+ stq $17, 0($5) # L :
+ subq $3, 1, $3 # E : For consistency later
+ addq $1, 8, $1 # E : Increment towards zero for alignment
+ addq $5, 8, $4 # E : Initial wh64 address (filler instruction)
+
+ nop
+ nop
+ addq $5, 8, $5 # E : Inc address
+ blt $1, $alignmod64_w # U :
+
+$bigalign_w:
+ /*
+ * $3 - number quads left to go
+ * $5 - target address (aligned 0mod64)
+ * $17 - mask of stuff to store
+ * Scratch registers available: $7, $2, $4, $1
+ * we know that we'll be taking a minimum of one trip through
+ * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle
+ * Assumes the wh64 needs to be for 2 trips through the loop in the future
+ * The wh64 is issued on for the starting destination address for trip +2
+ * through the loop, and if there are less than two trips left, the target
+ * address will be for the current trip.
+ */
+
+$do_wh64_w:
+ wh64 ($4) # L1 : memory subsystem write hint
+ subq $3, 24, $2 # E : For determining future wh64 addresses
+ stq $17, 0($5) # L :
+ nop # E :
+
+ addq $5, 128, $4 # E : speculative target of next wh64
+ stq $17, 8($5) # L :
+ stq $17, 16($5) # L :
+ addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr)
+
+ stq $17, 24($5) # L :
+ stq $17, 32($5) # L :
+ cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle
+ nop
+
+ stq $17, 40($5) # L :
+ stq $17, 48($5) # L :
+ subq $3, 16, $2 # E : Repeat the loop at least once more?
+ nop
+
+ stq $17, 56($5) # L :
+ addq $5, 64, $5 # E :
+ subq $3, 8, $3 # E :
+ bge $2, $do_wh64_w # U :
+
+ nop
+ nop
+ nop
+ beq $3, no_quad_w # U : Might have finished already
+
+.align 4
+ /*
+ * Simple loop for trailing quadwords, or for small amounts
+ * of data (where we can't use an unrolled loop and wh64)
+ */
+loop_w:
+ stq $17,0($5) # L :
+ subq $3,1,$3 # E : Decrement number quads left
+ addq $5,8,$5 # E : Inc address
+ bne $3,loop_w # U : more?
+
+no_quad_w:
+ /*
+ * Write 0..7 trailing bytes.
+ */
+ nop # E :
+ beq $18,end_w # U : All done?
+ ldq $7,0($5) # L :
+ mskqh $7,$6,$2 # U : Mask final quad
+
+ insqh $17,$6,$4 # U : New bits
+ bis $2,$4,$1 # E : Put it all together
+ stq $1,0($5) # L : And back to memory
+ ret $31,($26),1 # L0 :
+
+within_quad_w:
+ ldq_u $1,0($16) # L :
+ insql $17,$16,$2 # U : New bits
+ mskql $1,$16,$4 # U : Clear old
+ bis $2,$4,$2 # E : New result
+
+ mskql $2,$6,$4 # U :
+ mskqh $1,$6,$2 # U :
+ bis $2,$4,$1 # E :
+ stq_u $1,0($16) # L :
+
+end_w:
+ nop
+ nop
+ nop
+ ret $31,($26),1 # L0 :
+
+ .end __memsetw
+++ /dev/null
-/*
- * arch/alpha/lib/strcpy.S
- * Contributed by Richard Henderson (rth@tamu.edu)
- *
- * Copy a null-terminated string from SRC to DST. Return a pointer
- * to the null-terminator in the source.
- */
-
- .text
-
- .align 4
- .globl strcpy
- .ent strcpy
-strcpy:
- .frame $30, 0, $26
- .prologue 0
-
- mov $16, $0 # set up return value
- mov $26, $23 # set up return address
- br __stxcpy # do the copy
- nop
-
- .end strcpy
+++ /dev/null
-/*
- * arch/alpha/lib/strncpy.S
- * Contributed by Richard Henderson (rth@tamu.edu)
- *
- * Copy no more than COUNT bytes of the null-terminated string from
- * SRC to DST. If SRC does not cover all of COUNT, the balance is
- * zeroed.
- *
- * Or, rather, if the kernel cared about that weird ANSI quirk. This
- * version has cropped that bit o' nastiness as well as assuming that
- * __stxncpy is in range of a branch.
- */
-
- .set noat
- .set noreorder
-
- .text
-
- .align 4
- .globl strncpy
- .ent strncpy
-strncpy:
- .frame $30, 0, $26
- .prologue 0
-
- mov $16, $0 # set return value now
- beq $18, 0f
- mov $26, $23 # set return address
- br __stxncpy # do the work of the copy
-
-0: ret
- nop
- nop
- nop
-
- .end strncpy
--- /dev/null
+/*
+ * arch/alpha/lib/ev6-stxcpy.S
+ * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
+ *
+ * Copy a null-terminated string from SRC to DST.
+ *
+ * This is an internal routine used by strcpy, stpcpy, and strcat.
+ * As such, it uses special linkage conventions to make implementation
+ * of these public functions more efficient.
+ *
+ * On input:
+ * t9 = return address
+ * a0 = DST
+ * a1 = SRC
+ *
+ * On output:
+ * t12 = bitmask (with one bit set) indicating the last byte written
+ * a0 = unaligned address of the last *word* written
+ *
+ * Furthermore, v0, a3-a5, t11, and t12 are untouched.
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ * Compiler Writer's Guide for the Alpha 21264
+ * abbreviated as 'CWG' in other comments here
+ * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ * E - either cluster
+ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ * Try not to change the actual algorithm if possible for consistency.
+ */
+
+#include <alpha/regdef.h>
+
+ .set noat
+ .set noreorder
+
+ .text
+
+/* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
+ doesn't like putting the entry point for a procedure somewhere in the
+ middle of the procedure descriptor. Work around this by putting the
+ aligned copy in its own procedure descriptor */
+
+
+ .ent stxcpy_aligned
+ .align 4
+stxcpy_aligned:
+ .frame sp, 0, t9
+ .prologue 0
+
+ /* On entry to this basic block:
+ t0 == the first destination word for masking back in
+ t1 == the first source word. */
+
+ /* Create the 1st output word and detect 0's in the 1st input word. */
+ lda t2, -1 # E : build a mask against false zero
+ mskqh t2, a1, t2 # U : detection in the src word (stall)
+ mskqh t1, a1, t3 # U :
+ ornot t1, t2, t2 # E : (stall)
+
+ mskql t0, a1, t0 # U : assemble the first output word
+ cmpbge zero, t2, t8 # E : bits set iff null found
+ or t0, t3, t1 # E : (stall)
+ bne t8, $a_eos # U : (stall)
+
+ /* On entry to this basic block:
+ t0 == the first destination word for masking back in
+ t1 == a source word not containing a null. */
+ /* Nops here to separate store quads from load quads */
+
+$a_loop:
+ stq_u t1, 0(a0) # L :
+ addq a0, 8, a0 # E :
+ nop
+ nop
+
+ ldq_u t1, 0(a1) # L : Latency=3
+ addq a1, 8, a1 # E :
+ cmpbge zero, t1, t8 # E : (3 cycle stall)
+ beq t8, $a_loop # U : (stall for t8)
+
+ /* Take care of the final (partial) word store.
+ On entry to this basic block we have:
+ t1 == the source word containing the null
+ t8 == the cmpbge mask that found it. */
+$a_eos:
+ negq t8, t6 # E : find low bit set
+ and t8, t6, t12 # E : (stall)
+ /* For the sake of the cache, don't read a destination word
+ if we're not going to need it. */
+ and t12, 0x80, t6 # E : (stall)
+ bne t6, 1f # U : (stall)
+
+ /* We're doing a partial word store and so need to combine
+ our source and original destination words. */
+ ldq_u t0, 0(a0) # L : Latency=3
+ subq t12, 1, t6 # E :
+ zapnot t1, t6, t1 # U : clear src bytes >= null (stall)
+ or t12, t6, t8 # E : (stall)
+
+ zap t0, t8, t0 # E : clear dst bytes <= null
+ or t0, t1, t1 # E : (stall)
+ nop
+ nop
+
+1: stq_u t1, 0(a0) # L :
+ ret (t9) # L0 : Latency=3
+ nop
+ nop
+
+ .end stxcpy_aligned
+
+ .align 4
+ .ent __stxcpy
+ .globl __stxcpy
+__stxcpy:
+ .frame sp, 0, t9
+ .prologue 0
+
+ /* Are source and destination co-aligned? */
+ xor a0, a1, t0 # E :
+ unop # E :
+ and t0, 7, t0 # E : (stall)
+ bne t0, $unaligned # U : (stall)
+
+ /* We are co-aligned; take care of a partial first word. */
+ ldq_u t1, 0(a1) # L : load first src word
+ and a0, 7, t0 # E : take care not to load a word ...
+ addq a1, 8, a1 # E :
+ beq t0, stxcpy_aligned # U : ... if we wont need it (stall)
+
+ ldq_u t0, 0(a0) # L :
+ br stxcpy_aligned # L0 : Latency=3
+ nop
+ nop
+
+
+/* The source and destination are not co-aligned. Align the destination
+ and cope. We have to be very careful about not reading too much and
+ causing a SEGV. */
+
+ .align 4
+$u_head:
+ /* We know just enough now to be able to assemble the first
+ full source word. We can still find a zero at the end of it
+ that prevents us from outputting the whole thing.
+
+ On entry to this basic block:
+ t0 == the first dest word, for masking back in, if needed else 0
+ t1 == the low bits of the first source word
+ t6 == bytemask that is -1 in dest word bytes */
+
+ ldq_u t2, 8(a1) # L :
+ addq a1, 8, a1 # E :
+ extql t1, a1, t1 # U : (stall on a1)
+ extqh t2, a1, t4 # U : (stall on a1)
+
+ mskql t0, a0, t0 # U :
+ or t1, t4, t1 # E :
+ mskqh t1, a0, t1 # U : (stall on t1)
+ or t0, t1, t1 # E : (stall on t1)
+
+ or t1, t6, t6 # E :
+ cmpbge zero, t6, t8 # E : (stall)
+ lda t6, -1 # E : for masking just below
+ bne t8, $u_final # U : (stall)
+
+ mskql t6, a1, t6 # U : mask out the bits we have
+ or t6, t2, t2 # E : already extracted before (stall)
+ cmpbge zero, t2, t8 # E : testing eos (stall)
+ bne t8, $u_late_head_exit # U : (stall)
+
+ /* Finally, we've got all the stupid leading edge cases taken care
+ of and we can set up to enter the main loop. */
+
+ stq_u t1, 0(a0) # L : store first output word
+ addq a0, 8, a0 # E :
+ extql t2, a1, t0 # U : position ho-bits of lo word
+ ldq_u t2, 8(a1) # U : read next high-order source word
+
+ addq a1, 8, a1 # E :
+ cmpbge zero, t2, t8 # E : (stall for t2)
+ nop # E :
+ bne t8, $u_eos # U : (stall)
+
+ /* Unaligned copy main loop. In order to avoid reading too much,
+ the loop is structured to detect zeros in aligned source words.
+ This has, unfortunately, effectively pulled half of a loop
+ iteration out into the head and half into the tail, but it does
+ prevent nastiness from accumulating in the very thing we want
+ to run as fast as possible.
+
+ On entry to this basic block:
+ t0 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word
+
+ We further know that t2 does not contain a null terminator. */
+
+ .align 3
+$u_loop:
+ extqh t2, a1, t1 # U : extract high bits for current word
+ addq a1, 8, a1 # E : (stall)
+ extql t2, a1, t3 # U : extract low bits for next time (stall)
+ addq a0, 8, a0 # E :
+
+ or t0, t1, t1 # E : current dst word now complete
+ ldq_u t2, 0(a1) # L : Latency=3 load high word for next time
+ stq_u t1, -8(a0) # L : save the current word (stall)
+ mov t3, t0 # E :
+
+ cmpbge zero, t2, t8 # E : test new word for eos
+ beq t8, $u_loop # U : (stall)
+ nop
+ nop
+
+ /* We've found a zero somewhere in the source word we just read.
+ If it resides in the lower half, we have one (probably partial)
+ word to write out, and if it resides in the upper half, we
+ have one full and one partial word left to write out.
+
+ On entry to this basic block:
+ t0 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word. */
+$u_eos:
+ extqh t2, a1, t1 # U :
+ or t0, t1, t1 # E : first (partial) source word complete (stall)
+ cmpbge zero, t1, t8 # E : is the null in this first bit? (stall)
+ bne t8, $u_final # U : (stall)
+
+$u_late_head_exit:
+ stq_u t1, 0(a0) # L : the null was in the high-order bits
+ addq a0, 8, a0 # E :
+ extql t2, a1, t1 # U :
+ cmpbge zero, t1, t8 # E : (stall)
+
+ /* Take care of a final (probably partial) result word.
+ On entry to this basic block:
+ t1 == assembled source word
+ t8 == cmpbge mask that found the null. */
+$u_final:
+ negq t8, t6 # E : isolate low bit set
+ and t6, t8, t12 # E : (stall)
+ and t12, 0x80, t6 # E : avoid dest word load if we can (stall)
+ bne t6, 1f # U : (stall)
+
+ ldq_u t0, 0(a0) # E :
+ subq t12, 1, t6 # E :
+ or t6, t12, t8 # E : (stall)
+ zapnot t1, t6, t1 # U : kill source bytes >= null (stall)
+
+ zap t0, t8, t0 # U : kill dest bytes <= null (2 cycle data stall)
+ or t0, t1, t1 # E : (stall)
+ nop
+ nop
+
+1: stq_u t1, 0(a0) # L :
+ ret (t9) # L0 : Latency=3
+ nop
+ nop
+
+ /* Unaligned copy entry point. */
+ .align 4
+$unaligned:
+
+ ldq_u t1, 0(a1) # L : load first source word
+ and a0, 7, t4 # E : find dest misalignment
+ and a1, 7, t5 # E : find src misalignment
+ /* Conditionally load the first destination word and a bytemask
+ with 0xff indicating that the destination byte is sacrosanct. */
+ mov zero, t0 # E :
+
+ mov zero, t6 # E :
+ beq t4, 1f # U :
+ ldq_u t0, 0(a0) # L :
+ lda t6, -1 # E :
+
+ mskql t6, a0, t6 # U :
+ nop
+ nop
+ nop
+1:
+ subq a1, t4, a1 # E : sub dest misalignment from src addr
+ /* If source misalignment is larger than dest misalignment, we need
+ extra startup checks to avoid SEGV. */
+ cmplt t4, t5, t12 # E :
+ beq t12, $u_head # U :
+ lda t2, -1 # E : mask out leading garbage in source
+
+ mskqh t2, t5, t2 # U :
+ ornot t1, t2, t3 # E : (stall)
+ cmpbge zero, t3, t8 # E : is there a zero? (stall)
+ beq t8, $u_head # U : (stall)
+
+ /* At this point we've found a zero in the first partial word of
+ the source. We need to isolate the valid source data and mask
+ it into the original destination data. (Incidentally, we know
+ that we'll need at least one byte of that original dest word.) */
+
+ ldq_u t0, 0(a0) # L :
+ negq t8, t6 # E : build bitmask of bytes <= zero
+ and t6, t8, t12 # E : (stall)
+ and a1, 7, t5 # E :
+
+ subq t12, 1, t6 # E :
+ or t6, t12, t8 # E : (stall)
+ srl t12, t5, t12 # U : adjust final null return value
+ zapnot t2, t8, t2 # U : prepare source word; mirror changes (stall)
+
+ and t1, t2, t1 # E : to source validity mask
+ extql t2, a1, t2 # U :
+ extql t1, a1, t1 # U : (stall)
+ andnot t0, t2, t0 # .. e1 : zero place for source to reside (stall)
+
+ or t0, t1, t1 # e1 : and put it there
+ stq_u t1, 0(a0) # .. e0 : (stall)
+ ret (t9) # e1 :
+ nop
+
+ .end __stxcpy
+
--- /dev/null
+/*
+ * arch/alpha/lib/ev6-stxncpy.S
+ * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com>
+ *
+ * Copy no more than COUNT bytes of the null-terminated string from
+ * SRC to DST.
+ *
+ * This is an internal routine used by strncpy, stpncpy, and strncat.
+ * As such, it uses special linkage conventions to make implementation
+ * of these public functions more efficient.
+ *
+ * On input:
+ * t9 = return address
+ * a0 = DST
+ * a1 = SRC
+ * a2 = COUNT
+ *
+ * Furthermore, COUNT may not be zero.
+ *
+ * On output:
+ * t0 = last word written
+ * t10 = bitmask (with one bit set) indicating the byte position of
+ * the end of the range specified by COUNT
+ * t12 = bitmask (with one bit set) indicating the last byte written
+ * a0 = unaligned address of the last *word* written
+ * a2 = the number of full words left in COUNT
+ *
+ * Furthermore, v0, a3-a5, t11, t12, and $at are untouched.
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ * Compiler Writer's Guide for the Alpha 21264
+ * abbreviated as 'CWG' in other comments here
+ * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ * E - either cluster
+ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ * Try not to change the actual algorithm if possible for consistency.
+ */
+
+#include <alpha/regdef.h>
+
+ .set noat
+ .set noreorder
+
+ .text
+
+/* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that
+ doesn't like putting the entry point for a procedure somewhere in the
+ middle of the procedure descriptor. Work around this by putting the
+ aligned copy in its own procedure descriptor */
+
+
+ .ent stxncpy_aligned
+ .align 4
+stxncpy_aligned:
+ .frame sp, 0, t9, 0
+ .prologue 0
+
+ /* On entry to this basic block:
+ t0 == the first destination word for masking back in
+ t1 == the first source word. */
+
+ /* Create the 1st output word and detect 0's in the 1st input word. */
+ lda t2, -1 # E : build a mask against false zero
+ mskqh t2, a1, t2 # U : detection in the src word (stall)
+ mskqh t1, a1, t3 # U :
+ ornot t1, t2, t2 # E : (stall)
+
+ mskql t0, a1, t0 # U : assemble the first output word
+ cmpbge zero, t2, t8 # E : bits set iff null found
+ or t0, t3, t0 # E : (stall)
+ beq a2, $a_eoc # U :
+
+ bne t8, $a_eos # U :
+ nop
+ nop
+ nop
+
+ /* On entry to this basic block:
+ t0 == a source word not containing a null. */
+
+ /*
+ * nops here to:
+ * separate store quads from load quads
+ * limit of 1 bcond/quad to permit training
+ */
+$a_loop:
+ stq_u t0, 0(a0) # L :
+ addq a0, 8, a0 # E :
+ subq a2, 1, a2 # E :
+ nop
+
+ ldq_u t0, 0(a1) # L :
+ addq a1, 8, a1 # E :
+ cmpbge zero, t0, t8 # E :
+ beq a2, $a_eoc # U :
+
+ beq t8, $a_loop # U :
+ nop
+ nop
+ nop
+
+ /* Take care of the final (partial) word store. At this point
+ the end-of-count bit is set in t8 iff it applies.
+
+ On entry to this basic block we have:
+ t0 == the source word containing the null
+ t8 == the cmpbge mask that found it. */
+
+$a_eos:
+ negq t8, t12 # E : find low bit set
+ and t8, t12, t12 # E : (stall)
+ /* For the sake of the cache, don't read a destination word
+ if we're not going to need it. */
+ and t12, 0x80, t6 # E : (stall)
+ bne t6, 1f # U : (stall)
+
+ /* We're doing a partial word store and so need to combine
+ our source and original destination words. */
+ ldq_u t1, 0(a0) # L :
+ subq t12, 1, t6 # E :
+ or t12, t6, t8 # E : (stall)
+ zapnot t0, t8, t0 # U : clear src bytes > null (stall)
+
+ zap t1, t8, t1 # .. e1 : clear dst bytes <= null
+ or t0, t1, t0 # e1 : (stall)
+ nop
+ nop
+
+1: stq_u t0, 0(a0) # L :
+ ret (t9) # L0 : Latency=3
+ nop
+ nop
+
+ /* Add the end-of-count bit to the eos detection bitmask. */
+$a_eoc:
+ or t10, t8, t8 # E :
+ br $a_eos # L0 : Latency=3
+ nop
+ nop
+
+ .end stxncpy_aligned
+
+ .align 4
+ .ent __stxncpy
+ .globl __stxncpy
+__stxncpy:
+ .frame sp, 0, t9, 0
+ .prologue 0
+
+ /* Are source and destination co-aligned? */
+ xor a0, a1, t1 # E :
+ and a0, 7, t0 # E : find dest misalignment
+ and t1, 7, t1 # E : (stall)
+ addq a2, t0, a2 # E : bias count by dest misalignment (stall)
+
+ subq a2, 1, a2 # E :
+ and a2, 7, t2 # E : (stall)
+ srl a2, 3, a2 # U : a2 = loop counter = (count - 1)/8 (stall)
+ addq zero, 1, t10 # E :
+
+ sll t10, t2, t10 # U : t10 = bitmask of last count byte
+ bne t1, $unaligned # U :
+ /* We are co-aligned; take care of a partial first word. */
+ ldq_u t1, 0(a1) # L : load first src word
+ addq a1, 8, a1 # E :
+
+ beq t0, stxncpy_aligned # U : avoid loading dest word if not needed
+ ldq_u t0, 0(a0) # L :
+ nop
+ nop
+
+ br stxncpy_aligned # .. e1 :
+ nop
+ nop
+ nop
+
+
+
+/* The source and destination are not co-aligned. Align the destination
+ and cope. We have to be very careful about not reading too much and
+ causing a SEGV. */
+
+ .align 4
+$u_head:
+ /* We know just enough now to be able to assemble the first
+ full source word. We can still find a zero at the end of it
+ that prevents us from outputting the whole thing.
+
+ On entry to this basic block:
+ t0 == the first dest word, unmasked
+ t1 == the shifted low bits of the first source word
+ t6 == bytemask that is -1 in dest word bytes */
+
+ ldq_u t2, 8(a1) # L : Latency=3 load second src word
+ addq a1, 8, a1 # E :
+ mskql t0, a0, t0 # U : mask trailing garbage in dst
+ extqh t2, a1, t4 # U : (3 cycle stall on t2)
+
+ or t1, t4, t1 # E : first aligned src word complete (stall)
+ mskqh t1, a0, t1 # U : mask leading garbage in src (stall)
+ or t0, t1, t0 # E : first output word complete (stall)
+ or t0, t6, t6 # E : mask original data for zero test (stall)
+
+ cmpbge zero, t6, t8 # E :
+ beq a2, $u_eocfin # U :
+ nop
+ nop
+
+ bne t8, $u_final # U :
+ lda t6, -1 # E : mask out the bits we have
+ mskql t6, a1, t6 # U : already seen (stall)
+ stq_u t0, 0(a0) # L : store first output word
+
+ or t6, t2, t2 # E :
+ cmpbge zero, t2, t8 # E : find nulls in second partial (stall)
+ addq a0, 8, a0 # E :
+ subq a2, 1, a2 # E :
+
+ bne t8, $u_late_head_exit # U :
+ /* Finally, we've got all the stupid leading edge cases taken care
+ of and we can set up to enter the main loop. */
+ extql t2, a1, t1 # U : position hi-bits of lo word
+ ldq_u t2, 8(a1) # L : read next high-order source word
+ addq a1, 8, a1 # E :
+
+ cmpbge zero, t2, t8 # E : (stall)
+ beq a2, $u_eoc # U :
+ nop
+ nop
+
+ bne t8, $u_eos # e1 :
+ nop
+ nop
+ nop
+
+ /* Unaligned copy main loop. In order to avoid reading too much,
+ the loop is structured to detect zeros in aligned source words.
+ This has, unfortunately, effectively pulled half of a loop
+ iteration out into the head and half into the tail, but it does
+ prevent nastiness from accumulating in the very thing we want
+ to run as fast as possible.
+
+ On entry to this basic block:
+ t1 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word
+
+ We further know that t2 does not contain a null terminator. */
+
+ .align 4
+$u_loop:
+ extqh t2, a1, t0 # U : extract high bits for current word
+ addq a1, 8, a1 # E :
+ extql t2, a1, t3 # U : extract low bits for next time
+ addq a0, 8, a0 # E :
+
+ or t0, t1, t0 # E : current dst word now complete
+ ldq_u t2, 0(a1) # U : Latency=3 load high word for next time
+ stq_u t0, -8(a0) # U : save the current word (stall)
+ mov t3, t1 # E :
+
+ subq a2, 1, a2 # E :
+ cmpbge zero, t2, t8 # E : test new word for eos (2 cycle stall for data)
+ beq a2, $u_eoc # U : (stall)
+ nop
+
+ beq t8, $u_loop # U :
+ nop
+ nop
+ nop
+
+ /* We've found a zero somewhere in the source word we just read.
+ If it resides in the lower half, we have one (probably partial)
+ word to write out, and if it resides in the upper half, we
+ have one full and one partial word left to write out.
+
+ On entry to this basic block:
+ t1 == the shifted high-order bits from the previous source word
+ t2 == the unshifted current source word. */
+$u_eos:
+ extqh t2, a1, t0 # U :
+ or t0, t1, t0 # E : first (partial) source word complete (stall)
+ cmpbge zero, t0, t8 # E : is the null in this first bit? (stall)
+ bne t8, $u_final # U : (stall)
+
+ stq_u t0, 0(a0) # L : the null was in the high-order bits
+ addq a0, 8, a0 # E :
+ subq a2, 1, a2 # E :
+ nop
+
+$u_late_head_exit:
+ extql t2, a1, t0 # U :
+ cmpbge zero, t0, t8 # E :
+ or t8, t10, t6 # E : (stall)
+ cmoveq a2, t6, t8 # E : Latency=2, extra map slot (stall)
+
+ /* Take care of a final (probably partial) result word.
+ On entry to this basic block:
+ t0 == assembled source word
+ t8 == cmpbge mask that found the null. */
+$u_final:
+ negq t8, t6 # E : isolate low bit set
+ and t6, t8, t12 # E : (stall)
+ and t12, 0x80, t6 # E : avoid dest word load if we can (stall)
+ bne t6, 1f # U : (stall)
+
+ ldq_u t1, 0(a0) # L :
+ subq t12, 1, t6 # E :
+ or t6, t12, t8 # E : (stall)
+ zapnot t0, t8, t0 # U : kill source bytes > null
+
+ zap t1, t8, t1 # U : kill dest bytes <= null
+ or t0, t1, t0 # E : (stall)
+ nop
+ nop
+
+1: stq_u t0, 0(a0) # L :
+ ret (t9) # L0 : Latency=3
+
+$u_eoc: # end-of-count
+ extqh t2, a1, t0 # U :
+ or t0, t1, t0 # E : (stall)
+ cmpbge zero, t0, t8 # E : (stall)
+ nop
+
+$u_eocfin: # end-of-count, final word
+ or t10, t8, t8 # E :
+ br $u_final # L0 : Latency=3
+ nop
+ nop
+
+ /* Unaligned copy entry point. */
+ .align 4
+$unaligned:
+
+ ldq_u t1, 0(a1) # L : load first source word
+ and a0, 7, t4 # E : find dest misalignment
+ and a1, 7, t5 # E : find src misalignment
+ /* Conditionally load the first destination word and a bytemask
+ with 0xff indicating that the destination byte is sacrosanct. */
+ mov zero, t0 # E :
+
+ mov zero, t6 # E :
+ beq t4, 1f # U :
+ ldq_u t0, 0(a0) # L :
+ lda t6, -1 # E :
+
+ mskql t6, a0, t6 # U :
+ nop
+ nop
+ nop
+1:
+ subq a1, t4, a1 # E : sub dest misalignment from src addr
+
+ /* If source misalignment is larger than dest misalignment, we need
+ extra startup checks to avoid SEGV. */
+
+ cmplt t4, t5, t12 # E :
+ extql t1, a1, t1 # U : shift src into place
+ lda t2, -1 # E : for creating masks later
+ beq t12, $u_head # U : (stall)
+
+ mskqh t2, t5, t2 # U : begin src byte validity mask
+ cmpbge zero, t1, t8 # E : is there a zero?
+ extql t2, a1, t2 # U :
+ or t8, t10, t5 # E : test for end-of-count too
+
+ cmpbge zero, t2, t3 # E :
+ cmoveq a2, t5, t8 # E : Latency=2, extra map slot
+ nop # E : keep with cmoveq
+ andnot t8, t3, t8 # E : (stall)
+
+ beq t8, $u_head # U :
+ /* At this point we've found a zero in the first partial word of
+ the source. We need to isolate the valid source data and mask
+ it into the original destination data. (Incidentally, we know
+ that we'll need at least one byte of that original dest word.) */
+ ldq_u t0, 0(a0) # L :
+ negq t8, t6 # E : build bitmask of bytes <= zero
+ mskqh t1, t4, t1 # U :
+
+ and t6, t8, t12 # E :
+ subq t12, 1, t6 # E : (stall)
+ or t6, t12, t8 # E : (stall)
+ zapnot t2, t8, t2 # U : prepare source word; mirror changes (stall)
+
+ zapnot t1, t8, t1 # U : to source validity mask
+ andnot t0, t2, t0 # E : zero place for source to reside
+ or t0, t1, t0 # E : and put it there (stall both t0, t1)
+ stq_u t0, 0(a0) # L : (stall)
+
+ ret (t9) # L0 : Latency=3
+ nop
+ nop
+ nop
+
+ .end __stxncpy
+
or t2, t3, t0 # E : bits set iff char match or zero match
andnot t0, t4, t0 # E : clear garbage bits
- cttz t3, a2 # U0 : speculative (in case we get a match)
+ cttz t0, a2 # U0 : speculative (in case we get a match)
nop # E :
- bne t0, $found # U : Stall on t0
+ bne t0, $found # U :
/*
* Yuk. This loop is going to stall like crazy waiting for the
/* For backward modules compatibility, define __memcpy. */
asm("__memcpy = memcpy; .globl __memcpy");
-
-void *memmove (void *dest, const void *src, size_t n)
-{
- if (dest <= src) {
- if (!(((unsigned long) dest ^ (unsigned long) src) & 7))
- __memcpy_aligned_up ((unsigned long) dest,
- (unsigned long) src, n);
- else
- __memcpy_unaligned_up ((unsigned long) dest,
- (unsigned long) src, n);
- }
- else {
- if (!(((unsigned long) dest ^ (unsigned long) src) & 7))
- __memcpy_aligned_dn ((unsigned long) dest,
- (unsigned long) src, n);
- else
- __memcpy_unaligned_dn ((unsigned long) dest,
- (unsigned long) src, n);
- }
- return dest;
-}
--- /dev/null
+/*
+ * arch/alpha/lib/memmove.S
+ *
+ * Barely optimized memmove routine for Alpha EV5.
+ *
+ * This is hand-massaged output from the original memcpy.c. We defer to
+ * memcpy whenever possible; the backwards copy loops are not unrolled.
+ */
+
+ .set noat
+ .set noreorder
+ .text
+
+ .align 4
+ .globl memmove
+ .ent memmove
+memmove:
+ addq $16,$18,$4
+ addq $17,$18,$5
+ cmpule $4,$17,$1 /* dest + n <= src */
+ cmpule $5,$16,$2 /* dest >= src + n */
+
+ bis $1,$2,$1
+ mov $16,$0
+ xor $16,$17,$2
+ bne $1,memcpy
+
+ and $2,7,$2 /* Test for src/dest co-alignment. */
+ bne $2,$misaligned
+
+ and $4,7,$1
+ beq $1,$skip_aligned_byte_loop_head
+
+$aligned_byte_loop_head:
+ lda $4,-1($4)
+ lda $5,-1($5)
+ unop
+ ble $18,$egress
+
+ ldq_u $3,0($5)
+ ldq_u $2,0($4)
+ lda $18,-1($18)
+ extbl $3,$5,$1
+
+ insbl $1,$4,$1
+ mskbl $2,$4,$2
+ bis $1,$2,$1
+ and $4,7,$6
+
+ stq_u $1,0($4)
+ bne $6,$aligned_byte_loop_head
+
+$skip_aligned_byte_loop_head:
+ lda $18,-8($18)
+ blt $18,$skip_aligned_word_loop
+
+$aligned_word_loop:
+ ldq $1,-8($5)
+ nop
+ lda $5,-8($5)
+ lda $18,-8($18)
+
+ stq $1,-8($4)
+ nop
+ lda $4,-8($4)
+ bge $18,$aligned_word_loop
+
+$skip_aligned_word_loop:
+ lda $18,8($18)
+ bgt $18,$byte_loop_tail
+ unop
+ ret $31,($26),1
+
+ .align 4
+$misaligned:
+ nop
+ fnop
+ unop
+ beq $18,$egress
+
+$byte_loop_tail:
+ ldq_u $3,-1($5)
+ ldq_u $2,-1($4)
+ lda $5,-1($5)
+ lda $4,-1($4)
+
+ lda $18,-1($18)
+ extbl $3,$5,$1
+ insbl $1,$4,$1
+ mskbl $2,$4,$2
+
+ bis $1,$2,$1
+ stq_u $1,0($4)
+ nop
+ bgt $18,$byte_loop_tail
+
+$egress:
+ ret $31,($26),1
+ nop
+ nop
+ nop
+
+ .end memmove
mov $16, $0 # set up return value
mov $26, $23 # set up return address
+ unop
br __stxcpy # do the copy
.end strcpy
beq $18, 0f
mov $26, $23 # set return address
br __stxncpy # do the work of the copy
+
0: ret
+ nop
+ nop
+ nop
.end strncpy
-/* stxcpy.S
+/*
+ * arch/alpha/lib/stxcpy.S
* Contributed by Richard Henderson (rth@tamu.edu)
*
* Copy a null-terminated string from SRC to DST.
-/* stxncpy.S
+/*
+ * arch/alpha/lib/stxncpy.S
* Contributed by Richard Henderson (rth@tamu.edu)
*
* Copy no more than COUNT bytes of the null-terminated string from
fi
tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
-tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE
+tristate '/dev/cpu/microcode - Intel IA32 CPU microcode support' CONFIG_MICROCODE
tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR
tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID
--- /dev/null
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/apm_bios.h>
+#include <asm/io.h>
+
+struct dmi_header
+{
+ u8 type;
+ u8 length;
+ u16 handle;
+};
+
+static char * __init dmi_string(struct dmi_header *dm, u8 s)
+{
+ u8 *bp=(u8 *)dm;
+ bp+=dm->length;
+ s--;
+ while(s>0)
+ {
+ bp+=strlen(bp);
+ bp++;
+ s--;
+ }
+ return bp;
+}
+
+static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *))
+{
+ u8 *buf;
+ struct dmi_header *dm;
+ u8 *data;
+ int i=1;
+ int last = 0;
+
+ buf = ioremap(base, len);
+ if(buf==NULL)
+ return -1;
+
+ data = buf;
+ while(i<num && (data - buf) < len)
+ {
+ dm=(struct dmi_header *)data;
+ if(dm->type < last)
+ break;
+ last = dm->type;
+ decode(dm);
+ data+=dm->length;
+ while(*data || data[1])
+ data++;
+ data+=2;
+ i++;
+ }
+ iounmap(buf);
+ return 0;
+}
+
+
+int __init dmi_iterate(void (*decode)(struct dmi_header *))
+{
+ unsigned char buf[20];
+ long fp=0xE0000L;
+ fp -= 16;
+
+ while( fp < 0xFFFFF)
+ {
+ fp+=16;
+ isa_memcpy_fromio(buf, fp, 20);
+ if(memcmp(buf, "_DMI_", 5)==0)
+ {
+ u16 num=buf[13]<<8|buf[12];
+ u16 len=buf[7]<<8|buf[6];
+ u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8];
+
+ printk(KERN_INFO "DMI %d.%d present.\n",
+ buf[14]>>4, buf[14]&0x0F);
+ printk(KERN_INFO "%d structures occupying %d bytes.\n",
+ buf[13]<<8|buf[12],
+ buf[7]<<8|buf[6]);
+ printk(KERN_INFO "DMI table at 0x%08X.\n",
+ buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]);
+ if(dmi_table(base,len, num, decode)==0)
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * Process a DMI table entry. Right now all we care about are the BIOS
+ * and machine entries. For 2.4 we should pull the smbus controller info
+ * out of here.
+ */
+
+static void __init dmi_decode(struct dmi_header *dm)
+{
+ u8 *data = (u8 *)dm;
+ char *p;
+
+ switch(dm->type)
+ {
+ case 0:
+ p=dmi_string(dm,data[4]);
+
+ if(*p && *p!=' ')
+ {
+ printk("BIOS Vendor: %s\n", p);
+ printk("BIOS Version: %s\n",
+ dmi_string(dm, data[5]));
+ printk("BIOS Release: %s\n",
+ dmi_string(dm, data[8]));
+ }
+
+ /*
+ * Check for clue free BIOS implementations who use
+ * the following QA technique
+ *
+ * [ Write BIOS Code ]<------
+ * | ^
+ * < Does it Compile >----N--
+ * |Y ^
+ * < Does it Boot Win98 >-N--
+ * |Y
+ * [Ship It]
+ *
+ * Phoenix A04 08/24/2000 is known bad (Dell Inspiron 5000e)
+ * Phoenix A07 09/29/2000 is known good (Dell Inspiron 5000)
+ */
+
+ if(strcmp(dmi_string(dm, data[4]), "Phoenix Technologies LTD")==0)
+ {
+ if(strcmp(dmi_string(dm, data[5]), "A04")==0
+ && strcmp(dmi_string(dm, data[8]), "08/24/2000")==0)
+ {
+ apm_info.get_power_status_broken = 1;
+ printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
+ }
+ }
+ break;
+ case 1:
+ p=dmi_string(dm,data[4]);
+
+ if(*p && *p!=' ')
+ {
+ printk("System Vendor: %s.\n",p);
+ printk("Product Name: %s.\n",
+ dmi_string(dm, data[5]));
+ printk("Version %s.\n",
+ dmi_string(dm, data[6]));
+ printk("Serial Number %s.\n",
+ dmi_string(dm, data[7]));
+ }
+ break;
+ case 2:
+ p=dmi_string(dm,data[4]);
+
+ if(*p && *p!=' ')
+ {
+ printk("Board Vendor: %s.\n",p);
+ printk("Board Name: %s.\n",
+ dmi_string(dm, data[5]));
+ printk("Board Version: %s.\n",
+ dmi_string(dm, data[6]));
+ }
+ break;
+ case 3:
+ p=dmi_string(dm,data[8]);
+ if(*p && *p!=' ')
+ printk("Asset Tag: %s.\n", p);
+ break;
+ }
+}
+
+static int __init dmi_scan_machine(void)
+{
+ return dmi_iterate(dmi_decode);
+}
+
+module_init(dmi_scan_machine);
* Copyright (C) 2000 Tigran Aivazian
*
* This driver allows to upgrade microcode on Intel processors
- * belonging to P6 family - PentiumPro, Pentium II,
- * Pentium III, Xeon etc.
+ * belonging to IA-32 family - PentiumPro, Pentium II,
+ * Pentium III, Xeon, Pentium 4, etc.
*
- * Reference: Section 8.10 of Volume III, Intel Pentium III Manual,
- * Order Number 243192 or free download from:
+ * Reference: Section 8.10 of Volume III, Intel Pentium 4 Manual,
+ * Order Number 245472 or free download from:
*
- * http://developer.intel.com/design/pentiumii/manuals/243192.htm
+ * http://developer.intel.com/design/pentium4/manuals/245472.htm
*
* For more information, go to http://www.urbanmyth.org/microcode
*
* to be 0 on my machine which is why it worked even when I
* disabled update by the BIOS)
* Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
+ * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
+ * Tigran Aivazian <tigran@veritas.com>
+ * Intel Pentium 4 processor support and bugfixes.
*/
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
-#define MICROCODE_VERSION "1.07"
+#define MICROCODE_VERSION "1.08"
-MODULE_DESCRIPTION("Intel CPU (P6) microcode update driver");
+MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver");
MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>");
EXPORT_NO_SYMBOLS;
+#define MICRO_DEBUG 0
+
+#if MICRO_DEBUG
+#define printf(x...) printk(##x)
+#else
+#define printf(x...)
+#endif
+
/* VFS interface */
static int microcode_open(struct inode *, struct file *);
static ssize_t microcode_read(struct file *, char *, size_t, loff_t *);
static char *mc_applied; /* array of applied microcode blocks */
static unsigned int mc_fsize; /* file size of /dev/cpu/microcode */
+/* we share file_operations between misc and devfs mechanisms */
static struct file_operations microcode_fops = {
owner: THIS_MODULE,
read: microcode_read,
static int __init microcode_init(void)
{
- int error = 0;
+ int error;
- if (misc_register(µcode_dev) < 0) {
+ error = misc_register(µcode_dev);
+ if (error)
printk(KERN_WARNING
"microcode: can't misc_register on minor=%d\n",
MICROCODE_MINOR);
- error = 1;
- }
+
devfs_handle = devfs_register(NULL, "cpu/microcode",
DEVFS_FL_DEFAULT, 0, 0, S_IFREG | S_IRUSR | S_IWUSR,
µcode_fops, NULL);
if (devfs_handle == NULL && error) {
printk(KERN_ERR "microcode: failed to devfs_register()\n");
- return -EINVAL;
+ goto out;
}
- printk(KERN_INFO "P6 Microcode Update Driver v%s\n", MICROCODE_VERSION);
- return 0;
+ printk(KERN_INFO
+ "IA-32 Microcode Update Driver: v%s <tigran@veritas.com>\n",
+ MICROCODE_VERSION);
+
+out:
+ return error;
}
static void __exit microcode_exit(void)
devfs_unregister(devfs_handle);
if (mc_applied)
kfree(mc_applied);
- printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n",
+ printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n",
MICROCODE_VERSION);
}
-module_init(microcode_init);
-module_exit(microcode_exit);
+module_init(microcode_init)
+module_exit(microcode_exit)
static int microcode_open(struct inode *unused1, struct file *unused2)
{
unsigned int pf = 0, val[2], rev, sig;
int i,found=0;
- req->err = 1; /* assume the worst */
+ req->err = 1; /* assume update will fail on this cpu */
- if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 != 6){
- printk(KERN_ERR "microcode: CPU%d not an Intel P6\n", cpu_num);
+ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
+ test_bit(X86_FEATURE_IA64, &c->x86_capability)){
+ printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num);
return;
}
sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8);
- if (c->x86_model >= 5) {
- /* get processor flags from BBL_CR_OVRD MSR (0x17) */
- rdmsr(0x17, val[0], val[1]);
+ if ((c->x86_model >= 5) || (c->x86 > 6)) {
+ /* get processor flags from MSR 0x17 */
+ rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
pf = 1 << ((val[1] >> 18) & 7);
}
microcode[i].ldrver == 1 && microcode[i].hdrver == 1) {
found=1;
- wrmsr(0x8B, 0, 0);
+
+ printf("Microcode\n");
+ printf(" Header Revision %d\n",microcode[i].hdrver);
+ printf(" Date %x/%x/%x\n",
+ ((microcode[i].date >> 24 ) & 0xff),
+ ((microcode[i].date >> 16 ) & 0xff),
+ (microcode[i].date & 0xFFFF));
+ printf(" Type %x Family %x Model %x Stepping %x\n",
+ ((microcode[i].sig >> 12) & 0x3),
+ ((microcode[i].sig >> 8) & 0xf),
+ ((microcode[i].sig >> 4) & 0xf),
+ ((microcode[i].sig & 0xf)));
+ printf(" Checksum %x\n",microcode[i].cksum);
+ printf(" Loader Revision %x\n",microcode[i].ldrver);
+ printf(" Processor Flags %x\n\n",microcode[i].pf);
+
+ /* trick, to work even if there was no prior update by the BIOS */
+ wrmsr(MSR_IA32_UCODE_REV, 0, 0);
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
- rdmsr(0x8B, val[0], rev);
+
+ /* get current (on-cpu) revision into rev (ignore val[0]) */
+ rdmsr(MSR_IA32_UCODE_REV, val[0], rev);
if (microcode[i].rev < rev) {
printk(KERN_ERR
"microcode: CPU%d not 'upgrading' to earlier revision"
break;
}
- wrmsr(0x79, (unsigned int)(m->bits), 0);
+ /* write microcode via MSR 0x79 */
+ wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(m->bits), 0);
+
+ /* serialize */
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
- rdmsr(0x8B, val[0], val[1]);
+ /* get the current revision from MSR 0x8B */
+ rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
+
+ /* notify the caller of success on this cpu */
req->err = 0;
req->slot = i;
- printk(KERN_ERR "microcode: CPU%d updated from revision "
+
+ printk(KERN_INFO "microcode: CPU%d updated from revision "
"%d to %d, date=%08x\n",
cpu_num, rev, val[1], m->date);
}
static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos)
{
- if (*ppos >= mc_fsize)
- return 0;
+ ssize_t ret = 0;
+
down_read(µcode_rwsem);
+ if (*ppos >= mc_fsize)
+ goto out;
if (*ppos + len > mc_fsize)
len = mc_fsize - *ppos;
- if (copy_to_user(buf, mc_applied + *ppos, len)) {
- up_read(µcode_rwsem);
- return -EFAULT;
- }
+ ret = -EFAULT;
+ if (copy_to_user(buf, mc_applied + *ppos, len))
+ goto out;
*ppos += len;
+ ret = len;
+out:
up_read(µcode_rwsem);
- return len;
+ return ret;
}
static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode),
GFP_KERNEL);
if (!mc_applied) {
- printk(KERN_ERR "microcode: out of memory for saved microcode\n");
up_write(µcode_rwsem);
+ printk(KERN_ERR "microcode: out of memory for saved microcode\n");
return -ENOMEM;
}
}
case MICROCODE_IOCFREE:
down_write(µcode_rwsem);
if (mc_applied) {
+ int bytes = smp_num_cpus * sizeof(struct microcode);
+
devfs_set_file_size(devfs_handle, 0);
kfree(mc_applied);
mc_applied = NULL;
- printk(KERN_INFO "microcode: freed %d bytes\n", mc_fsize);
+ printk(KERN_INFO "microcode: freed %d bytes\n", bytes);
mc_fsize = 0;
up_write(µcode_rwsem);
return 0;
return 1;
}
+/*
+ * VLSI: nibble offset 0x74 - educated guess due to routing table and
+ * config space of VLSI 82C534 PCI-bridge/router (1004:0102)
+ * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
+ * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
+ * for the busbridge to the docking station.
+ */
+
+static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+ if (pirq > 8) {
+ printk("VLSI router pirq escape (%d)\n", pirq);
+ return 0;
+ }
+ return read_config_nybble(router, 0x74, pirq-1);
+}
+
+static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+ if (pirq > 8) {
+ printk("VLSI router pirq escape (%d)\n", pirq);
+ return 0;
+ }
+ write_config_nybble(router, 0x74, pirq-1, irq);
+ return 1;
+}
+
#ifdef CONFIG_PCI_BIOS
static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{ "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set },
{ "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set },
+ { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set },
{ "default", 0, 0, NULL, NULL }
};
pirq_router_dev->slot_name);
}
-static struct irq_info *pirq_get_info(struct pci_dev *dev, int pin)
+static struct irq_info *pirq_get_info(struct pci_dev *dev)
{
struct irq_routing_table *rt = pirq_table;
int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
{
+ u8 pin;
struct irq_info *info;
- int i, pirq, pin, newirq;
+ int i, pirq, newirq;
int irq = 0;
u32 mask;
struct irq_router *r = pirq_router;
- struct pci_dev *dev2, *d;
+ struct pci_dev *dev2;
char *msg = NULL;
if (!pirq_table)
return 0;
/* Find IRQ routing entry */
- pin = pci_get_interrupt_pin(dev, &d);
- if (pin < 0) {
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if (!pin) {
DBG(" -> no interrupt pin\n");
return 0;
}
- DBG("IRQ for %s(%d) via %s", dev->slot_name, pin, d->slot_name);
- info = pirq_get_info(d, pin);
+ pin = pin - 1;
+
+ DBG("IRQ for %s:%d", dev->slot_name, pin);
+ info = pirq_get_info(dev);
if (!info) {
DBG(" -> not found in routing table\n");
return 0;
DBG(" -> newirq=%d", newirq);
/* Try to get current IRQ */
- if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) {
+ if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) {
DBG(" -> got IRQ %d\n", irq);
msg = "Found";
/* We refuse to override the dev->irq information. Give a warning! */
}
} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
DBG(" -> assigning IRQ %d", newirq);
- if (r->set(pirq_router_dev, d, pirq, newirq)) {
+ if (r->set(pirq_router_dev, dev, pirq, newirq)) {
eisa_set_level_irq(newirq);
DBG(" ... OK\n");
msg = "Assigned";
/* Update IRQ for all devices with the same pirq value */
pci_for_each_dev(dev2) {
- if ((pin = pci_get_interrupt_pin(dev2, &d)) >= 0 &&
- (info = pirq_get_info(d, pin)) &&
- info->irq[pin].link == pirq) {
+ pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin);
+ if (!pin)
+ continue;
+ pin--;
+ info = pirq_get_info(dev2);
+ if (!info)
+ continue;
+ if (info->irq[pin].link == pirq) {
dev2->irq = irq;
pirq_penalty[irq]++;
if (dev != dev2)
-/* $Id: piggyback.c,v 1.3 2000/03/11 00:22:26 zaitcev Exp $
+/* $Id: piggyback.c,v 1.4 2000/12/05 00:48:57 anton Exp $
Simple utility to make a single-image install kernel with initial ramdisk
for Sparc tftpbooting without need to set up nfs.
-
+
Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
-
+ Pete Zaitcev <zaitcev@yahoo.com> endian fixes for cross-compiles, 2000.
+
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
* as PROM looks for a.out image only.
*/
+unsigned short ld2(char *p)
+{
+ return (p[0] << 8) | p[1];
+}
+
+unsigned int ld4(char *p)
+{
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+void st4(char *p, unsigned int x)
+{
+ p[0] = x >> 24;
+ p[1] = x >> 16;
+ p[2] = x >> 8;
+ p[3] = x;
+}
+
void usage(void)
{
/* fs_img.gz is an image of initial ramdisk. */
int main(int argc,char **argv)
{
- char buffer [1024], *q, *r;
+ static char aout_magic[] = { 0x01, 0x03, 0x01, 0x07 };
+ unsigned char buffer[1024], *q, *r;
unsigned int i, j, k, start, end, offset;
FILE *map;
struct stat s;
}
if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
if (read(image,buffer,512) != 512) die(argv[1]);
- if (!memcmp (buffer, "\177ELF", 4)) {
- unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28));
-
- i = p[1] + *(unsigned int *)(buffer + 24) - p[2];
+ if (memcmp (buffer, "\177ELF", 4) == 0) {
+ q = buffer + ld4(buffer + 28);
+ i = ld4(q + 4) + ld4(buffer + 24) - ld4(q + 8);
if (lseek(image,i,0) < 0) die("lseek");
if (read(image,buffer,512) != 512) die(argv[1]);
j = 0;
- } else if (*(unsigned int *)buffer == 0x01030107) {
+ } else if (memcmp(buffer, aout_magic, 4) == 0) {
i = j = 32;
} else {
fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n");
exit(1);
}
k = i;
- i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512;
+ i += (ld2(buffer + j + 2)<<2) - 512;
if (lseek(image,i,0) < 0) die("lseek");
if (read(image,buffer,1024) != 1024) die(argv[1]);
for (q = buffer, r = q + 512; q < r; q += 4) {
}
offset = i + (q - buffer) + 10;
if (lseek(image, offset, 0) < 0) die ("lseek");
- *(unsigned *)buffer = 0;
- *(unsigned *)(buffer + 4) = 0x01000000;
- *(unsigned *)(buffer + 8) = ((end + 32 + 4095) & ~4095);
- *(unsigned *)(buffer + 12) = s.st_size;
+
+ st4(buffer, 0);
+ st4(buffer + 4, 0x01000000);
+ st4(buffer + 8, (end + 32 + 4095) & ~4095);
+ st4(buffer + 12, s.st_size);
+
if (write(image,buffer+2,14) != 14) die (argv[1]);
if (lseek(image, k - start + ((end + 32 + 4095) & ~4095), 0) < 0) die ("lseek");
if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]);
-/* $Id: ioport.c,v 1.41 2000/11/27 07:46:31 anton Exp $
+/* $Id: ioport.c,v 1.42 2000/12/05 00:56:36 anton Exp $
* ioport.c: Simple io mapping allocator.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Once the device is given the dma address, the device owns this memory
* until either pci_unmap_single or pci_dma_sync_single is performed.
*/
-dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
+dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
+ int direction)
{
if (direction == PCI_DMA_NONE)
BUG();
-/* $Id: pcic.c,v 1.19 2000/11/08 04:49:17 davem Exp $
+/* $Id: pcic.c,v 1.20 2000/12/05 00:56:36 anton Exp $
* pcic.c: Sparc/PCI controller support
*
* Copyright (C) 1998 V. Roganov and G. Raiko
*/
printk("PCIC: Skipping I/O space at 0x%lx,"
"this will Oops if a driver attaches;"
- "device '%s' (%x,%x)\n", address, namebuf,
- dev->device, dev->vendor);
+ "device '%s' at %02x:%02x)\n", address,
+ namebuf, dev->bus->number, dev->devfn);
}
}
}
{
struct pcic_ca2irq *p;
int i, ivec;
- char namebuf[64]; /* P3 remove */
+ char namebuf[64];
if (node == 0 || node == -1) {
strcpy(namebuf, "???");
} else {
- prom_getstring(node, "name", namebuf, sizeof(namebuf)); /* P3 remove */
+ prom_getstring(node, "name", namebuf, sizeof(namebuf));
}
if ((p = pcic->pcic_imap) == 0) {
if (p->irq == 0 || p->irq >= 15) { /* Corrupted map */
printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {}
}
- printk("PCIC: setting irq %x for device (%x,%x)\n",
- p->irq, dev->device, dev->vendor);
+ printk("PCIC: setting irq %d at pin %d for device %02x:%02x\n",
+ p->irq, p->pin, dev->bus->number, dev->devfn);
dev->irq = p->irq;
i = p->pin;
-/* $Id: sparc_ksyms.c,v 1.104 2000/09/06 05:43:00 anton Exp $
+/* $Id: sparc_ksyms.c,v 1.105 2000/12/11 05:24:25 anton Exp $
* arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
EXPORT_SYMBOL_PRIVATE(_rw_read_exit);
EXPORT_SYMBOL_PRIVATE(_rw_write_enter);
#endif
+/* semaphores */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_trylock);
+EXPORT_SYMBOL(__down_interruptible);
/* rw semaphores */
EXPORT_SYMBOL_NOVERS(___down_read);
EXPORT_SYMBOL_NOVERS(___down_write);
-/* $Id: sys_sparc.c,v 1.66 2000/07/10 20:57:35 davem Exp $
+/* $Id: sys_sparc.c,v 1.67 2000/11/30 08:37:31 anton Exp $
* linux/arch/sparc/kernel/sys_sparc.c
*
* This file contains various random system calls that
return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */
}
+#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1))
+
unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
{
struct vm_area_struct * vmm;
return 0;
if (!addr)
addr = TASK_UNMAPPED_BASE;
- addr = PAGE_ALIGN(addr);
+
+ if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+ addr = COLOUR_ALIGN(addr);
+ else
+ addr = PAGE_ALIGN(addr);
for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
/* At this point: (!vmm || addr < vmm->vm_end). */
if (!vmm || addr + len <= vmm->vm_start)
return addr;
addr = vmm->vm_end;
+ if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+ addr = COLOUR_ALIGN(addr);
}
}
goto out_putf;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+ if (flags & MAP_SHARED)
+ current->thread.flags |= SPARC_FLAG_MMAPSHARED;
+
down(¤t->mm->mmap_sem);
retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up(¤t->mm->mmap_sem);
+ current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
+
out_putf:
if (file)
fput(file);
unsigned long old_len, unsigned long new_len,
unsigned long flags, unsigned long new_addr)
{
+ struct vm_area_struct *vma;
unsigned long ret = -EINVAL;
if (ARCH_SUN4C_SUN4) {
if (old_len > 0x20000000 || new_len > 0x20000000)
new_len > TASK_SIZE - PAGE_SIZE)
goto out;
down(¤t->mm->mmap_sem);
+ vma = find_vma(current->mm, addr);
+ if (vma && (vma->vm_flags & VM_SHARED))
+ current->thread.flags |= SPARC_FLAG_MMAPSHARED;
if (flags & MREMAP_FIXED) {
if (ARCH_SUN4C_SUN4 &&
new_addr < 0xe0000000 &&
}
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
out_sem:
+ current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
up(¤t->mm->mmap_sem);
out:
return ret;
static int count = 0;
if (count++ > 5) return -ENOSYS;
- lock_kernel();
printk ("%s[%d]: Unimplemented SPARC system call %d\n", current->comm, current->pid, (int)regs->u_regs[1]);
#ifdef DEBUG_UNIMP_SYSCALL
show_regs (regs);
#endif
- unlock_kernel();
return -ENOSYS;
}
-/* $Id: init.c,v 1.95 2000/11/10 04:49:56 davem Exp $
+/* $Id: init.c,v 1.96 2000/11/30 08:51:50 anton Exp $
* linux/arch/sparc/mm/init.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
void flush_page_to_ram(struct page *page)
{
- unsigned long vaddr = (unsigned long) kmap(page);
- __flush_page_to_ram(vaddr);
- kunmap(page);
+ unsigned long vaddr = (unsigned long)page_address(page);
+
+ if (vaddr)
+ __flush_page_to_ram(vaddr);
}
-/* $Id: srmmu.c,v 1.224 2000/11/09 22:40:05 davem Exp $
+/* $Id: srmmu.c,v 1.225 2000/11/30 08:37:31 anton Exp $
* srmmu.c: SRMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
unsigned int hwbug_bitmask;
int vac_cache_size;
int vac_line_size;
-int vac_badbits;
extern struct resource sparc_iomap;
}
}
-static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma,
- unsigned long address, pte_t pte)
-{
- if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) {
- struct vm_area_struct *vmaring;
- struct file *file;
- struct address_space *mapping;
- unsigned long flags, offset, vaddr, start;
- int alias_found = 0;
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
-
- __save_and_cli(flags);
-
- file = vma->vm_file;
- if (!file)
- goto done;
- mapping = file->f_dentry->d_inode->i_mapping;
- offset = (address & PAGE_MASK) - vma->vm_start;
- spin_lock(&mapping->i_shared_lock);
- vmaring = mapping->i_mmap_shared;
- if (vmaring != NULL) do {
- /* Do not mistake ourselves as another mapping. */
- if(vmaring == vma)
- continue;
-
- vaddr = vmaring->vm_start + offset;
- if ((vaddr ^ address) & vac_badbits) {
- alias_found++;
- start = vmaring->vm_start;
- while (start < vmaring->vm_end) {
- pgdp = srmmu_pgd_offset(vmaring->vm_mm, start);
- if(!pgdp) goto next;
- pmdp = srmmu_pmd_offset(pgdp, start);
- if(!pmdp) goto next;
- ptep = srmmu_pte_offset(pmdp, start);
- if(!ptep) goto next;
-
- if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) {
-#if 0
- printk("Fixing USER/USER alias [%ld:%08lx]\n",
- vmaring->vm_mm->context, start);
-#endif
- flush_cache_page(vmaring, start);
- srmmu_set_pte(ptep, __pte((pte_val(*ptep) &
- ~SRMMU_CACHE)));
- flush_tlb_page(vmaring, start);
- }
- next:
- start += PAGE_SIZE;
- }
- }
- } while ((vmaring = vmaring->vm_next_share) != NULL);
- spin_unlock(&mapping->i_shared_lock);
-
- if(alias_found && ((pte_val(pte) & SRMMU_CACHE) != 0)) {
- pgdp = srmmu_pgd_offset(vma->vm_mm, address);
- pmdp = srmmu_pmd_offset(pgdp, address);
- ptep = srmmu_pte_offset(pmdp, address);
- flush_cache_page(vma, address);
- srmmu_set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE)));
- flush_tlb_page(vma, address);
- }
- done:
- __restore_flags(flags);
- }
-}
-
/* Init various srmmu chip types. */
static void __init srmmu_is_bad(void)
{
}
vac_cache_size = cache_lines * vac_line_size;
- vac_badbits = (vac_cache_size - 1) & PAGE_MASK;
#ifdef CONFIG_SMP
if(vac_cache_size > max_size)
max_size = vac_cache_size;
#ifdef CONFIG_SMP
vac_cache_size = max_size;
vac_line_size = min_line_size;
- vac_badbits = (vac_cache_size - 1) & PAGE_MASK;
#endif
printk("SRMMU: Using VAC size of %d bytes, line size %d bytes.\n",
(int)vac_cache_size, (int)vac_line_size);
BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP);
- BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
poke_srmmu = poke_hypersparc;
hypersparc_setup_blockops();
BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP);
BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP);
- BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
poke_srmmu = poke_cypress;
}
-/* $Id: sun4c.c,v 1.201 2000/11/09 22:39:36 davem Exp $
+/* $Id: sun4c.c,v 1.202 2000/12/01 03:17:31 anton Exp $
* sun4c.c: Doing in software what should be done in hardware.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
return freed;
}
-/* There are really two cases of aliases to watch out for, and these
- * are:
- *
- * 1) A user's page which can be aliased with the kernels virtual
- * mapping of the physical page.
- *
- * 2) Multiple user mappings of the same inode/anonymous object
- * such that two copies of the same data for the same phys page
- * can live (writable) in the cache at the same time.
- *
- * We handle number 1 by flushing the kernel copy of the page always
- * after COW page operations.
- *
- * NOTE: We are a bit slowed down now because the VMA arg is indeed used
- * now, so our ref/mod bit tracking quick userfaults eat a few more
- * cycles than they used to.
- */
-static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte)
-{
- pgd_t *pgdp;
- pte_t *ptep;
-
- if (vma->vm_file) {
- struct address_space *mapping;
- unsigned long offset = (address & PAGE_MASK) - vma->vm_start;
- struct vm_area_struct *vmaring;
- int alias_found = 0;
-
- mapping = vma->vm_file->f_dentry->d_inode->i_mapping;
- spin_lock(&mapping->i_shared_lock);
- vmaring = mapping->i_mmap_shared;
- if (vmaring != NULL) do {
- unsigned long vaddr = vmaring->vm_start + offset;
- unsigned long start;
-
- /* Do not mistake ourselves as another mapping. */
- if (vmaring == vma)
- continue;
-
- if (S4CVAC_BADALIAS(vaddr, address)) {
- alias_found++;
- start = vmaring->vm_start;
- while (start < vmaring->vm_end) {
- pgdp = sun4c_pgd_offset(vmaring->vm_mm, start);
- if (!pgdp)
- goto next;
- ptep = sun4c_pte_offset((pmd_t *) pgdp, start);
- if (!ptep)
- goto next;
-
- if (pte_val(*ptep) & _SUN4C_PAGE_PRESENT) {
- flush_cache_page(vmaring, start);
- *ptep = __pte(pte_val(*ptep) |
- _SUN4C_PAGE_NOCACHE);
- flush_tlb_page(vmaring, start);
- }
- next:
- start += PAGE_SIZE;
- }
- }
- } while ((vmaring = vmaring->vm_next_share) != NULL);
- spin_unlock(&mapping->i_shared_lock);
-
- if (alias_found && !(pte_val(pte) & _SUN4C_PAGE_NOCACHE)) {
- pgdp = sun4c_pgd_offset(vma->vm_mm, address);
- ptep = sun4c_pte_offset((pmd_t *) pgdp, address);
- *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_NOCACHE);
- pte = *ptep;
- }
- }
-}
-
/* An experiment, turn off by default for now... -DaveM */
#define SUN4C_PRELOAD_PSEG
#endif
start += PAGE_SIZE;
}
- if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
- sun4c_vac_alias_fixup(vma, address, pte);
#ifndef SUN4C_PRELOAD_PSEG
sun4c_put_pte(address, pte_val(pte));
#endif
add_lru(entry);
}
- if ((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
- sun4c_vac_alias_fixup(vma, address, pte);
-
sun4c_put_pte(address, pte_val(pte));
restore_flags(flags);
}
-/* $Id: sparc64_ksyms.c,v 1.98 2000/11/13 10:03:32 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.99 2000/12/09 04:15:24 anton Exp $
* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
EXPORT_SYMBOL(__flushw_user);
+EXPORT_SYMBOL(flush_icache_range);
EXPORT_SYMBOL(__flush_dcache_page);
EXPORT_SYMBOL(mstk48t02_regs);
-/* $Id: sys_sparc.c,v 1.46 2000/08/29 07:01:54 davem Exp $
+/* $Id: sys_sparc.c,v 1.47 2000/11/29 05:56:12 anton Exp $
* linux/arch/sparc64/kernel/sys_sparc.c
*
* This file contains various random system calls that
return PAGE_SIZE;
}
+#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1))
+
unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
{
struct vm_area_struct * vmm;
return 0;
if (!addr)
addr = TASK_UNMAPPED_BASE;
- addr = PAGE_ALIGN(addr);
+
+ if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+ addr = COLOUR_ALIGN(addr);
+ else
+ addr = PAGE_ALIGN(addr);
task_size -= len;
if (!vmm || addr + len <= vmm->vm_start)
return addr;
addr = vmm->vm_end;
+ if (current->thread.flags & SPARC_FLAG_MMAPSHARED)
+ addr = COLOUR_ALIGN(addr);
}
}
goto out_putf;
}
+ if (flags & MAP_SHARED)
+ current->thread.flags |= SPARC_FLAG_MMAPSHARED;
+
down(¤t->mm->mmap_sem);
retval = do_mmap(file, addr, len, prot, flags, off);
up(¤t->mm->mmap_sem);
+ current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
+
out_putf:
if (file)
fput(file);
unsigned long old_len, unsigned long new_len,
unsigned long flags, unsigned long new_addr)
{
+ struct vm_area_struct *vma;
unsigned long ret = -EINVAL;
if (current->thread.flags & SPARC_FLAG_32BIT)
goto out;
if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET)
goto out;
down(¤t->mm->mmap_sem);
+ vma = find_vma(current->mm, addr);
+ if (vma && (vma->vm_flags & VM_SHARED))
+ current->thread.flags |= SPARC_FLAG_MMAPSHARED;
if (flags & MREMAP_FIXED) {
if (new_addr < PAGE_OFFSET &&
new_addr + new_len > -PAGE_OFFSET)
ret = -ENOMEM;
if (!(flags & MREMAP_MAYMOVE))
goto out_sem;
- new_addr = get_unmapped_area (addr, new_len);
+ new_addr = get_unmapped_area(addr, new_len);
if (!new_addr)
goto out_sem;
flags |= MREMAP_FIXED;
}
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
out_sem:
+ current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
up(¤t->mm->mmap_sem);
out:
return ret;
-/* $Id: sys_sparc32.c,v 1.166 2000/11/10 04:49:56 davem Exp $
+/* $Id: sys_sparc32.c,v 1.168 2000/12/11 18:59:35 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
err = copy_from_user(kaddr + offset, (char *)A(str),
bytes_to_copy);
+ flush_dcache_page(page);
flush_page_to_ram(page);
kunmap(page);
unsigned long old_len, unsigned long new_len,
unsigned long flags, u32 __new_addr)
{
+ struct vm_area_struct *vma;
unsigned long ret = -EINVAL;
unsigned long new_addr = AA(__new_addr);
if (addr > 0xf0000000UL - old_len)
goto out;
down(¤t->mm->mmap_sem);
+ vma = find_vma(current->mm, addr);
+ if (vma && (vma->vm_flags & VM_SHARED))
+ current->thread.flags |= SPARC_FLAG_MMAPSHARED;
if (flags & MREMAP_FIXED) {
if (new_addr > 0xf0000000UL - new_len)
goto out_sem;
ret = -ENOMEM;
if (!(flags & MREMAP_MAYMOVE))
goto out_sem;
- new_addr = get_unmapped_area (addr, new_len);
+ new_addr = get_unmapped_area(addr, new_len);
if (!new_addr)
goto out_sem;
flags |= MREMAP_FIXED;
}
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
out_sem:
+ current->thread.flags &= ~(SPARC_FLAG_MMAPSHARED);
up(¤t->mm->mmap_sem);
out:
return ret;
-/* $Id: init.c,v 1.159 2000/11/06 06:59:04 davem Exp $
+/* $Id: init.c,v 1.161 2000/12/09 20:16:58 davem Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
__update_mmu_cache(vma, address, pte);
}
+/* In arch/sparc64/mm/ultra.S */
+extern void __flush_icache_page(unsigned long);
+
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+ unsigned long kaddr;
+
+ for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE)
+ __flush_icache_page(__get_phys(kaddr));
+}
+
/*
* BAD_PAGE is the page that is used for page faults when linux
* is out-of-memory. Older versions of linux just did a
#ifdef FILL_RX_POOLS_IN_BH
// initialise bottom half
- dev->bh.next = 0;
+ INIT_LIST_HEAD(&dev->bh.list);
dev->bh.sync = 0;
dev->bh.routine = (void (*)(void *)) fill_rx_pools;
dev->bh.data = dev;
* blk_cleanup_queue: - release a &request_queue_t when it is no longer needed
* @q: the request queue to be released
*
- * Description: blk_cleanup_queue is the pair to blk_init_queue(). It should
- * be called when a request queue is being released; typically when a block
- * device is being de-registered.
- * Currently, its primary task it to free all the &struct request structures
- * that were allocated to the queue.
- * Caveat:
- * Hopefully the low level driver will have finished any outstanding
- * requests first...
+ * Description:
+ * blk_cleanup_queue is the pair to blk_init_queue(). It should
+ * be called when a request queue is being released; typically
+ * when a block device is being de-registered. Currently, its
+ * primary task it to free all the &struct request structures that
+ * were allocated to the queue.
+ * Caveat:
+ * Hopefully the low level driver will have finished any
+ * outstanding requests first...
**/
void blk_cleanup_queue(request_queue_t * q)
{
* @plug: the function to be called to plug a queue
*
* Description:
- * A request queue will be "plugged" if a request is added to it while it
- * is empty. This allows a number of requests to be added before any are
- * processed, thus providing an opportunity for these requests to be merged
- * or re-ordered.
- * The default plugging function (generic_plug_device()) sets the "plugged"
- * flag for the queue and adds a task to the $tq_disk task queue to unplug
- * the queue and call the request function at a later time.
+ * A request queue will be "plugged" if a request is added to it
+ * while it is empty. This allows a number of requests to be added
+ * before any are processed, thus providing an opportunity for these
+ * requests to be merged or re-ordered.
+ * The default plugging function (generic_plug_device()) sets the
+ * "plugged" flag for the queue and adds a task to the $tq_disk task
+ * queue to unplug the queue and call the request function at a
+ * later time.
*
- * A device driver may provide an alternate plugging function by passing it to
- * blk_queue_pluggable(). This function should set the "plugged" flag if it
- * want calls to the request_function to be blocked, and should place a
- * task on $tq_disk which will unplug the queue. Alternately it can simply
- * do nothing and there-by disable plugging of the device.
+ * A device driver may provide an alternate plugging function by
+ * passing it to blk_queue_pluggable(). This function should set
+ * the "plugged" flag if it want calls to the request_function to be
+ * blocked, and should place a task on $tq_disk which will unplug
+ * the queue. Alternately it can simply do nothing and there-by
+ * disable plugging of the device.
**/
void blk_queue_pluggable (request_queue_t * q, plug_device_fn *plug)
* @mfn: the alternate make_request function
*
* Description:
- * The normal way for &struct buffer_heads to be passed to a device driver
- * it to collect into requests on a request queue, and allow the device
- * driver to select requests off that queue when it is ready. This works
- * well for many block devices. However some block devices (typically
- * virtual devices such as md or lvm) do not benefit from the processes on
- * the request queue, and are served best by having the requests passed
- * directly to them. This can be achieved by providing a function to
- * blk_queue_make_request().
+ * The normal way for &struct buffer_heads to be passed to a device
+ * driver is for them to be collected into requests on a request
+ * queue, and then to allow the device driver to select requests
+ * off that queue when it is ready. This works well for many block
+ * devices. However some block devices (typically virtual devices
+ * such as md or lvm) do not benefit from the processing on the
+ * request queue, and are served best by having the requests passed
+ * directly to them. This can be achieved by providing a function
+ * to blk_queue_make_request().
+ *
+ * Caveat:
+ * The driver that does this *must* be able to deal appropriately
+ * with buffers in "highmemory", either by calling bh_kmap() to get
+ * a kernel mapping, to by calling create_bounce() to create a
+ * buffer in normal memory.
**/
void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
next = blkdev_next_request(req);
if (req->sector + req->nr_sectors != next->sector)
return;
- if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem)
+ if (req->cmd != next->cmd
+ || req->rq_dev != next->rq_dev
+ || req->nr_sectors + next->nr_sectors > max_sectors
+ || next->sem)
return;
/*
* If we are not allowed to merge these requests, then
goto get_rq;
}
- el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments);
+ el_ret = elevator->elevator_merge_fn(q, &req, bh, rw,
+ &max_sectors, &max_segments);
switch (el_ret) {
case ELEVATOR_BACK_MERGE:
return 0;
}
+/**
+ * generic_make_request: hand a buffer head to it's device driver for I/O
+ * @rw: READ, WRITE, or READA - what sort of I/O is desired.
+ * @bh: The buffer head describing the location in memory and on the device.
+ *
+ * generic_make_request() is used to make I/O requests of block
+ * devices. It is passed a &struct buffer_head and a &rw value. The
+ * %READ and %WRITE options are (hopefully) obvious in meaning. The
+ * %READA value means that a read is required, but that the driver is
+ * free to fail the request if, for example, it cannot get needed
+ * resources immediately.
+ *
+ * generic_make_request() does not return any status. The
+ * success/failure status of the request, along with notification of
+ * completion, is delivered asynchronously through the bh->b_end_io
+ * function described (one day) else where.
+ *
+ * The caller of generic_make_request must make sure that b_page,
+ * b_addr, b_size are set to describe the memory buffer, that b_rdev
+ * and b_rsector are set to describe the device address, and the
+ * b_end_io and optionally b_private are set to describe how
+ * completion notification should be signaled. BH_Mapped should also
+ * be set (to confirm that b_dev and b_blocknr are valid).
+ *
+ * generic_make_request and the drivers it calls may use b_reqnext,
+ * and may change b_rdev and b_rsector. So the values of these fields
+ * should NOT be depended on after the call to generic_make_request.
+ * Because of this, the caller should record the device address
+ * information in b_dev and b_blocknr.
+ *
+ * Apart from those fields mentioned above, no other fields, and in
+ * particular, no other flags, are changed by generic_make_request or
+ * any lower level drivers.
+ * */
void generic_make_request (int rw, struct buffer_head * bh)
{
int major = MAJOR(bh->b_rdev);
}
-/*
- * Submit a buffer head for IO.
+/**
+ * submit_bh: submit a buffer_head to the block device later for I/O
+ * @rw: whether to %READ or %WRITE, or mayve to %READA (read ahead)
+ * @bh: The &struct buffer_head which describes the I/O
+ *
+ * submit_bh() is very similar in purpose to generic_make_request(), and
+ * uses that function to do most of the work.
+ *
+ * The extra functionality provided by submit_bh is to determine
+ * b_rsector from b_blocknr and b_size, and to set b_rdev from b_dev.
+ * This is is appropriate for IO requests that come from the buffer
+ * cache and page cache which (currently) always use aligned blocks.
*/
void submit_bh(int rw, struct buffer_head * bh)
{
unlock_buffer(bh);
}
-/* This function can be used to request a number of buffers from a block
- device. Currently the only restriction is that all buffers must belong to
- the same device */
+/**
+ * ll_rw_block: low-level access to block devices
+ * @rw: whether to %READ or %WRITE or maybe %READA (readahead)
+ * @nr: number of &struct buffer_heads in the array
+ * @bhs: array of pointers to &struct buffer_head
+ *
+ * ll_rw_block() takes an array of pointers to &struct buffer_heads,
+ * and requests an I/O operation on them, either a %READ or a %WRITE.
+ * The third %READA option is described in the documentation for
+ * generic_make_request() which ll_rw_block() calls.
+ *
+ * This function provides extra functionality that is not in
+ * generic_make_request() that is relevant to buffers in the buffer
+ * cache or page cache. In particular it drops any buffer that it
+ * cannot get a lock on (with the BH_Lock state bit), any buffer that
+ * appears to be clean when doing a write request, and any buffer that
+ * appears to be up-to-date when doing read request. Further it marks
+ * as clean buffers that are processed for writing (the buffer cache
+ * wont assume that they are actually clean until the buffer gets
+ * unlocked).
+ *
+ * ll_rw_block sets b_end_io to simple completion handler that marks
+ * the buffer up-to-date (if approriate), unlocks the buffer and wakes
+ * any waiters. As client that needs a more interesting completion
+ * routine should call submit_bh() (or generic_make_request())
+ * directly.
+ *
+ * Caveat:
+ * All of the buffers must be for the same device, and must also be
+ * of the current approved size for the device. */
void ll_rw_block(int rw, int nr, struct buffer_head * bhs[])
{
static spinlock_t ps_spinlock __attribute__((unused)) = SPIN_LOCK_UNLOCKED;
static struct timer_list ps_timer = { function: ps_timer_int };
-static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+static struct tq_struct ps_tq = { routine: ps_tq_int };
static void ps_set_intr( void (*continuation)(void),
int (*ready)(void),
dev->dma->next_queue = NULL;
dev->dma->this_buffer = NULL;
- dev->tq.next = NULL;
+ INIT_LIST_HEAD(&dev->tq.list);
dev->tq.sync = 0;
dev->tq.routine = gamma_dma_schedule_tq_wrapper;
dev->tq.data = dev;
dev->dma->next_queue = NULL;
dev->dma->this_buffer = NULL;
- dev->tq.next = NULL;
+ INIT_LIST_HEAD(&dev->tq.list);
dev->tq.sync = 0;
dev->tq.routine = i810_dma_task_queue;
dev->tq.data = dev;
dev->dma->next_buffer = NULL;
dev->dma->next_queue = NULL;
dev->dma->this_buffer = NULL;
- dev->tq.next = NULL;
+ INIT_LIST_HEAD(&dev->tq.list);
dev->tq.sync = 0;
dev->tq.routine = mga_dma_task_queue;
dev->tq.data = dev;
* Add 'on_timer' to timer task queue
* (will be called from timer bh)
*/
- pInfo->bh_1.next = NULL;
+ INIT_LIST_HEAD(&pInfo->bh_1.list);
pInfo->bh_1.sync = 0;
pInfo->bh_1.routine = &on_timer_1;
pInfo->bh_1.data = pInfo;
- pInfo->bh_2.next = NULL;
+ INIT_LIST_HEAD(&pInfo->bh_2.list);
pInfo->bh_2.sync = 0;
pInfo->bh_2.routine = &on_timer_2;
pInfo->bh_2.data = pInfo;
static void r3964_close(struct tty_struct *tty)
{
- struct tq_struct *tq, *prev;
struct r3964_info *pInfo=(struct r3964_info*)tty->disc_data;
struct r3964_client_info *pClient, *pNext;
struct r3964_message *pMsg;
* Make sure that our task queue isn't activated. If it
* is, take it out of the linked list.
*/
- save_flags(flags);
- cli();
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if ((tq == &pInfo->bh_1) || (tq==&pInfo->bh_2)) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
- }
- }
- restore_flags(flags);
+ spin_lock_irqsave(&tqueue_lock, flags);
+ if (pInfo->bh_1.sync)
+ list_del(&pInfo->bh_1.list);
+ if (pInfo->bh_2.sync)
+ list_del(&pInfo->bh_2.list);
+ spin_unlock_irqrestore(&tqueue_lock, flags);
/* Remove client-structs and message queues: */
pClient=pInfo->firstClient;
void __init scan_kbd_init(void)
{
- task_scan_kbd.next=NULL;
+ INIT_LIST_HEAD(task_scan_kbd.list);
task_scan_kbd.sync=0;
task_scan_kbd.routine=scan_kbd;
task_scan_kbd.data=NULL;
int c_dcd;
struct sx_board *board;
int line;
- int locks;
+ long locks;
};
struct sx_board {
*/
unsigned char keyboard_type = KB_101;
-#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
#endif
ucval = keyboard_type;
goto setchar;
-#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && !defined(__arm__) && !defined(__sh__)
/*
* These cannot be implemented on any machine that implements
* ioperm() in user level (such as Alpha PCs).
};
static int lan_context;
-static struct tq_struct i2o_post_buckets_task = {
- 0, 0, (void (*)(void *))i2o_lan_receive_post, (void *) 0
+DECLARE_TASK_QUEUE(i2o_post_buckets_task);
+struct tq_struct run_i2o_post_buckets_task = {
+ routine: (void (*)(void *)) run_task_queue,
+ data: (void *) 0
};
/* Functions to handle message failures and transaction errors:
/* If DDM has already consumed bucket_thresh buckets, post new ones */
if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) {
- i2o_post_buckets_task.data = (void *)dev;
- queue_task(&i2o_post_buckets_task, &tq_immediate);
+ run_i2o_post_buckets_task.data = (void *)dev;
+ queue_task(&run_i2o_post_buckets_task, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
atomic_set(&priv->tx_out, 0);
priv->tx_count = 0;
- priv->i2o_batch_send_task.next = NULL;
+ INIT_LIST_HEAD(&priv->i2o_batch_send_task.list);
priv->i2o_batch_send_task.sync = 0;
priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send;
priv->i2o_batch_send_task.data = (void *)dev;
return;
}
- greq->tq.next = NULL;
+ INIT_LIST_HEAD(&greq->tq.list);
greq->tq.sync = 0;
greq->tq.routine = (void (*)(void*))pkt_complete;
greq->tq.data = greq;
/* initialize bottom handler */
d->task.sync = 0;
- d->task.next = NULL;
+ INIT_LIST_HEAD(&d->task.list);
d->task.routine = dma_rcv_bh;
d->task.data = (void*)d;
cs->tx_skb = NULL;
cs->tx_cnt = 0;
cs->event = 0;
- cs->tqueue.next = 0;
+ INIT_LIST_HEAD(&cs->tqueue.list);
cs->tqueue.sync = 0;
cs->tqueue.data = cs;
bcs->cs = cs;
bcs->channel = bc;
- bcs->tqueue.next = 0;
+ INIT_LIST_HEAD(&bcs->tqueue.list);
bcs->tqueue.sync = 0;
bcs->tqueue.routine = (void *) (void *) BChannel_bh;
bcs->tqueue.data = bcs;
card->writebootseq = ergo_writebootseq;
card->waitpofready = ergo_waitpofready;
card->set_errlog_state = ergo_set_errlog_state;
- card->irq_queue.next = 0;
+ INIT_LIST_HEAD(&card->irq_queue.list);
card->irq_queue.sync = 0;
card->irq_queue.data = card; /* init task queue for interrupt */
card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
dev->b2->id = 1;
- dev->qdelivery.next = NULL;
+ INIT_LIST_HEAD(&dev->qdelivery.list);
dev->qdelivery.sync = 0;
dev->qdelivery.routine = pcbit_deliver;
dev->qdelivery.data = dev;
obj-n :=
obj- :=
-# NOTE: xor.o must link *before* md.o so that auto-detect
-# of raid5 arrays works (and doesn't Oops). Fortunately
-# they are both export-objs, so setting the order here
-# works.
+# Note: link order is important. All raid personalities
+# and xor.o must come before md.o, as they each initialise
+# themselves, and md.o may use the personalities when it
+# auto-initialised.
+# The use of MIX_OBJS allows link order to be maintained even
+# though some are export-objs and some aren't.
+
obj-$(CONFIG_MD_LINEAR) += linear.o
obj-$(CONFIG_MD_RAID0) += raid0.o
obj-$(CONFIG_MD_RAID1) += raid1.o
obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o
# Translate to Rules.make lists.
-O_OBJS := $(filter-out $(export-objs), $(obj-y))
-OX_OBJS := $(filter $(export-objs), $(obj-y))
-M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
-MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+active-objs := $(sort $(obj-y) $(obj-m))
+
+O_OBJS := $(obj-y)
+M_OBJS := $(obj-m)
+MIX_OBJS := $(filter $(export-objs), $(active-objs))
include $(TOPDIR)/Rules.make
dev_info_t *disk = conf->disks + j;
if (size < 0) {
- table->dev1 = disk;
- table++;
+ table[-1].dev1 = disk;
}
size += disk->size;
- while (size) {
+ while (size>0) {
table->dev0 = disk;
- size -= conf->smallest->size;
- if (size < 0)
- break;
table->dev1 = NULL;
+ size -= conf->smallest->size;
table++;
}
}
- table->dev1 = NULL;
+ if (table-conf->hash_table != nb_zone)
+ BUG();
return 0;
if (!hash->dev1) {
printk ("linear_make_request : hash->dev1==NULL for block %ld\n",
block);
- return -1;
+ buffer_IO_error(bh);
+ return 0;
}
tmp_dev = hash->dev1;
} else
if (block >= (tmp_dev->size + tmp_dev->offset)
|| block < tmp_dev->offset) {
printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
- return -1;
+ buffer_IO_error(bh);
+ return 0;
}
bh->b_rdev = tmp_dev->dev;
bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
status: linear_status,
};
-#ifndef MODULE
-
-void md__init linear_init (void)
+static int md__init linear_init (void)
{
- register_md_personality (LINEAR, &linear_personality);
+ return register_md_personality (LINEAR, &linear_personality);
}
-#else
-
-int init_module (void)
-{
- return (register_md_personality (LINEAR, &linear_personality));
-}
-
-void cleanup_module (void)
+static void linear_exit (void)
{
unregister_md_personality (LINEAR);
}
-#endif
+module_init(linear_init);
+module_exit(linear_exit);
* - avoided inline strings functions lvm_strlen etc.
* 14/02/2000 - support for 2.3.43
* - integrated Andrea Arcangeli's snapshot code
+ * 07/12/2000 - make sure lvm_make_request_fn returns correct value - 0 or 1 - NeilBrown
*
*/
*/
static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh)
{
- lvm_map(bh, rw);
- return 1;
+ if (lvm_map(bh, rw)<0)
+ return 0; /* failure, buffer_IO_error has been called, don't recurse */
+ else
+ return 1; /* all ok, mapping done, call lower level driver */
}
/*
return mddev->pers->make_request(mddev, rw, bh);
else {
buffer_IO_error(bh);
- return -1;
+ return 0;
}
}
init_MUTEX(&mddev->resync_sem);
MD_INIT_LIST_HEAD(&mddev->disks);
MD_INIT_LIST_HEAD(&mddev->all_mddevs);
+ atomic_set(&mddev->active, 0);
/*
* The 'base' mddev is the one with data NULL.
static int lock_rdev (mdk_rdev_t *rdev)
{
int err = 0;
+ struct block_device *bdev;
- /*
- * First insert a dummy inode.
- */
- if (rdev->inode)
- MD_BUG();
- rdev->inode = get_empty_inode();
- if (!rdev->inode)
+ bdev = bdget(rdev->dev);
+ if (bdev == NULL)
return -ENOMEM;
- /*
- * we dont care about any other fields
- */
- rdev->inode->i_dev = rdev->inode->i_rdev = rdev->dev;
- insert_inode_hash(rdev->inode);
-
- memset(&rdev->filp, 0, sizeof(rdev->filp));
- rdev->filp.f_mode = 3; /* read write */
+ err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE);
+ if (!err) {
+ rdev->bdev = bdev;
+ }
return err;
}
static void unlock_rdev (mdk_rdev_t *rdev)
{
- if (!rdev->inode)
+ if (!rdev->bdev)
MD_BUG();
- iput(rdev->inode);
- rdev->inode = NULL;
+ blkdev_put(rdev->bdev, BDEV_FILE);
+ bdput(rdev->bdev);
+ rdev->bdev = NULL;
}
static void export_rdev (mdk_rdev_t * rdev)
abort_free:
if (rdev->sb) {
- if (rdev->inode)
+ if (rdev->bdev)
unlock_rdev(rdev);
free_disk_sb(rdev);
}
#define STILL_MOUNTED KERN_WARNING \
"md: md%d still mounted.\n"
+#define STILL_IN_USE \
+"md: md%d still in use.\n"
static int do_md_stop (mddev_t * mddev, int ro)
{
int err = 0, resync_interrupted = 0;
kdev_t dev = mddev_to_kdev(mddev);
+ if (atomic_read(&mddev->active)>1) {
+ printk(STILL_IN_USE, mdidx(mddev));
+ OUT(-EBUSY);
+ }
+
+ /* this shouldn't be needed as above would have fired */
if (!ro && get_super(dev)) {
printk (STILL_MOUNTED, mdidx(mddev));
OUT(-EBUSY);
* the 'same_array' list. Then order this list based on superblock
* update time (freshest comes first), kick out 'old' disks and
* compare superblocks. If everything's fine then run it.
+ *
+ * If "unit" is allocated, then bump its reference count
*/
-static void autorun_devices (void)
+static void autorun_devices (kdev_t countdev)
{
struct md_list_head candidates;
struct md_list_head *tmp;
continue;
}
mddev = alloc_mddev(md_kdev);
+ if (mddev == NULL) {
+ printk("md: cannot allocate memory for md drive.\n");
+ break;
+ }
+ if (md_kdev == countdev)
+ atomic_inc(&mddev->active);
printk("created md%d\n", mdidx(mddev));
ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) {
bind_rdev_to_array(rdev, mddev);
#define AUTORUNNING KERN_INFO \
"md: auto-running md%d.\n"
-static int autostart_array (kdev_t startdev)
+static int autostart_array (kdev_t startdev, kdev_t countdev)
{
int err = -EINVAL, i;
mdp_super_t *sb = NULL;
/*
* possibly return codes
*/
- autorun_devices();
+ autorun_devices(countdev);
return 0;
abort:
md_list_add(&rdev->pending, &pending_raid_disks);
}
- autorun_devices();
+ autorun_devices(-1);
}
dev_cnt = -1; /* make sure further calls to md_autodetect_dev are ignored */
err = -ENOMEM;
goto abort;
}
+ atomic_inc(&mddev->active);
+
/*
* alloc_mddev() should possibly self-lock.
*/
/*
* possibly make it lock the array ...
*/
- err = autostart_array((kdev_t)arg);
+ err = autostart_array((kdev_t)arg, dev);
if (err) {
printk("autostart %s failed!\n",
partition_name((kdev_t)arg));
static int md_open (struct inode *inode, struct file *file)
{
/*
- * Always succeed
+ * Always succeed, but increment the usage count
*/
+ mddev_t *mddev = kdev_to_mddev(inode->i_rdev);
+ if (mddev)
+ atomic_inc(&mddev->active);
return (0);
}
+static int md_release (struct inode *inode, struct file * file)
+{
+ mddev_t *mddev = kdev_to_mddev(inode->i_rdev);
+ if (mddev)
+ atomic_dec(&mddev->active);
+ return 0;
+}
+
static struct block_device_operations md_fops=
{
open: md_open,
+ release: md_release,
ioctl: md_ioctl,
};
create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL);
#endif
}
-void hsm_init (void);
-void translucent_init (void);
-void linear_init (void);
-void raid0_init (void);
-void raid1_init (void);
-void raid5_init (void);
int md__init md_init (void)
{
md_register_reboot_notifier(&md_notifier);
raid_table_header = register_sysctl_table(raid_root_table, 1);
-#ifdef CONFIG_MD_LINEAR
- linear_init ();
-#endif
-#ifdef CONFIG_MD_RAID0
- raid0_init ();
-#endif
-#ifdef CONFIG_MD_RAID1
- raid1_init ();
-#endif
-#ifdef CONFIG_MD_RAID5
- raid5_init ();
-#endif
md_geninit();
return (0);
}
bad_map:
printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bh->b_rsector, bh->b_size >> 10);
- return -1;
+ goto outerr;
bad_hash:
printk("raid0_make_request bug: hash==NULL for block %ld\n", block);
- return -1;
+ goto outerr;
bad_zone0:
printk ("raid0_make_request bug: hash->zone0==NULL for block %ld\n", block);
- return -1;
+ goto outerr;
bad_zone1:
printk ("raid0_make_request bug: hash->zone1==NULL for block %ld\n", block);
- return -1;
+ outerr:
+ buffer_IO_error(bh);
+ return 0;
}
static int raid0_status (char *page, mddev_t *mddev)
status: raid0_status,
};
-#ifndef MODULE
-
-void raid0_init (void)
-{
- register_md_personality (RAID0, &raid0_personality);
-}
-
-#else
-
-int init_module (void)
+static int md__init raid0_init (void)
{
- return (register_md_personality (RAID0, &raid0_personality));
+ return register_md_personality (RAID0, &raid0_personality);
}
-void cleanup_module (void)
+static void raid0_exit (void)
{
unregister_md_personality (RAID0);
}
-#endif
+module_init(raid0_init);
+module_exit(raid0_exit);
+
if (conf->resync_mirrors)
goto rb_out;
- if (conf->working_disks < 2) {
- int i = 0;
-
- while( !conf->mirrors[new_disk].operational &&
- (i < MD_SB_DISKS) ) {
- new_disk = conf->mirrors[new_disk].next;
- i++;
- }
-
- if (i >= MD_SB_DISKS) {
+
+ /* make sure that disk is operational */
+ while( !conf->mirrors[new_disk].operational) {
+ if (new_disk <= 0) new_disk = conf->raid_disks;
+ new_disk--;
+ if (new_disk == disk) {
/*
* This means no working disk was found
* Nothing much to do, lets not change anything
*/
new_disk = conf->last_used;
+
+ goto rb_out;
}
-
- goto rb_out;
}
-
+ disk = new_disk;
+ /* now disk == new_disk == starting point for search */
+
/*
* Don't touch anything for sequential reads.
*/
if (conf->sect_count >= conf->mirrors[new_disk].sect_limit) {
conf->sect_count = 0;
-
- while( new_disk != conf->mirrors[new_disk].next ) {
- if ((conf->mirrors[new_disk].write_only) ||
- (!conf->mirrors[new_disk].operational) )
- continue;
-
- new_disk = conf->mirrors[new_disk].next;
- break;
- }
-
+
+ do {
+ if (new_disk<=0)
+ new_disk = conf->raid_disks;
+ new_disk--;
+ if (new_disk == disk)
+ break;
+ } while ((conf->mirrors[new_disk].write_only) ||
+ (!conf->mirrors[new_disk].operational));
+
goto rb_out;
}
/* Find the disk which is closest */
- while( conf->mirrors[disk].next != conf->last_used ) {
- disk = conf->mirrors[disk].next;
+ do {
+ if (disk <= 0)
+ disk = conf->raid_disks;
+ disk--;
if ((conf->mirrors[disk].write_only) ||
(!conf->mirrors[disk].operational))
current_distance = new_distance;
new_disk = disk;
}
- }
+ } while (disk != conf->last_used);
rb_out:
conf->mirrors[new_disk].head_position = this_sector + sectors;
return sz;
}
-static void unlink_disk (raid1_conf_t *conf, int target)
-{
- int disks = MD_SB_DISKS;
- int i;
-
- for (i = 0; i < disks; i++)
- if (conf->mirrors[i].next == target)
- conf->mirrors[i].next = conf->mirrors[target].next;
-}
-
#define LAST_DISK KERN_ALERT \
"raid1: only one disk left and IO error.\n"
mdp_super_t *sb = mddev->sb;
mirror->operational = 0;
- unlink_disk(conf, failed);
mark_disk_faulty(sb->disks+mirror->number);
mark_disk_nonsync(sb->disks+mirror->number);
mark_disk_inactive(sb->disks+mirror->number);
#undef DISK_FAILED
#undef START_SYNCING
-/*
- * Insert the spare disk into the drive-ring
- */
-static void link_disk(raid1_conf_t *conf, struct mirror_info *mirror)
-{
- int j, next;
- int disks = MD_SB_DISKS;
- struct mirror_info *p = conf->mirrors;
-
- for (j = 0; j < disks; j++, p++)
- if (p->operational && !p->write_only) {
- next = p->next;
- p->next = mirror->raid_disk;
- mirror->next = next;
- return;
- }
-
- printk("raid1: bug: no read-operational devices\n");
-}
static void print_raid1_conf (raid1_conf_t *conf)
{
}
}
+static void close_sync(raid1_conf_t *conf)
+{
+ mddev_t *mddev = conf->mddev;
+ /* If reconstruction was interrupted, we need to close the "active" and "pending"
+ * holes.
+ * we know that there are no active rebuild requests, os cnt_active == cnt_ready ==0
+ */
+ /* this is really needed when recovery stops too... */
+ spin_lock_irq(&conf->segment_lock);
+ conf->start_active = conf->start_pending;
+ conf->start_ready = conf->start_pending;
+ wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock);
+ conf->start_active =conf->start_ready = conf->start_pending = conf->start_future;
+ conf->start_future = mddev->sb->size+1;
+ conf->cnt_pending = conf->cnt_future;
+ conf->cnt_future = 0;
+ conf->phase = conf->phase ^1;
+ wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock);
+ conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0;
+ conf->phase = 0;
+ conf->cnt_future = conf->cnt_done;;
+ conf->cnt_done = 0;
+ spin_unlock_irq(&conf->segment_lock);
+ wake_up(&conf->wait_done);
+}
+
static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state)
{
int err = 0;
* Deactivate a spare disk:
*/
case DISKOP_SPARE_INACTIVE:
+ close_sync(conf);
sdisk = conf->mirrors + spare_disk;
sdisk->operational = 0;
sdisk->write_only = 0;
* property)
*/
case DISKOP_SPARE_ACTIVE:
-
+ close_sync(conf);
sdisk = conf->mirrors + spare_disk;
fdisk = conf->mirrors + failed_disk;
*/
fdisk->spare = 0;
fdisk->write_only = 0;
- link_disk(conf, fdisk);
/*
* if we activate a spare, we definitely replace a
conf->resync_mirrors = 0;
}
- /* If reconstruction was interrupted, we need to close the "active" and "pending"
- * holes.
- * we know that there are no active rebuild requests, os cnt_active == cnt_ready ==0
- */
- /* this is really needed when recovery stops too... */
- spin_lock_irq(&conf->segment_lock);
- conf->start_active = conf->start_pending;
- conf->start_ready = conf->start_pending;
- wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock);
- conf->start_active =conf->start_ready = conf->start_pending = conf->start_future;
- conf->start_future = mddev->sb->size+1;
- conf->cnt_pending = conf->cnt_future;
- conf->cnt_future = 0;
- conf->phase = conf->phase ^1;
- wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock);
- conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0;
- conf->phase = 0;
- conf->cnt_future = conf->cnt_done;;
- conf->cnt_done = 0;
- spin_unlock_irq(&conf->segment_lock);
- wake_up(&conf->wait_done);
+ close_sync(conf);
up(&mddev->recovery_sem);
raid1_shrink_buffers(conf);
struct raid1_bh *r1_bh;
struct buffer_head *bh;
int bsize;
+ int disk;
spin_lock_irq(&conf->segment_lock);
if (!block_nr) {
* could dedicate one to rebuild and others to
* service read requests ..
*/
+ disk = conf->last_used;
+ /* make sure disk is operational */
+ while (!conf->mirrors[disk].operational) {
+ if (disk <= 0) disk = conf->raid_disks;
+ disk--;
+ if (disk == conf->last_used)
+ break;
+ }
+ conf->last_used = disk;
+
mirror = conf->mirrors+conf->last_used;
r1_bh = raid1_alloc_buf (conf);
bh->b_list = BUF_LOCKED;
bh->b_dev = mirror->dev;
bh->b_rdev = mirror->dev;
- bh->b_state = (1<<BH_Req) | (1<<BH_Mapped);
+ bh->b_state = (1<<BH_Req) | (1<<BH_Mapped) | (1<<BH_Lock);
if (!bh->b_page)
BUG();
if (!bh->b_data)
* find the first working one and use it as a starting point
* to read balancing.
*/
- for (j = 0; !conf->mirrors[j].operational; j++)
+ for (j = 0; !conf->mirrors[j].operational && j < MD_SB_DISKS; j++)
/* nothing */;
conf->last_used = j;
- /*
- * initialize the 'working disks' list.
- */
- for (i = conf->raid_disks - 1; i >= 0; i--) {
- if (conf->mirrors[i].operational) {
- conf->mirrors[i].next = j;
- j = i;
- }
- }
if (conf->working_disks != sb->raid_disks) {
printk(KERN_ALERT "raid1: md%d, not all disks are operational -- trying to recover array\n", mdidx(mddev));
sync_request: raid1_sync_request
};
-int raid1_init (void)
+static int md__init raid1_init (void)
{
return register_md_personality (RAID1, &raid1_personality);
}
-#ifdef MODULE
-int init_module (void)
-{
- return raid1_init();
-}
-
-void cleanup_module (void)
+static void raid1_exit (void)
{
unregister_md_personality (RAID1);
}
-#endif
+
+module_init(raid1_init);
+module_exit(raid1_exit);
+
sync_request: raid5_sync_request
};
-int raid5_init (void)
+static int md__init raid5_init (void)
{
return register_md_personality (RAID5, &raid5_personality);
}
-#ifdef MODULE
-int init_module (void)
-{
- return raid5_init();
-}
-
-void cleanup_module (void)
+static void raid5_exit (void)
{
unregister_md_personality (RAID5);
}
-#endif
+
+module_init(raid5_init);
+module_exit(raid5_exit);
+
int tuner_type; /* tuner type, when found */
int running; /* are we rolling? */
rwlock_t lock;
- int state; /* what is requested of us? */
+ long state; /* what is requested of us? */
#define STATE_OVERLAY 0
#define STATE_VBI 1
struct vidinfo* workqueue; /* buffers to grab, head is active */
-# $Id: Config.in,v 1.20 2000/07/13 12:40:46 scote1 Exp $
+# $Id: No. :) $
mainmenu_option next_comment
comment 'Memory Technology Devices (MTD)'
tristate 'Memory Technology Device (MTD) support' CONFIG_MTD
-if [ "$CONFIG_MTD" != "n" ]; then
- dep_tristate ' M-Systems Disk-On-Chip 1000 support' CONFIG_MTD_DOC1000 $CONFIG_MTD
- dep_tristate ' M-Systems Disk-On-Chip 2000' CONFIG_MTD_DOC2000 $CONFIG_MTD
- dep_tristate ' M-Systems Disk-On-Chip Millennium' CONFIG_MTD_DOC2001 $CONFIG_MTD
+if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then
+ bool 'Debugging' CONFIG_MTD_DEBUG
+ if [ "$CONFIG_MTD_DEBUG" = "y" ]; then
+ int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
+ fi
+
+comment 'Disk-On-Chip Device Drivers'
+ dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver' CONFIG_MTD_DOC2001 $CONFIG_MTD
if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
define_tristate CONFIG_MTD_DOCPROBE y
else
define_tristate CONFIG_MTD_DOCPROBE n
fi
fi
+ if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then
+ hex ' Physical address of DiskOnChip' CONFIG_MTD_DOCPROBE_ADDRESS 0x0000
+ bool ' Probe high addresses' CONFIG_MTD_DOCPROBE_HIGH
+ bool ' Probe for 0x55 0xAA BIOS Extension Signature' CONFIG_MTD_DOCPROBE_55AA
+ fi
+
+comment 'RAM/ROM Device Drivers'
dep_tristate ' Use extra onboard system memory as MTD device' CONFIG_MTD_SLRAM $CONFIG_MTD
dep_tristate ' Ramix PMC551 PCI Mezzanine ram card support' CONFIG_MTD_PMC551 $CONFIG_MTD $CONFIG_PCI
if [ "$CONFIG_MTD_PMC551" != "n" ]; then
int 'Size of the erase sectors in kB' CONFIG_MTDRAM_ERASE_SIZE 128
fi
-comment 'MTD drivers for mapped chips'
+comment 'Linearly Mapped Flash Device Drivers'
dep_tristate ' Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD
dep_tristate ' CFI support for Intel/Sharp Extended Command Set chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI
dep_tristate ' CFI support for AMD/Fujitsu Standard Command Set chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI
+ dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
+ dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
# These will later become config-options
define_bool CONFIG_MTD_JEDEC n
-define_bool CONFIG_MTD_RAM n
-define_bool CONFIG_MTD_ROM n
- dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI
- if [ "$CONFIG_MTD_PHYSMAP" != "n" ]; then
- hex 'Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
- hex 'Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+ dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI
+ if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then
+ hex ' Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
+ hex ' Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+ int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
fi
comment 'Drivers for chip mappings'
comment 'User modules and translation layers for MTD devices'
dep_tristate ' Direct chardevice access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD
- dep_tristate ' Pseudo-blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD
+ dep_tristate ' Caching blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD
dep_tristate ' FTL (Flash Translation Layer) support' CONFIG_FTL $CONFIG_MTD
dep_tristate ' NFTL (NAND Flash Translation Layer) support' CONFIG_NFTL $CONFIG_MTD
- if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFTL" != "n" ]; then
- bool ' Write support for NFTL (EXPERIMENTAL)' CONFIG_NFTL_RW
+ if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then
+ bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW
fi
fi
ALL_SUB_DIRS :=
MOD_SUB_DIRS :=
-export-objs := mtdcore.o
+export-objs := mtdcore.o mtdpart.o
list-multi :=
# MTD devices
obj-$(CONFIG_MTD_DOC1000) += doc1000.o
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
-obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o
+obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o
obj-$(CONFIG_MTD_SLRAM) += slram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_MIXMEM) += mixmem.o
obj-$(CONFIG_MTD_NORA) += nora.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
-obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
+obj-$(CONFIG_MTD_PNC2000) += pnc2000.o mtdpart.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
obj-$(CONFIG_MTD_VMAX) += vmax301.o
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
obj-$(CONFIG_FTL) += ftl.o
-obj-$(CONFIG_NFTL) += nftl.o
+obj-$(CONFIG_NFTL) += nftl.o nftlmount.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
-/* Linux driver for Disk-On-Chip 2000 */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2000.c,v 1.24 2000/07/13 10:03:31 dwmw2 Exp $ */
+/*
+ * Linux driver for Disk-On-Chip 2000 and Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2000.c,v 1.39 2000/12/01 17:34:29 dwmw2 Exp $
+ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
#include <linux/mtd/doc2000.h>
-//#define PRERELEASE
+#define DOC_SUPPORT_2000
+#define DOC_SUPPORT_MILLENNIUM
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf);
-static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
-static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
+#ifdef DOC_SUPPORT_2000
+#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k)
+#else
+#define DoC_is_2000(doc) (0)
+#endif
+#ifdef DOC_SUPPORT_MILLENNIUM
+#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
+#else
+#define DoC_is_Millennium(doc) (0)
+#endif
+
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcpy_from|to_io().
+ * This may be due to the different revisions of the ASIC controller built-in or
+ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
+ * this:
+ #undef USE_MEMCPY
+*/
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
static struct mtd_info *doc2klist = NULL;
-/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+/* Perform the required delay cycles by reading from the appropriate register */
+static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles)
+{
+ volatile char dummy;
+ int i;
+
+ for (i = 0; i < cycles; i++) {
+ if (DoC_is_Millennium(doc))
+ dummy = ReadDOC(doc->virtadr, NOP);
+ else
+ dummy = ReadDOC(doc->virtadr, DOCStatus);
+ }
+
+}
-static int _DoC_WaitReady (unsigned long docptr)
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct DiskOnChip *doc)
{
- //long c=-1;
- short c=-1;
+ unsigned long docptr = doc->virtadr;
+ unsigned short c = 0xffff;
- DEBUG(2,"_DoC_WaitReady called for out-of-line wait\n");
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
;
if (c == 0)
- DEBUG(2, "_DoC_WaitReady timed out.\n");
-
- return (c==0);
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
+ return (c == 0);
}
-static inline int DoC_WaitReady(unsigned long docptr)
+static inline int DoC_WaitReady(struct DiskOnChip *doc)
{
+ unsigned long docptr = doc->virtadr;
/* This is inline, to optimise the common case, where it's ready instantly */
- volatile char dummy;
int ret = 0;
- /* Out-of-line routine to wait for chip response */
- /* TPW: Add 4 reads - see Software Requirement 2.3.2 */
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
-
+ /* 4 read form NOP register should be issued in prior to the read from CDSNControl
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 4);
+
if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- ret = _DoC_WaitReady(docptr); /* Call the out-of-line routine to wait */
-
- /* TPW: Add 2 reads - see Software Requirement 2.3.2 */
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+
+ /* issue 2 read from NOP register after reading from CDSNControl register
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 2);
return ret;
}
+/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-/* DoC_Command: Send a flash command to the flash chip */
-
-static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags)
+static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
+ unsigned char xtraflags)
{
+ unsigned long docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags |= CDSN_CTRL_FLASH_IO;
+
/* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE,
- docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ if (DoC_is_Millennium(doc))
+ WriteDOC(command, docptr, CDSNSlowIO);
/* Send the command */
- WriteDOC(command, docptr, 2k_CDSN_IO);
-
+ WriteDOC_(command, docptr, doc->ioreg);
+
/* Lower the CLE line */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
- /* Wait for the chip to respond */
- return DoC_WaitReady(docptr);
+ /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */
+ return DoC_WaitReady(doc);
}
-/* DoC_Address: Set the current address for the flash chip */
+/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs,
- unsigned char xtraflags1, unsigned char xtraflags2)
+static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
+ unsigned char xtraflags1, unsigned char xtraflags2)
{
- /* Assert the ALE (Address Latch Enable line to the flash chip */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE,
- docptr, CDSNControl);
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags1 |= CDSN_CTRL_FLASH_IO;
+
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
/* Send the address */
- /* Three cases:
- numbytes == 1: Send single byte, bits 0-7.
- numbytes == 2: Send bits 9-16 followed by 17-23
- numbytes == 3: Send 0-7, 9-16, then 17-23
- */
- if (numbytes != 2)
- WriteDOC(ofs & 0xff, docptr, 2k_CDSN_IO);
-
- if (numbytes != 1) {
- WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CDSN_IO);
- WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CDSN_IO);
+ /* Devices with 256-byte page are addressed as:
+ Column (bits 0-7), Page (bits 8-15, 16-23, 24-31)
+ * there is no device on the market with page256
+ and more than 24 bits.
+ Devices with 512-byte page are addressed as:
+ Column (bits 0-7), Page (bits 9-16, 17-24, 25-31)
+ * 25-31 is sent only if the chip support it.
+ * bit 8 changes the read command to be sent
+ (NAND_CMD_READ0 or NAND_CMD_READ1).
+ */
+
+ if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
}
- /* Lower the ALE line */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl);
+
+ if (doc->page256) {
+ ofs = ofs >> 8;
+ } else {
+ ofs = ofs >> 9;
+ }
+
+ if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) {
+ for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
+ }
+ }
+
+ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
- /* Wait for the chip to respond */
- return DoC_WaitReady(docptr);
+ /* FIXME: The SlowIO's for millennium could be replaced by
+ a single WritePipeTerm here. mf. */
+
+ /* Lower the ALE line */
+ WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Wait for the chip to respond - Software requirement 11.4.1 */
+ return DoC_WaitReady(doc);
+}
+
+/* Read a buffer from DoC, taking care of Millennium odditys */
+static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len)
+{
+ int dummy;
+ int modulus = 0xffff;
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ if (DoC_is_Millennium(doc)) {
+ /* Read the data via the internal pipeline through CDSN IO register,
+ see Pipelined Read Operations 11.3 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+
+ /* Millennium should use the LastDataRead register - Pipeline Reads */
+ len--;
+
+ /* This is needed for correctly ECC calculation */
+ modulus = 0xff;
+ }
+
+ for (i = 0; i < len; i++)
+ buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus));
+
+ if (DoC_is_Millennium(doc)) {
+ buf[i] = ReadDOC(docptr, LastDataRead);
+ }
+}
+
+/* Write a buffer to DoC, taking care of Millennium odditys */
+static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len)
+{
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++)
+ WriteDOC_(buf[i], docptr, doc->ioreg + i);
+
+ if (DoC_is_Millennium(doc)) {
+ WriteDOC(0x00, docptr, WritePipeTerm);
+ }
}
+
/* DoC_SelectChip: Select a given flash chip within the current floor */
-static inline int DoC_SelectChip(unsigned long docptr, int chip)
+static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip)
{
+ unsigned long docptr = doc->virtadr;
+
+ /* Software requirement 11.4.4 before writing DeviceSelect */
+ /* Deassert the CE line to eliminate glitches on the FCE# outputs */
+ WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
/* Select the individual flash chip requested */
- WriteDOC( chip, docptr, CDSNDeviceSelect);
-
+ WriteDOC(chip, docptr, CDSNDeviceSelect);
+ DoC_Delay(doc, 4);
+
+ /* Reassert the CE line */
+ WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr,
+ CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
/* Wait for it to be ready */
- return DoC_WaitReady(docptr);
+ return DoC_WaitReady(doc);
}
/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
-static inline int DoC_SelectFloor(unsigned long docptr, int floor)
+static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor)
{
+ unsigned long docptr = doc->virtadr;
+
/* Select the floor (bank) of chips required */
- WriteDOC( floor, docptr, FloorSelect);
+ WriteDOC(floor, docptr, FloorSelect);
/* Wait for the chip to be ready */
- return DoC_WaitReady(docptr);
+ return DoC_WaitReady(doc);
}
-
+
/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
{
- int mfr, id, chipshift=0;
- char *mfrname=NULL, *idname=NULL;
+ int mfr, id, i;
+ volatile char dummy;
/* Page in the required floor/chip */
- DoC_SelectFloor(doc->virtadr, floor);
- DoC_SelectChip(doc->virtadr, chip);
+ DoC_SelectFloor(doc, floor);
+ DoC_SelectChip(doc, chip);
/* Reset the chip */
- if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP)) {
- DEBUG(2, "DoC_Command (reset) for %d,%d returned true\n", floor,chip);
+ if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (reset) for %d,%d returned true\n",
+ floor, chip);
return 0;
}
-
- /* Read the NAND chip ID: 1. Send ReadID command */
- if(DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP)) {
- DEBUG(2,"DoC_Command (ReadID) for %d,%d returned true\n", floor,chip);
+
+
+ /* Read the NAND chip ID: 1. Send ReadID command */
+ if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (ReadID) for %d,%d returned true\n",
+ floor, chip);
return 0;
}
- /* Read the NAND chip ID: 2. Send address byte zero
- */
- DoC_Address(doc->virtadr, 1, 0, CDSN_CTRL_WP, 0);
-
+ /* Read the NAND chip ID: 2. Send address byte zero */
+ DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0);
+
/* Read the manufacturer and device id codes from the device */
- mfr = ReadDOC(doc->virtadr, 2k_CDSN_IO);
- id = ReadDOC(doc->virtadr, 2k_CDSN_IO);
-
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ id = ReadDOC_(doc->virtadr, doc->ioreg);
+
/* No response - return failure */
if (mfr == 0xff || mfr == 0)
return 0;
-
+
/* Check it's the same as the first chip we identified.
* M-Systems say that any given DiskOnChip device should only
* contain _one_ type of flash part, although that's not a
* hardware restriction. */
if (doc->mfr) {
if (doc->mfr == mfr && doc->id == id)
- return 1; /* This is another the same the first */
+ return 1; /* This is another the same the first */
else
- printk(KERN_WARNING "Flash chip at floor %d, chip %d is different:\n",
+ printk(KERN_WARNING
+ "Flash chip at floor %d, chip %d is different:\n",
floor, chip);
}
-
- /* Print (and store if first time) the manufacturer and ID codes. */
-
- switch(mfr) {
- case NAND_MFR_TOSHIBA: /* Toshiba */
- mfrname = "Toshiba";
-
- switch(id) {
- case 0x64:
- idname = "TC5816BDC";
- chipshift = 21;
- break;
-
- case 0x6b:
- idname = "TC5832DC";
- chipshift = 22;
- break;
-
- case 0x73:
- idname = "TH58V128DC";
- chipshift = 24;
- break;
-
- case 0x75:
- idname = "TC58256FT/DC";
- chipshift = 25;
- break;
-
- case 0xe5:
- idname = "TC58V32DC";
- chipshift = 22;
- break;
-
- case 0xe6:
- idname = "TC58V64DC";
- chipshift = 23;
- break;
-
- case 0xea:
- idname = "TC58V16BDC";
- chipshift = 21;
- break;
- }
- break; /* End of Toshiba parts */
-
- case NAND_MFR_SAMSUNG: /* Samsung */
- mfrname = "Samsung";
-
- switch(id) {
- case 0x64:
- idname = "KM29N16000";
- chipshift = 21;
-
- case 0x73:
- idname = "KM29U128T";
- chipshift = 24;
- break;
-
- case 0x75:
- idname = "KM29U256T";
- chipshift = 25;
- break;
-
- case 0xe3:
- idname = "KM29W32000";
- chipshift = 22;
- break;
-
- case 0xe6:
- idname = "KM29U64000";
- chipshift = 23;
- break;
-
- case 0xea:
- idname = "KM29W16000";
- chipshift = 21;
- break;
- }
- break; /* End of Samsung parts */
- }
-
- /* If we've identified it fully, print the full names */
- if (idname) {
-#ifdef PRERELEASE
- DEBUG(1, "Flash chip found: %2.2X %2.2X (%s %s)\n",
- mfr,id,mfrname,idname);
-#endif
- /* If this is the first chip, store the id codes */
- if (!doc->mfr) {
- doc->mfr = mfr;
- doc->id = id;
- doc->chipshift = chipshift;
- return 1;
+
+ /* Print and store the manufacturer and ID codes. */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (mfr == nand_flash_ids[i].manufacture_id &&
+ id == nand_flash_ids[i].model_id) {
+ printk(KERN_INFO
+ "Flash chip found: Manufacturer ID: %2.2X, "
+ "Chip ID: %2.2X (%s)\n", mfr, id,
+ nand_flash_ids[i].name);
+ if (!doc->mfr) {
+ doc->mfr = mfr;
+ doc->id = id;
+ doc->chipshift =
+ nand_flash_ids[i].chipshift;
+ doc->page256 = nand_flash_ids[i].page256;
+ doc->pageadrlen =
+ nand_flash_ids[i].pageadrlen;
+ doc->erasesize =
+ nand_flash_ids[i].erasesize;
+ return 1;
+ }
+ return 0;
}
- return 0;
}
+
/* We haven't fully identified the chip. Print as much as we know. */
- if (mfrname)
- printk(KERN_WARNING "Unknown %s flash chip found: %2.2X %2.2X\n", mfrname,
- id, mfr);
- else
- printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", id, mfr);
-
- printk(KERN_WARNING "Please report to David.Woodhouse@mvhi.com\n");
+ printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n",
+ id, mfr);
+
+ printk(KERN_WARNING "Please report to dwmw2@infradead.org\n");
return 0;
-}
+}
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
{
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++) {
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
ret = 1;
- numchips[floor]=0;
- for (chip = 0 ; chip < MAX_CHIPS && ret != 0; chip++ ) {
-
+ numchips[floor] = 0;
+ for (chip = 0; chip < maxchips && ret != 0; chip++) {
+
ret = DoC_IdentChip(this, floor, chip);
if (ret) {
numchips[floor]++;
}
}
}
-
+
/* If there are none at all that we recognise, bail */
if (!this->numchips) {
printk("No flash chips recognised.\n");
return;
}
-
+
/* Allocate an array to hold the information for each chip */
this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
- if (!this->chips){
+ if (!this->chips) {
printk("No memory for allocating chip info structures\n");
return;
}
-
+
ret = 0;
-
+
/* Fill out the chip array with {floor, chipno} for each
* detected chip in the device. */
for (floor = 0; floor < MAX_FLOORS; floor++) {
- for (chip = 0 ; chip < numchips[floor] ; chip++) {
+ for (chip = 0; chip < numchips[floor]; chip++) {
this->chips[ret].floor = floor;
this->chips[ret].chip = chip;
this->chips[ret].curadr = 0;
/* Calculate and print the total size of the device */
this->totlen = this->numchips * (1 << this->chipshift);
- printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mb\n", this->numchips ,
- this->totlen >> 20);
+ printk(KERN_INFO
+ "%d flash chips found. Total DiskOnChip size: %ld Mb\n",
+ this->numchips, this->totlen >> 20);
}
* purpose. If it's value is the same on both chips, they might
* be the same chip, and we write to one and check for a change in
* the other. It's unclear if this register is usuable in the
- * DoC 2000 (it's in the Millenium docs), but it seems to work. */
+ * DoC 2000 (it's in the Millennium docs), but it seems to work. */
tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
if (tmp1 != tmp2)
return 0;
-
- WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution);
+
+ WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution);
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp2 == (tmp1+1) % 0xff)
+ if (tmp2 == (tmp1 + 1) % 0xff)
retval = 1;
else
retval = 0;
/* Restore register contents. May not be necessary, but do it just to
* be safe. */
WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
+
return retval;
}
-
static const char im_name[] = "DoC2k_init";
/* This routine is made available to other mtd code via
*/
static void DoC2k_init(struct mtd_info *mtd)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
struct DiskOnChip *old = NULL;
/* We must avoid being called twice for the same device. */
if (doc2klist)
- old = (struct DiskOnChip *)doc2klist->priv;
+ old = (struct DiskOnChip *) doc2klist->priv;
while (old) {
if (DoC2k_is_alias(old, this)) {
- printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
+ printk(KERN_NOTICE
+ "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
this->physadr);
- iounmap((void *)this->virtadr);
+ iounmap((void *) this->virtadr);
kfree(mtd);
return;
}
if (old->nextdoc)
- old = (struct DiskOnChip *)old->nextdoc->priv;
+ old = (struct DiskOnChip *) old->nextdoc->priv;
else
old = NULL;
}
-
-
- mtd->name = "DiskOnChip 2000";
- printk(KERN_NOTICE "DiskOnChip 2000 found at address 0x%lX\n",this->physadr);
+
+
+ switch (this->ChipID) {
+ case DOC_ChipID_Doc2k:
+ mtd->name = "DiskOnChip 2000";
+ this->ioreg = DoC_2k_CDSN_IO;
+ break;
+ case DOC_ChipID_DocMil:
+ mtd->name = "DiskOnChip Millennium";
+ this->ioreg = DoC_Mil_CDSN_IO;
+ break;
+ }
+
+ printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
+ this->physadr);
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = 0;
- mtd->erasesize = 0x2000;
+ mtd->erasesize = 0;
mtd->oobblock = 512;
mtd->oobsize = 16;
mtd->module = THIS_MODULE;
mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
-
+
this->totlen = 0;
this->numchips = 0;
-
+
this->curfloor = -1;
this->curchip = -1;
-
+
/* Ident all the chips present. */
DoC_ScanChips(this);
-
+
if (!this->totlen) {
kfree(mtd);
- iounmap((void *)this->virtadr);
+ iounmap((void *) this->virtadr);
} else {
this->nextdoc = doc2klist;
doc2klist = mtd;
- mtd->size = this->totlen;
+ mtd->size = this->totlen;
+ mtd->erasesize = this->erasesize;
add_mtd_device(mtd);
return;
}
}
-
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf)
{
/* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
}
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf)
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf, u_char * eccbuf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int di=0; /* Yes, DI is a hangover from when I was disassembling the binary driver */
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long docptr;
struct Nand *mychip;
+ unsigned char syndrome[6];
+ volatile char dummy;
+ int i, len256 = 0, ret=0;
docptr = this->virtadr;
/* Don't allow read past end of device */
if (from >= this->totlen)
return -EINVAL;
-
+
/* Don't allow a single read to cross a 512-byte block boundary */
- if (from + len > ( (from | 0x1ff) + 1))
+ if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
+ /* The ECC will not be calculated correctly if less than 512 is read */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector read (adr: %lx size %lx)\n",
+ (long) from, (long) len);
+
+ /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
+
+
/* Find the chip which is to be used and select it */
mychip = &this->chips[from >> (this->chipshift)];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
-
+
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
+ DoC_Command(this,
+ (!this->page256
+ && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
+ CDSN_CTRL_ECC_IO);
if (eccbuf) {
/* Prime the ECC engine */
- WriteDOC ( DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC ( DOC_ECC_EN, docptr, ECCConf);
+ 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);
}
- DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, from, CDSN_CTRL_WP , CDSN_CTRL_ECC_IO);
+ /* 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);
- for (di=0; di < len ; di++) {
- buf[di] = ReadDOC(docptr, 2k_CDSN_IO);
+ DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
+ CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
/* Let the caller know we completed it */
*retlen = len;
if (eccbuf) {
/* Read the ECC data through the DiskOnChip ECC logic */
- for (di=0; di<6; di++) {
- eccbuf[di] = ReadDOC(docptr, 2k_CDSN_IO);
- }
-
+ /* 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 */
- (void) ReadDOC(docptr, 2k_ECCStatus);
- (void) ReadDOC(docptr, 2k_ECCStatus);
-
+ 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 (ReadDOC(docptr, 2k_ECCStatus) & 0x80) {
+ if (i & 0x80) {
+ int nb_errors;
/* There was an ECC error */
+#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] =
+ ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
- /* FIXME: Implement ECC error correction, don't just whinge */
-
- /* 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 */
- return -EIO;
+#ifdef ECC_DEBUG
+ printk("Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
}
+
#ifdef PSYCHO_DEBUG
- else
- printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4],
- eccbuf[5]);
+ printk("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
+ eccbuf[3], eccbuf[4], eccbuf[5]);
#endif
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
-
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
- return 0;
+ return ret;
}
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf)
{
- static char as[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, as);
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
}
-static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf)
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf,
+ u_char * eccbuf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int di=0;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
unsigned long docptr;
+ volatile char dummy;
+ int len256 = 0;
struct Nand *mychip;
docptr = this->virtadr;
/* Don't allow write past end of device */
if (to >= this->totlen)
return -EINVAL;
-#if 0
+
/* Don't allow a single write to cross a 512-byte block boundary */
- if (to + len > ( (to | 0x1ff) + 1))
+ if (to + len > ((to | 0x1ff) + 1))
len = ((to | 0x1ff) + 1) - to;
-#else
- /* Don't allow writes which aren't exactly one block */
- if (to & 0x1ff || len != 0x200)
- return -EINVAL;
-#endif
+ /* The ECC will not be calculated correctly if less than 512 is written */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector write (adr: %lx size %lx)\n",
+ (long) to, (long) len);
+
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
/* Find the chip which is to be used and select it */
mychip = &this->chips[to >> (this->chipshift)];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
-
+
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
/* Set device to main plane of flash */
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(this,
+ (!this->page256
+ && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
if (eccbuf) {
/* Prime the ECC engine */
- WriteDOC ( DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ 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(docptr, NAND_CMD_SEQIN, 0);
- DoC_Address(docptr, 3, 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);
+
+ 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);
- for (di=0; di < len ; di++) {
- WriteDOC(buf[di], docptr, 2k_CDSN_IO);
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+ return -EIO;
+ }
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
+ CDSN_CTRL_ECC_IO);
}
+ DoC_WriteBuf(this, &buf[len256], len - len256);
if (eccbuf) {
- WriteDOC( CDSN_CTRL_ECC_IO | CDSN_CTRL_CE , docptr, CDSNControl );
-
-#if 1
- /* eduardp@m-sys.com says this shouldn't be necessary,
- * but it doesn't actually work without it, so I've
- * left it in for now. dwmw2.
- */
-
- WriteDOC( 0, docptr, 2k_CDSN_IO);
- WriteDOC( 0, docptr, 2k_CDSN_IO);
- WriteDOC( 0, docptr, 2k_CDSN_IO);
-#endif
+ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ if (DoC_is_Millennium(this)) {
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ } else {
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
/* Read the ECC data through the DiskOnChip ECC logic */
- for (di=0; di<6; di++) {
+ 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] );
+ 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
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
-
}
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
- DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
/* There's an implicit DoC_WaitReady() in DoC_Command */
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error programming flash\n");
/* Error in programming */
*retlen = 0;
/* Let the caller know we completed it */
*retlen = len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+ size_t dummy;
+
+ /* Write the ECC data to flash */
+ for (di=0; di<6; di++)
+ x[di] = eccbuf[di];
+
+ x[6]=0x55;
+ x[7]=0x55;
+
+ return doc_write_oob(mtd, to, 8, &dummy, x);
+ }
return 0;
}
-
-
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, u_char * buf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int i;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
unsigned long docptr;
struct Nand *mychip;
-
+
docptr = this->virtadr;
-
+
mychip = &this->chips[ofs >> this->chipshift];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
-
-
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0);
-
- for (i=0; i<len; i++)
- buf[i] = ReadDOC(docptr, 2k_CDSN_IO);
-
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_ReadBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff),
+ CDSN_CTRL_WP, 0);
+ }
+
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
*retlen = len;
return 0;
}
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, const u_char * buf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int i;
- unsigned long docptr;
- struct Nand *mychip;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+ int dummy;
- // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
+ // 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]);
- docptr = this->virtadr;
-
- mychip = &this->chips[ofs >> this->chipshift];
-
+ /* Find the chip which is to be used and select it */
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_SEQIN, 0);
- DoC_Address(docptr, 3, ofs, 0, 0);
-
- for (i=0; i<len; i++)
- WriteDOC(buf[i], docptr, 2k_CDSN_IO);
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0);
- DoC_Command(docptr, NAND_CMD_STATUS, 0);
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+ return -EIO;
+ }
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0);
+ }
+
+ DoC_WriteBuf(this, &buf[len256], len - len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error programming oob data\n");
/* There was an error */
*retlen = 0;
}
-
-int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
+int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long ofs = instr->addr;
unsigned long len = instr->len;
unsigned long docptr;
struct Nand *mychip;
-
- if(len != mtd->erasesize)
- printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize);
-
+
+ if (len != mtd->erasesize)
+ printk(KERN_WARNING "Erase not right size (%lx != %lx)n",
+ len, mtd->erasesize);
docptr = this->virtadr;
-
+
mychip = &this->chips[ofs >> this->chipshift];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
instr->state = MTD_ERASE_PENDING;
- DoC_Command(docptr, NAND_CMD_ERASE1, 0);
- DoC_Address(docptr, 2, ofs, 0, 0);
- DoC_Command(docptr, NAND_CMD_ERASE2, 0);
+ DoC_Command(this, NAND_CMD_ERASE1, 0);
+ DoC_Address(this, ADDR_PAGE, ofs, 0, 0);
+ DoC_Command(this, NAND_CMD_ERASE2, 0);
instr->state = MTD_ERASING;
- DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error writing\n");
/* There was an error */
instr->state = MTD_ERASE_FAILED;
- }
- else
+ } else
instr->state = MTD_ERASE_DONE;
- if (instr->callback)
+ if (instr->callback)
instr->callback(instr);
-
+
return 0;
}
-
-
-
/****************************************************************************
*
* Module stuff
*
****************************************************************************/
-static int __init init_doc2000(void)
-{
- inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
- return 0;
-}
-
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define cleanup_doc2000 cleanup_module
-#endif
-#define __exit
+#define init_doc2000 init_module
#endif
+int __init init_doc2000(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
+ return 0;
+}
static void __exit cleanup_doc2000(void)
{
struct mtd_info *mtd;
struct DiskOnChip *this;
- while((mtd=doc2klist)) {
- this = (struct DiskOnChip *)mtd->priv;
+ while ((mtd = doc2klist)) {
+ this = (struct DiskOnChip *) mtd->priv;
doc2klist = this->nextdoc;
-
+
del_mtd_device(mtd);
-
- iounmap((void *)this->virtadr);
+
+ iounmap((void *) this->virtadr);
kfree(this->chips);
kfree(mtd);
}
inter_module_unregister(im_name);
-
}
-module_init(init_doc2000);
-
-#if LINUX_VERSION_CODE > 0x20300
module_exit(cleanup_doc2000);
-#endif
+module_init(init_doc2000);
-/* Linux driver for Disk-On-Chip Millennium */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2001.c,v 1.7 2000/07/13 10:41:39 dwmw2 Exp $ */
+
+/*
+ * Linux driver for Disk-On-Chip Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2001.c,v 1.24 2000/12/01 13:11:02 dwmw2 Exp $
+ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
#include <linux/mtd/doc2000.h>
-static struct {
- char * name;
- int manufacture_id;
- int model_id;
- int chipshift;
-} nand_flash_ids[] = {
- {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21},
- {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22},
- {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24},
- {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25},
- {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22},
- {"Toshiba TC58V64DC", NAND_MFR_TOSHIBA, 0xe6, 23},
- {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21},
- {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21},
- {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24},
- {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25},
- {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22},
- {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23},
- {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21},
- {NULL,}
-};
-
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, u_char *eecbuf);
-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);
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcop_form|to_io().
+ * This may be due to the different revisions of the ASIC controller built-in or
+ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
+ * this:
+ #undef USE_MEMCPY
+*/
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf);
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf);
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
static struct mtd_info *docmillist = NULL;
+/* Perform the required delay cycles by reading from the NOP register */
static void DoC_Delay(unsigned long docptr, unsigned short cycles)
{
volatile char dummy;
{
unsigned short c = 0xffff;
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
+
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
;
+ if (c == 0)
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
return (c == 0);
}
-static __inline__ int DoC_WaitReady(unsigned long docptr)
+static inline int DoC_WaitReady(unsigned long docptr)
{
/* This is inline, to optimise the common case, where it's ready instantly */
int ret = 0;
return ret;
}
-/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to bypass
- the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after
- writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static __inline__ void DoC_Command(unsigned long docptr, unsigned char command,
- unsigned char xtraflags)
+/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static inline void DoC_Command(unsigned long docptr, unsigned char command,
+ unsigned char xtraflags)
{
/* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
/* Send the command */
WriteDOC(command, docptr, CDSNSlowIO);
WriteDOC(command, docptr, Mil_CDSN_IO);
-
+
/* Lower the CLE line */
WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
}
-/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to bypass
- the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after
- writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static __inline__ void DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs,
+/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs,
unsigned char xtraflags1, unsigned char xtraflags2)
{
- /* Assert the ALE (Address Latch Enable line to the flash chip */
- WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
/* Send the address */
if (mfr == 0xff || mfr == 0)
return 0;
- /* FIXME: to deal with mulit-flash on multi-Millennium case more carefully */
+ /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (mfr == nand_flash_ids[i].manufacture_id &&
id == nand_flash_ids[i].model_id) {
- printk(KERN_INFO "Flash chip found: Manufacture ID: %2.2X, "
+ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
"Chip ID: %2.2X (%s)\n",
mfr, id, nand_flash_ids[i].name);
doc->mfr = mfr;
return 0;
else
return 1;
-}
+}
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
static void DoC_ScanChips(struct DiskOnChip *this)
int floor, chip;
int numchips[MAX_FLOORS_MIL];
int ret;
-
+
this->numchips = 0;
this->mfr = 0;
this->id = 0;
-
+
/* For each floor, find the number of valid chips it contains */
for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) {
numchips[floor] = 0;
printk("No flash chips recognised.\n");
return;
}
-
+
/* Allocate an array to hold the information for each chip */
this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
if (!this->chips){
printk("No memory for allocating chip info structures\n");
return;
}
-
+
/* Fill out the chip array with {floor, chipno} for each
* detected chip in the device. */
for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) {
/* Calculate and print the total size of the device */
this->totlen = this->numchips * (1 << this->chipshift);
- printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n",
+ printk(KERN_NOTICE "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n",
this->numchips ,this->totlen >> 20);
}
/* Restore register contents. May not be necessary, but do it just to
* be safe. */
WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
+
return retval;
}
* this module is non-zero, i.e. between inter_module_get and
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
*/
-void DoCMil_init(struct mtd_info *mtd)
+static void DoCMil_init(struct mtd_info *mtd)
{
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
struct DiskOnChip *old = NULL;
mtd->name = "DiskOnChip Millennium";
printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",
- this->physadr);
+ this->physadr);
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = 0;
+
+ /* FIXME: erase size is not always 8kB */
mtd->erasesize = 0x2000;
+
mtd->oobblock = 512;
mtd->oobsize = 16;
mtd->module = THIS_MODULE;
mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
-
+
this->totlen = 0;
- this->numchips = 0;
+ this->numchips = 0;
this->curfloor = -1;
this->curchip = -1;
-
+
/* Ident all the chips present. */
DoC_ScanChips(this);
-
+
if (!this->totlen) {
kfree(mtd);
iounmap((void *)this->virtadr);
static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *eccbuf)
{
- int i;
+ int i, ret;
volatile char dummy;
+ unsigned char syndrome[6];
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
+ /* issue the Read0 or Read1 command depend on which half of the page
+ we are accessing. Polling the Flash Ready bit after issue 3 bytes
+ address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
+ DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
+ DoC_WaitReady(docptr);
+
if (eccbuf) {
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_EN, docptr, ECCConf);
} else {
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
}
- /* issue the Read0 or Read1 command depend on which half of the page
- we are accessing. Polling the Flash Ready bit after issue 3 bytes
- address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
- DoC_WaitReady(docptr);
-
/* Read the data via the internal pipeline through CDSN IO register,
see Pipelined Read Operations 11.3 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < len-1; i++) {
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
}
- buf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
/* Let the caller know we completed it */
*retlen = len;
+ ret = 0;
if (eccbuf) {
- /* FIXME: are we reading the ECC from the ECC logic of DOC or
- the spare data space on the flash chip i.e. How do we
- control the Spare Area Enable bit of the flash ?? */
- /* Read the ECC data through the DiskOnChip ECC logic
+ /* Read the ECC data from Spare Data Area,
see Reed-Solomon EDC/ECC 11.1 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < 5; i++) {
- eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
}
- eccbuf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5);
+#endif
+ eccbuf[5] = ReadDOC(docptr, LastDataRead);
/* Flush the pipeline */
dummy = ReadDOC(docptr, ECCConf);
/* Check the ECC Status */
if (ReadDOC(docptr, ECCConf) & 0x80) {
+ int nb_errors;
/* There was an ECC error */
+#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
-
- /* FIXME: Implement ECC error correction, don't just whinge */
-
- /* 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 */
- return -EIO;
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
+#ifdef ECC_DEBUG
+ printk("Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
}
+
#ifdef PSYCHO_DEBUG
- else
- printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
- eccbuf[4], eccbuf[5]);
+ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
#endif
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
- return 0;
+ return ret;
}
static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
- static char as[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, as);
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
}
static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
/* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(docptr, NAND_CMD_RESET, 0x00);
DoC_WaitReady(docptr);
/* Set device to main plane of flash */
- DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Command(docptr, NAND_CMD_READ0, 0x00);
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+ DoC_Address(docptr, 3, to, 0x00, 0x00);
+ DoC_WaitReady(docptr);
if (eccbuf) {
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
} else {
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
}
- /* issue the Serial Data In command to initial the Page Program process */
- DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
- DoC_Address(docptr, 3, to, 0x00, 0x00);
-
/* Write the data via the internal pipeline through CDSN IO register,
see Pipelined Write Operations 11.2 */
+#ifndef USE_MEMCPY
for (i = 0; i < len; i++) {
- WriteDOC(buf[i], docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
}
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
WriteDOC(0x00, docptr, WritePipeTerm);
if (eccbuf) {
- /* Write ECC data to flash, the ECC info is generated by the DiskOnChip DECC logic
+ /* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic
see Reed-Solomon EDC/ECC 11.1 */
WriteDOC(0, docptr, NOP);
WriteDOC(0, docptr, NOP);
eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i);
}
+ /* ignore the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+
+#ifndef USE_MEMCPY
/* Write the ECC data to flash */
for (i = 0; i < 6; i++) {
- WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i);
}
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6);
+#endif
+
+ /* write the block status BLOCK_USED (0x5555) at the end of ECC data
+ FIXME: this is only a hack for programming the IPL area for LinuxBIOS
+ and should be replace with proper codes in user space utilities */
+ WriteDOC(0x55, docptr, Mil_CDSN_IO);
+ WriteDOC(0x55, docptr, Mil_CDSN_IO + 1);
+
WriteDOC(0x00, docptr, WritePipeTerm);
#ifdef PSYCHO_DEBUG
(long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
eccbuf[4], eccbuf[5]);
#endif
-
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
}
/* Commit the Page Program command and wait for ready
/* Read the status of the flash device through CDSN Slow IO register
see Software Requirement 11.4 item 5.*/
- DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
+ DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
printk("Error programming flash\n");
- /* Error in programming */
+ /* Error in programming
+ FIXME: implement Bad Block Replacement (in nftl.c ??) */
*retlen = 0;
return -EIO;
}
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf)
{
- volatile char dummy;
+#ifndef USE_MEMCPY
int i;
+#endif
+ volatile char dummy;
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
- /* FIXME: should we restrict the access between 512 to 527 ?? */
-
/* Find the chip which is to be used and select it */
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
- /* FIXME: should we disable ECC engine in this way ?? */
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
- /* issue the Read2 command to read the Spare Data Area.
+ /* issue the Read2 command to set the pointer to the Spare Data Area.
Polling the Flash Ready bit after issue 3 bytes address in
Sequence Read Mode, see Software Requirement 11.4 item 1.*/
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
/* Read the data out via the internal pipeline through CDSN IO register,
see Pipelined Read Operations 11.3 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < len-1; i++) {
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
}
buf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
*retlen = len;
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, const u_char *buf)
{
+#ifndef USE_MEMCPY
int i;
+#endif
volatile char dummy;
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
/* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
+ if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
- /* FIXME: should we disable ECC engine in this way ?? */
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
/* Reset the chip, see Software Requirement 11.4 item 1. */
DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
DoC_WaitReady(docptr);
- /* issue the Read2 command to read the Spare Data Area. */
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
/* issue the Serial Data In command to initial the Page Program process */
/* Write the data via the internal pipeline through CDSN IO register,
see Pipelined Write Operations 11.2 */
- for (i = 0; i < len; i++)
- WriteDOC(buf[i], docptr, Mil_CDSN_IO);
+#ifndef USE_MEMCPY
+ for (i = 0; i < len; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
WriteDOC(0x00, docptr, WritePipeTerm);
/* Commit the Page Program command and wait for ready
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
printk("Error programming oob data\n");
+ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */
*retlen = 0;
return -EIO;
}
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
instr->state = MTD_ERASE_PENDING;
/* issue the Erase Setup command */
instr->state = MTD_ERASING;
/* Read the status of the flash device through CDSN Slow IO register
- see Software Requirement 11.4 item 5.*/
+ see Software Requirement 11.4 item 5.
+ FIXME: it seems that we are not wait long enough, some blocks are not
+ erased fully */
DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
- printk("Error Erasing\n");
- /* There was an error */
+ printk("Error Erasing at 0x%lx\n", ofs);
+ /* There was an error
+ FIXME: implement Bad Block Replacement (in nftl.c ??) */
instr->state = MTD_ERASE_FAILED;
} else
instr->state = MTD_ERASE_DONE;
*
****************************************************************************/
-static int __init init_doc2001(void)
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define cleanup_doc2001 cleanup_module
+#define init_doc2001 init_module
+#endif
+
+int __init init_doc2001(void)
{
inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
return 0;
}
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
-#define cleanup_doc2001 cleanup_module
-#endif
-#define __exit
-#endif
-
-
static void __exit cleanup_doc2001(void)
{
struct mtd_info *mtd;
kfree(mtd);
}
inter_module_unregister(im_name);
-
}
+module_exit(cleanup_doc2001);
module_init(init_doc2001);
-#if LINUX_VERSION_CODE > 0x20300
-module_exit(cleanup_doc2001);
-#endif
+
--- /dev/null
+/*
+ * ECC algorithm for M-systems disk on chip. We use the excellent Reed
+ * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
+ * GNU GPL License. The rest is simply to convert the disk on chip
+ * syndrom into a standard syndom.
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: docecc.c,v 1.1 2000/11/03 12:43:43 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/doc2000.h>
+
+/* need to undef it (from asm/termbits.h) */
+#undef B0
+
+#define MM 10 /* Symbol size in bits */
+#define KK (1023-4) /* Number of data symbols per block */
+#define B0 510 /* First root of generator polynomial, alpha form */
+#define PRIM 1 /* power of alpha used to generate roots of generator poly */
+#define NN ((1 << MM) - 1)
+
+typedef unsigned short dtype;
+
+/* 1+x^3+x^10 */
+static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
+
+/* This defines the type used to store an element of the Galois Field
+ * used by the code. Make sure this is something larger than a char if
+ * if anything larger than GF(256) is used.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium.
+ */
+typedef int gf;
+
+/* No legal value in index form represents zero, so
+ * we need a special value for this purpose
+ */
+#define A0 (NN)
+
+/* Compute x % NN, where NN is 2**MM - 1,
+ * without a slow divide
+ */
+static inline gf
+modnn(int x)
+{
+ while (x >= NN) {
+ x -= NN;
+ x = (x >> MM) + (x & NN);
+ }
+ return x;
+}
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define CLEAR(a,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = 0;\
+}
+
+#define COPY(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define COPYDOWN(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define Ldec 1
+
+/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
+ lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
+ polynomial form -> index form index_of[j=alpha**i] = i
+ alpha=2 is the primitive element of GF(2**m)
+ HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
+ Let @ represent the primitive element commonly called "alpha" that
+ is the root of the primitive polynomial p(x). Then in GF(2^m), for any
+ 0 <= i <= 2^m-2,
+ @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
+ of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
+ example the polynomial representation of @^5 would be given by the binary
+ representation of the integer "alpha_to[5]".
+ Similarily, index_of[] can be used as follows:
+ As above, let @ represent the primitive element of GF(2^m) that is
+ the root of the primitive polynomial p(x). In order to find the power
+ of @ (alpha) that has the polynomial representation
+ a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ we consider the integer "i" whose binary representation with a(0) being LSB
+ and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
+ "index_of[i]". Now, @^index_of[i] is that element whose polynomial
+ representation is (a(0),a(1),a(2),...,a(m-1)).
+ NOTE:
+ The element alpha_to[2^m-1] = 0 always signifying that the
+ representation of "@^infinity" = 0 is (0,0,0,...,0).
+ Similarily, the element index_of[0] = A0 always signifying
+ that the power of alpha which has the polynomial representation
+ (0,0,...,0) is "infinity".
+
+*/
+
+static void
+generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
+{
+ register int i, mask;
+
+ mask = 1;
+ Alpha_to[MM] = 0;
+ for (i = 0; i < MM; i++) {
+ Alpha_to[i] = mask;
+ Index_of[Alpha_to[i]] = i;
+ /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
+ if (Pp[i] != 0)
+ Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
+ mask <<= 1; /* single left-shift */
+ }
+ Index_of[Alpha_to[MM]] = MM;
+ /*
+ * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
+ * poly-repr of @^i shifted left one-bit and accounting for any @^MM
+ * term that may occur when poly-repr of @^i is shifted.
+ */
+ mask >>= 1;
+ for (i = MM + 1; i < NN; i++) {
+ if (Alpha_to[i - 1] >= mask)
+ Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
+ else
+ Alpha_to[i] = Alpha_to[i - 1] << 1;
+ Index_of[Alpha_to[i]] = i;
+ }
+ Index_of[0] = A0;
+ Alpha_to[NN] = 0;
+}
+
+/*
+ * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
+ * of the feedback shift register after having processed the data and
+ * the ECC.
+ *
+ * Return number of symbols corrected, or -1 if codeword is illegal
+ * or uncorrectable. If eras_pos is non-null, the detected error locations
+ * are written back. NOTE! This array must be at least NN-KK elements long.
+ * The corrected data are written in eras_val[]. They must be xor with the data
+ * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
+ *
+ * First "no_eras" erasures are declared by the calling program. Then, the
+ * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
+ * If the number of channel errors is not greater than "t_after_eras" the
+ * transmitted codeword will be recovered. Details of algorithm can be found
+ * in R. Blahut's "Theory ... of Error-Correcting Codes".
+
+ * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
+ * will result. The decoder *could* check for this condition, but it would involve
+ * extra time on every decoding operation.
+ * */
+static int
+eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
+ gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
+ int no_eras)
+{
+ int deg_lambda, el, deg_omega;
+ int i, j, r,k;
+ gf u,q,tmp,num1,num2,den,discr_r;
+ gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
+ * and syndrome poly */
+ gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
+ gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
+ int syn_error, count;
+
+ syn_error = 0;
+ for(i=0;i<NN-KK;i++)
+ syn_error |= bb[i];
+
+ if (!syn_error) {
+ /* if remainder is zero, data[] is a codeword and there are no
+ * errors to correct. So return data[] unmodified
+ */
+ count = 0;
+ goto finish;
+ }
+
+ for(i=1;i<=NN-KK;i++){
+ s[i] = bb[0];
+ }
+ for(j=1;j<NN-KK;j++){
+ if(bb[j] == 0)
+ continue;
+ tmp = Index_of[bb[j]];
+
+ for(i=1;i<=NN-KK;i++)
+ s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
+ }
+
+ /* undo the feedback register implicit multiplication and convert
+ syndromes to index form */
+
+ for(i=1;i<=NN-KK;i++) {
+ tmp = Index_of[s[i]];
+ if (tmp != A0)
+ tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
+ s[i] = tmp;
+ }
+
+ CLEAR(&lambda[1],NN-KK);
+ lambda[0] = 1;
+
+ if (no_eras > 0) {
+ /* Init lambda to be the erasure locator polynomial */
+ lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
+ for (i = 1; i < no_eras; i++) {
+ u = modnn(PRIM*eras_pos[i]);
+ for (j = i+1; j > 0; j--) {
+ tmp = Index_of[lambda[j - 1]];
+ if(tmp != A0)
+ lambda[j] ^= Alpha_to[modnn(u + tmp)];
+ }
+ }
+#if DEBUG >= 1
+ /* Test code that verifies the erasure locator polynomial just constructed
+ Needed only for decoder debugging. */
+
+ /* find roots of the erasure location polynomial */
+ for(i=1;i<=no_eras;i++)
+ reg[i] = Index_of[lambda[i]];
+ count = 0;
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = 1; j <= no_eras; j++)
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ if (q != 0)
+ continue;
+ /* store root and error location number indices */
+ root[count] = i;
+ loc[count] = k;
+ count++;
+ }
+ if (count != no_eras) {
+ printf("\n lambda(x) is WRONG\n");
+ count = -1;
+ goto finish;
+ }
+#if DEBUG >= 2
+ printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
+ for (i = 0; i < count; i++)
+ printf("%d ", loc[i]);
+ printf("\n");
+#endif
+#endif
+ }
+ for(i=0;i<NN-KK+1;i++)
+ b[i] = Index_of[lambda[i]];
+
+ /*
+ * Begin Berlekamp-Massey algorithm to determine error+erasure
+ * locator polynomial
+ */
+ r = no_eras;
+ el = no_eras;
+ while (++r <= NN-KK) { /* r is the step number */
+ /* Compute discrepancy at the r-th step in poly-form */
+ discr_r = 0;
+ for (i = 0; i < r; i++){
+ if ((lambda[i] != 0) && (s[r - i] != A0)) {
+ discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
+ }
+ }
+ discr_r = Index_of[discr_r]; /* Index form */
+ if (discr_r == A0) {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ } else {
+ /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+ t[0] = lambda[0];
+ for (i = 0 ; i < NN-KK; i++) {
+ if(b[i] != A0)
+ t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
+ else
+ t[i+1] = lambda[i+1];
+ }
+ if (2 * el <= r + no_eras - 1) {
+ el = r + no_eras - el;
+ /*
+ * 2 lines below: B(x) <-- inv(discr_r) *
+ * lambda(x)
+ */
+ for (i = 0; i <= NN-KK; i++)
+ b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
+ } else {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ }
+ COPY(lambda,t,NN-KK+1);
+ }
+ }
+
+ /* Convert lambda to index form and compute deg(lambda(x)) */
+ deg_lambda = 0;
+ for(i=0;i<NN-KK+1;i++){
+ lambda[i] = Index_of[lambda[i]];
+ if(lambda[i] != A0)
+ deg_lambda = i;
+ }
+ /*
+ * Find roots of the error+erasure locator polynomial by Chien
+ * Search
+ */
+ COPY(®[1],&lambda[1],NN-KK);
+ count = 0; /* Number of roots of lambda(x) */
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = deg_lambda; j > 0; j--){
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ }
+ if (q != 0)
+ continue;
+ /* store root (index-form) and error location number */
+ root[count] = i;
+ loc[count] = k;
+ /* If we've already found max possible roots,
+ * abort the search to save time
+ */
+ if(++count == deg_lambda)
+ break;
+ }
+ if (deg_lambda != count) {
+ /*
+ * deg(lambda) unequal to number of roots => uncorrectable
+ * error detected
+ */
+ count = -1;
+ goto finish;
+ }
+ /*
+ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+ * x**(NN-KK)). in index form. Also find deg(omega).
+ */
+ deg_omega = 0;
+ for (i = 0; i < NN-KK;i++){
+ tmp = 0;
+ j = (deg_lambda < i) ? deg_lambda : i;
+ for(;j >= 0; j--){
+ if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
+ tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
+ }
+ if(tmp != 0)
+ deg_omega = i;
+ omega[i] = Index_of[tmp];
+ }
+ omega[NN-KK] = A0;
+
+ /*
+ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+ * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
+ */
+ for (j = count-1; j >=0; j--) {
+ num1 = 0;
+ for (i = deg_omega; i >= 0; i--) {
+ if (omega[i] != A0)
+ num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
+ }
+ num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
+ den = 0;
+
+ /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+ for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
+ if(lambda[i+1] != A0)
+ den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
+ }
+ if (den == 0) {
+#if DEBUG >= 1
+ printf("\n ERROR: denominator = 0\n");
+#endif
+ /* Convert to dual- basis */
+ count = -1;
+ goto finish;
+ }
+ /* Apply error to data */
+ if (num1 != 0) {
+ eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
+ } else {
+ eras_val[j] = 0;
+ }
+ }
+ finish:
+ for(i=0;i<count;i++)
+ eras_pos[i] = loc[i];
+ return count;
+}
+
+/***************************************************************************/
+/* The DOC specific code begins here */
+
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA MM bits words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
+
+/*
+ * Correct the errors in 'sector[]' by using 'ecc1[]' which is the
+ * content of the feedback shift register applyied to the sector and
+ * the ECC. Return the number of errors corrected (and correct them in
+ * sector), or -1 if error
+ */
+int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
+{
+ int parity, i, nb_errors;
+ gf bb[NN - KK + 1];
+ gf error_val[NN-KK];
+ int error_pos[NN-KK], pos, bitpos, index, val;
+ dtype *Alpha_to, *Index_of;
+
+ /* init log and exp tables here to save memory. However, it is slower */
+ Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Alpha_to)
+ return -1;
+
+ Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Index_of) {
+ kfree(Alpha_to);
+ return -1;
+ }
+
+ generate_gf(Alpha_to, Index_of);
+
+ parity = ecc1[1];
+
+ bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
+ bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
+ bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
+ bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
+
+ nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
+ error_val, error_pos, 0);
+ if (nb_errors <= 0)
+ goto the_end;
+
+ /* correct the errors */
+ for(i=0;i<nb_errors;i++) {
+ pos = error_pos[i];
+ if (pos >= NB_DATA && pos < KK) {
+ nb_errors = -1;
+ goto the_end;
+ }
+ if (pos < NB_DATA) {
+ /* extract bit position (MSB first) */
+ pos = 10 * (NB_DATA - 1 - pos) - 6;
+ /* now correct the following 10 bits. At most two bytes
+ can be modified since pos is even */
+ index = (pos >> 3) ^ 1;
+ bitpos = pos & 7;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] >> (2 + bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ index = ((pos >> 3) + 1) ^ 1;
+ bitpos = (bitpos + 10) & 7;
+ if (bitpos == 0)
+ bitpos = 8;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] << (8 - bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ }
+ }
+
+ /* use parity to test extra errors */
+ if ((parity & 0xff) != 0)
+ nb_errors = -1;
+
+ the_end:
+ kfree(Alpha_to);
+ kfree(Index_of);
+ return nb_errors;
+}
+
/* Probe routines common to all DoC devices */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $ */
+/* $Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $ */
#define DOC_PASSIVE_PROBE
*/
+
+/* DOC_SINGLE_DRIVER:
+ Millennium driver has been merged into DOC2000 driver.
+
+ The newly-merged driver doesn't appear to work for writing. It's the
+ same with the DiskOnChip 2000 and the Millennium. If you have a
+ Millennium and you want write support to work, remove the definition
+ of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
+
+ Otherwise, it's left on in the hope that it'll annoy someone with
+ a Millennium enough that they go through and work out what the
+ difference is :)
+*/
+#define DOC_SINGLE_DRIVER
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
#include <linux/mtd/doc2000.h>
/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#endif
+
+
+static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+
+static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__)
-static unsigned long __initdata doc_locations[] = {
- 0xc8000, 0xca000, 0xcc000, 0xce000,
- 0xd0000, 0xd2000, 0xd4000, 0xd6000,
- 0xd8000, 0xda000, 0xdc000, 0xde000,
- 0xe0000, 0xe2000, 0xe4000, 0xe6000,
- 0xe8000, 0xea000, 0xec000, 0xee000, 0 };
+#ifdef CONFIG_MTD_DOCPROBE_HIGH
+ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /* CONFIG_MTD_DOCPROBE_HIGH */
+ 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000,
+ 0xd8000, 0xda000, 0xdc000, 0xde000,
+ 0xe0000, 0xe2000, 0xe4000, 0xe6000,
+ 0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /* CONFIG_MTD_DOCPROBE_HIGH */
#elif defined(__ppc__)
-static unsigned long __initdata doc_locations[] = {
- 0xe4000000, 0};
+ 0xe4000000,
#else
#warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif
-
+ 0 };
/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
#endif
/* Routine copied from the Linux DOC driver */
-
- /* Check for 0x55 0xAA signature at beginning of window */
+
+#ifdef CONFIG_MTD_DOCPROBE_55AA
+ /* Check for 0x55 0xAA signature at beginning of window,
+ this is no longer true once we remove the IPL (for Millennium */
if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
return 0;
+#endif /* CONFIG_MTD_DOCPROBE_55AA */
#ifndef DOC_PASSIVE_PROBE
/* It's not possible to cleanly detect the DiskOnChip - the
break;
default:
+#ifndef CONFIG_MTD_DOCPROBE_55AA
printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
ChipID, physadr);
-
+#endif
#ifndef DOC_PASSIVE_PROBE
/* Put back the contents of the DOCControl register, in case it's not
* actually a DiskOnChip.
this->physadr = physadr;
this->ChipID = ChipID;
sprintf(namebuf, "with ChipID %2.2X", ChipID);
-
+
switch(ChipID) {
case DOC_ChipID_Doc2k:
name="2000";
im_funcname = "DoC2k_init";
im_modname = "doc2000";
break;
-
+
case DOC_ChipID_DocMil:
name="Millennium";
+#ifdef DOC_SINGLE_DRIVER
+ im_funcname = "DoC2k_init";
+ im_modname = "doc2000";
+#else
im_funcname = "DoCMil_init";
im_modname = "doc2001";
+#endif /* DOC_SINGLE_DRIVER */
break;
}
+
if (im_funcname)
initroutine = inter_module_get_request(im_funcname, im_modname);
+
if (initroutine) {
(*initroutine)(mtd);
inter_module_put(im_funcname);
return;
}
- printk("Cannot find driver for DiskOnChip %s at 0x%X\n", name, physadr);
+ printk("Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
}
iounmap((void *)docptr);
}
*
****************************************************************************/
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_doc init_module
#endif
-#define __exit
-#endif
int __init init_doc(void)
{
printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n");
#ifdef PRERELEASE
- printk(KERN_INFO "$Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $\n");
+ printk(KERN_INFO "$Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $\n");
#endif
-
- for (i=0; doc_locations[i]; i++) {
- DoC_Probe(doc_locations[i]);
+ if (doc_config_location) {
+ printk("Using configured probe address 0x%lx\n", doc_config_location);
+ DoC_Probe(doc_config_location);
+ } else {
+ for (i=0; doc_locations[i]; i++) {
+ DoC_Probe(doc_locations[i]);
+ }
}
-
+ /* So it looks like we've been used and we get unloaded */
+ MOD_INC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
return 0;
}
-
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_doc);
-#endif
/*
* Common code to handle map devices which are simple RAM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_ram.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $
+ * $Id: map_ram.c,v 1.7 2000/12/10 01:39:13 dwmw2 Exp $
*/
#include <linux/module.h>
* this module is non-zero, i.e. between inter_module_get and
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
*/
+
static struct mtd_info *map_ram_probe(struct map_info *map)
{
struct mtd_info *mtd;
memset(mtd, 0, sizeof(*mtd));
+ map->im_name = im_name;
map->fldrv_destroy = mapram_nop;
mtd->priv = map;
mtd->name = map->name;
mtd->read = mapram_read;
mtd->write = mapram_write;
mtd->sync = mapram_nop;
- mtd->im_name = im_name;
mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
-
+ mtd->erasesize = PAGE_SIZE;
+
return mtd;
}
/* Nothing to see here */
}
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_ram_init init_module
+#define map_ram_exit cleanup_module
+#endif
+
static int __init map_ram_init(void)
{
inter_module_register(im_name, THIS_MODULE, &map_ram_probe);
/*
* Common code to handle map devices which are simple ROM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_rom.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $
+ * $Id: map_rom.c,v 1.10 2000/12/10 01:39:13 dwmw2 Exp $
*/
#include <linux/module.h>
#include <linux/mtd/map.h>
-
static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static void maprom_nop (struct mtd_info *);
-struct mtd_info *map_rom_probe(struct map_info *);
-EXPORT_SYMBOL(map_rom_probe);
+static const char im_name[] = "map_rom_probe";
+
+/* This routine is made available to other mtd code via
+ * inter_module_register. It must only be accessed through
+ * inter_module_get which will bump the use count of this module. The
+ * addresses passed back in mtd are valid as long as the use count of
+ * this module is non-zero, i.e. between inter_module_get and
+ * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
+ */
struct mtd_info *map_rom_probe(struct map_info *map)
{
memset(mtd, 0, sizeof(*mtd));
+ map->im_name = im_name;
map->fldrv_destroy = maprom_nop;
mtd->priv = map;
mtd->name = map->name;
mtd->type = MTD_ROM;
mtd->size = map->size;
mtd->read = maprom_read;
+ mtd->write = maprom_write;
mtd->sync = maprom_nop;
mtd->flags = MTD_CAP_ROM;
-
- MOD_INC_USE_COUNT;
+ mtd->erasesize = 131072;
+
return mtd;
}
{
/* Nothing to see here */
}
+
+static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+{
+ printk(KERN_NOTICE "maprom_write called\n");
+ return -EIO;
+}
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_rom_init init_module
+#define map_rom_exit cleanup_module
+#endif
+
+static int __init map_rom_init(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &map_rom_probe);
+ return 0;
+}
+
+static void __exit map_rom_exit(void)
+{
+ inter_module_unregister(im_name);
+}
+
+module_init(map_rom_init);
+module_exit(map_rom_exit);
/*
* Direct MTD block device access
*
- * $Id: mtdblock.c,v 1.17 2000/07/13 14:25:54 dwmw2 Exp $
+ * $Id: mtdblock.c,v 1.38 2000/11/27 08:50:22 dwmw2 Exp $
+ *
+ * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache
*/
-#ifdef MTDBLOCK_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/types.h>
#include <linux/module.h>
-
+#include <linux/kernel.h>
+#include <linux/malloc.h>
#include <linux/mtd/mtd.h>
#define MAJOR_NR MTD_BLOCK_MAJOR
#define DEVICE_OFF(device)
#define DEVICE_NO_RANDOM
#include <linux/blk.h>
-
+/* for old kernels... */
+#ifndef QUEUE_EMPTY
+#define QUEUE_EMPTY (!CURRENT)
+#endif
#if LINUX_VERSION_CODE < 0x20300
-#define RQFUNC_ARG void
-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
#else
-#define RQFUNC_ARG request_queue_t *q
+#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
#endif
-#ifdef MTDBLOCK_DEBUG
-static int debug = MTDBLOCK_DEBUG;
-MODULE_PARM(debug, "i");
+#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 struct mtd_notifier notifier = {
+ mtd_notify_add,
+ mtd_notify_remove,
+ NULL
+};
+static devfs_handle_t devfs_dir_handle = NULL;
+static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
#endif
-#if 1
-static void mtdblock_end_request(struct request *req, int res)
+static struct mtdblk_dev {
+ struct mtd_info *mtd; /* Locked */
+ int count;
+ struct semaphore cache_sem;
+ unsigned char *cache_data;
+ unsigned long cache_offset;
+ unsigned int cache_size;
+ enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
+} *mtdblks[MAX_MTD_DEVICES];
+
+static spinlock_t mtdblks_lock;
+
+static int mtd_sizes[MAX_MTD_DEVICES];
+static int mtd_blksizes[MAX_MTD_DEVICES];
+
+
+/*
+ * Cache stuff...
+ *
+ * Since typical flash erasable sectors are much larger than what Linux's
+ * buffer cache can handle, we must implement read-modify-write on flash
+ * sectors for each block write requests. To avoid over-erasing flash sectors
+ * and to speed things up, we locally cache a whole flash sector while it is
+ * being written to until a different sector is required.
+ */
+
+static void erase_callback(struct erase_info *done)
{
- if (end_that_request_first( req, res, "mtdblock" ))
- return;
- end_that_request_last( req );
+ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+ wake_up(wait_q);
}
-#endif
-static int mtd_sizes[MAX_MTD_DEVICES];
+static int erase_write (struct mtd_info *mtd, unsigned long pos,
+ int len, const char *buf)
+{
+ struct erase_info erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+ size_t retlen;
+ int ret;
+
+ /*
+ * First, let's erase the flash block.
+ */
+
+ init_waitqueue_head(&wait_q);
+ erase.mtd = mtd;
+ erase.callback = erase_callback;
+ erase.addr = pos;
+ erase.len = len;
+ erase.priv = (u_long)&wait_q;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ ret = MTD_ERASE(mtd, &erase);
+ if (ret) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
+ "on \"%s\" failed\n",
+ pos, len, mtd->name);
+ return ret;
+ }
+ schedule(); /* Wait for erase to finish. */
+ remove_wait_queue(&wait_q, &wait);
-/* Keeping a separate list rather than just getting stuff directly out of
- the MTD core's mtd_table is perhaps not very nice, but I happen
- to dislike the idea of directly accessing mtd_table even more.
- dwmw2 31/3/0
-*/
+ /*
+ * Next, writhe data to flash.
+ */
-static int mtdblock_open(struct inode *inode, struct file *file)
+ ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
+ if (ret)
+ return ret;
+ if (retlen != len)
+ return -EIO;
+ return 0;
+}
+
+
+static int write_cached_data (struct mtdblk_dev *mtdblk)
+{
+ struct mtd_info *mtd = mtdblk->mtd;
+ int ret;
+
+ if (mtdblk->cache_state != STATE_DIRTY)
+ return 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "
+ "at 0x%lx, size 0x%x\n", mtd->name,
+ mtdblk->cache_offset, mtdblk->cache_size);
+
+ ret = erase_write (mtd, mtdblk->cache_offset,
+ mtdblk->cache_size, mtdblk->cache_data);
+ if (ret)
+ return ret;
+
+ /*
+ * Here we could argably set the cache state to STATE_CLEAN.
+ * However this could lead to inconsistency since we will not
+ * be notified if this content is altered on the flash by other
+ * means. Let's declare it empty and leave buffering tasks to
+ * the buffer cache instead.
+ */
+ mtdblk->cache_state = STATE_EMPTY;
+ return 0;
+}
+
+
+static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
+ int len, const char *buf)
{
- struct mtd_info *mtd = NULL;
+ struct mtd_info *mtd = mtdblk->mtd;
+ unsigned int sect_size = mtd->erasesize;
+ size_t retlen;
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
+ mtd->name, pos, len);
+
+ while (len > 0) {
+ unsigned long sect_start = (pos/sect_size)*sect_size;
+ unsigned int offset = pos - sect_start;
+ unsigned int size = sect_size - offset;
+ if( size > len )
+ size = len;
+
+ if (size == sect_size) {
+ /*
+ * We are covering a whole sector. Thus there is no
+ * need to bother with the cache while it may still be
+ * useful for other partial writes.
+ */
+ ret = erase_write (mtd, pos, size, buf);
+ if (ret)
+ return ret;
+ } else {
+ /* Partial sector: need to use the cache */
+
+ if (mtdblk->cache_state == STATE_DIRTY &&
+ mtdblk->cache_offset != sect_start) {
+ ret = write_cached_data(mtdblk);
+ if (ret)
+ return ret;
+ }
+
+ if (mtdblk->cache_state == STATE_EMPTY ||
+ mtdblk->cache_offset != sect_start) {
+ /* fill the cache with the current sector */
+ mtdblk->cache_state = STATE_EMPTY;
+ ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
+ if (ret)
+ return ret;
+ if (retlen != sect_size)
+ return -EIO;
+
+ mtdblk->cache_offset = sect_start;
+ mtdblk->cache_size = sect_size;
+ mtdblk->cache_state = STATE_CLEAN;
+ }
+
+ /* write data to our local cache */
+ memcpy (mtdblk->cache_data + offset, buf, size);
+ mtdblk->cache_state = STATE_DIRTY;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+ return 0;
+}
+
+
+static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
+ int len, char *buf)
+{
+ struct mtd_info *mtd = mtdblk->mtd;
+ unsigned int sect_size = mtd->erasesize;
+ size_t retlen;
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
+ mtd->name, pos, len);
+
+ while (len > 0) {
+ unsigned long sect_start = (pos/sect_size)*sect_size;
+ unsigned int offset = pos - sect_start;
+ unsigned int size = sect_size - offset;
+ if (size > len)
+ size = len;
+
+ /*
+ * Check if the requested data is already cached
+ * Read the requested amount of data from our internal cache if it
+ * contains what we want, otherwise we read the data directly
+ * from flash.
+ */
+ if (mtdblk->cache_state != STATE_EMPTY &&
+ mtdblk->cache_offset == sect_start) {
+ memcpy (buf, mtdblk->cache_data + offset, size);
+ } else {
+ ret = MTD_READ (mtd, pos, size, &retlen, buf);
+ if (ret)
+ return ret;
+ if (retlen != size)
+ return -EIO;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+ return 0;
+}
+
+
+static int mtdblock_open(struct inode *inode, struct file *file)
+{
+ struct mtdblk_dev *mtdblk;
int dev;
- DEBUG(1,"mtdblock_open\n");
+ DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
- if (inode == 0)
+ if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_MTD_DEVICES)
+ return -EINVAL;
MOD_INC_USE_COUNT;
- mtd = get_mtd_device(NULL, dev);
+ spin_lock(&mtdblks_lock);
+
+ /* If it's already open, no need to piss about. */
+ if (mtdblks[dev]) {
+ mtdblks[dev]->count++;
+ spin_unlock(&mtdblks_lock);
+ return 0;
+ }
+
+ /* OK, it's not open. Try to find it */
+
+ /* First we have to drop the lock, because we have to
+ to things which might sleep.
+ */
+ spin_unlock(&mtdblks_lock);
+
+ mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
+ if (!mtdblk) {
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+ memset(mtdblk, 0, sizeof(*mtdblk));
+ mtdblk->count = 1;
+ mtdblk->mtd = get_mtd_device(NULL, dev);
- if (!mtd) {
+ if (!mtdblk->mtd) {
+ kfree(mtdblk);
MOD_DEC_USE_COUNT;
return -ENODEV;
}
- mtd_sizes[dev] = mtd->size>>9;
+ init_MUTEX (&mtdblk->cache_sem);
+ mtdblk->cache_state = STATE_EMPTY;
+ mtdblk->cache_size = mtdblk->mtd->erasesize;
+ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+ if (!mtdblk->cache_data) {
+ put_mtd_device(mtdblk->mtd);
+ kfree(mtdblk);
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+
+ /* OK, we've created a new one. Add it to the list. */
+
+ spin_lock(&mtdblks_lock);
- DEBUG(1, "ok\n");
+ if (mtdblks[dev]) {
+ /* Another CPU made one at the same time as us. */
+ mtdblks[dev]->count++;
+ spin_unlock(&mtdblks_lock);
+ put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+ return 0;
+ }
+
+ mtdblks[dev] = mtdblk;
+ mtd_sizes[dev] = mtdblk->mtd->size/1024;
+ mtd_blksizes[dev] = mtdblk->mtd->erasesize;
+ if (mtd_blksizes[dev] > PAGE_SIZE)
+ mtd_blksizes[dev] = PAGE_SIZE;
+ set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
+
+ spin_unlock(&mtdblks_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
return 0;
}
static release_t mtdblock_release(struct inode *inode, struct file *file)
{
int dev;
- struct mtd_info *mtd;
+ struct mtdblk_dev *mtdblk;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
struct super_block * sb = get_super(inode->i_rdev);
#endif
- DEBUG(1, "mtdblock_release\n");
+ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
if (inode == NULL)
release_return(-ENODEV);
invalidate_buffers(inode->i_rdev);
dev = MINOR(inode->i_rdev);
- mtd = __get_mtd_device(NULL, dev);
-
- if (!mtd) {
- printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
- MOD_DEC_USE_COUNT;
- release_return(-ENODEV);
-
+ mtdblk = mtdblks[dev];
+
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+
+ spin_lock(&mtdblks_lock);
+ if (!--mtdblk->count) {
+ /* It was the last usage. Free the device */
+ mtdblks[dev] = NULL;
+ spin_unlock(&mtdblks_lock);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+ put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+ } else {
+ spin_unlock(&mtdblks_lock);
}
-
- if (mtd->sync)
- mtd->sync(mtd);
-
- put_mtd_device(mtd);
- DEBUG(1, "ok\n");
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
MOD_DEC_USE_COUNT;
release_return(0);
}
-static void mtdblock_request(RQFUNC_ARG)
+/*
+ * This is a special request_fn because it is executed in a process context
+ * to be able to sleep independently of the caller. The io_request_lock
+ * is held upon entry and exit.
+ * The head of our request queue is considered active so there is no need
+ * to dequeue requests before we are done.
+ */
+static void handle_mtdblock_request(void)
{
- struct request *current_request;
- unsigned int res = 0;
- struct mtd_info *mtd;
-
- while (1)
- {
- /* Grab the Request and unlink it from the request list, INIT_REQUEST
- will execute a return if we are done. */
- INIT_REQUEST;
- current_request = CURRENT;
-
- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
- {
- printk("mtd: Unsupported device!\n");
- end_request(0);
- continue;
- }
-
- // Grab our MTD structure
-
- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
- if (!mtd) {
- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
- end_request(0);
- }
-
- if (current_request->sector << 9 > mtd->size ||
- (current_request->sector + current_request->nr_sectors) << 9 > mtd->size)
- {
- printk("mtd: Attempt to read past end of device!\n");
- printk("size: %lx, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors);
- end_request(0);
- continue;
- }
-
- /* Remove the request we are handling from the request list so nobody messes
- with it */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- blkdev_dequeue_request(current_request);
-
- /* Now drop the lock that the ll_rw_blk functions grabbed for us
- and process the request. This is necessary due to the extreme time
- we spend processing it. */
- spin_unlock_irq(&io_request_lock);
-#endif
+ struct request *req;
+ struct mtdblk_dev *mtdblk;
+ unsigned int res;
+
+ for (;;) {
+ INIT_REQUEST;
+ req = CURRENT;
+ spin_unlock_irq(&io_request_lock);
+ mtdblk = mtdblks[MINOR(req->rq_dev)];
+ res = 0;
+
+ if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)
+ panic(__FUNCTION__": minor out of bound");
+
+ if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
+ goto end_req;
+
+ // Handle the request
+ switch (req->cmd)
+ {
+ int err;
+
+ case READ:
+ down(&mtdblk->cache_sem);
+ err = do_cached_read (mtdblk, req->sector << 9,
+ req->current_nr_sectors << 9,
+ req->buffer);
+ up(&mtdblk->cache_sem);
+ if (!err)
+ res = 1;
+ break;
+
+ case WRITE:
+ // Read only device
+ if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
+ break;
+
+ // Do the write
+ down(&mtdblk->cache_sem);
+ err = do_cached_write (mtdblk, req->sector << 9,
+ req->current_nr_sectors << 9,
+ req->buffer);
+ up(&mtdblk->cache_sem);
+ if (!err)
+ res = 1;
+ break;
+ }
+
+end_req:
+ spin_lock_irq(&io_request_lock);
+ end_request(res);
+ }
+}
- // Handle the request
- switch (current_request->cmd)
- {
- size_t retlen;
-
- case READ:
- if (mtd->read(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- case WRITE:
-//printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
-// current_request->nr_sectors);
-
- // Read only device
- if ((mtd->flags & MTD_CAP_RAM) == 0)
- {
- res = 0;
- break;
- }
-
- // Do the write
- if (mtd->write(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- // Shouldn't happen
- default:
- printk("mtd: unknown request\n");
- break;
- }
-
- // Grab the lock and re-thread the item onto the linked list
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- spin_lock_irq(&io_request_lock);
- mtdblock_end_request(current_request, res);
+static volatile int leaving = 0;
+#if LINUX_VERSION_CODE > 0x020300
+static DECLARE_MUTEX_LOCKED(thread_sem);
+static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
#else
- end_request(res);
+static struct semaphore thread_sem = MUTEX_LOCKED;
+DECLARE_WAIT_QUEUE_HEAD(thr_wq);
#endif
- }
+
+int mtdblock_thread(void *dummy)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+
+ tsk->session = 1;
+ tsk->pgrp = 1;
+ /* we might get involved when memory gets low, so use PF_MEMALLOC */
+ tsk->flags |= PF_MEMALLOC;
+ strcpy(tsk->comm, "mtdblockd");
+ tsk->tty = NULL;
+ spin_lock_irq(&tsk->sigmask_lock);
+ sigfillset(&tsk->blocked);
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+ exit_mm(tsk);
+ exit_files(tsk);
+ exit_sighand(tsk);
+ exit_fs(tsk);
+
+ while (!leaving) {
+ add_wait_queue(&thr_wq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irq(&io_request_lock);
+ if (QUEUE_EMPTY || QUEUE_PLUGGED) {
+ spin_unlock_irq(&io_request_lock);
+ schedule();
+ remove_wait_queue(&thr_wq, &wait);
+ } else {
+ remove_wait_queue(&thr_wq, &wait);
+ set_current_state(TASK_RUNNING);
+ handle_mtdblock_request();
+ spin_unlock_irq(&io_request_lock);
+ }
+ }
+
+ up(&thread_sem);
+ return 0;
}
+#if LINUX_VERSION_CODE < 0x20300
+#define RQFUNC_ARG void
+#else
+#define RQFUNC_ARG request_queue_t *q
+#endif
+
+static void mtdblock_request(RQFUNC_ARG)
+{
+ /* Don't do anything, except wake the thread if necessary */
+ wake_up(&thr_wq);
+}
static int mtdblock_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
- struct mtd_info *mtd;
+ struct mtdblk_dev *mtdblk;
- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
+ mtdblk = mtdblks[MINOR(inode->i_rdev)];
- if (!mtd) return -EINVAL;
+#ifdef PARANOIA
+ if (!mtdblk)
+ BUG();
+#endif
switch (cmd) {
case BLKGETSIZE: /* Return device size */
- if (!arg) return -EFAULT;
- return put_user((mtd->size >> 9),
- (long *) arg);
+ if (!arg)
+ return -EFAULT;
+ return put_user((mtdblk->mtd->size >> 9),
+ (long *) arg)?-EFAULT:0;
case BLKFLSBUF:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
#endif
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
- if (mtd->sync)
- mtd->sync(mtd);
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
return 0;
default:
}
}
- /*}}}*/
#if LINUX_VERSION_CODE < 0x20326
static struct file_operations mtd_fops =
{
};
#endif
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#ifdef CONFIG_DEVFS_FS
+/* Notification that a new device has been added. Create the devfs entry for
+ * it. */
+
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+ char name[8];
+
+ if (!mtd)
+ return;
+
+ sprintf(name, "%d", mtd->index);
+ devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index,
+ S_IFBLK | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+
+ devfs_unregister(devfs_rw_handle[mtd->index]);
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtdblock init_module
#define cleanup_mtdblock cleanup_module
#endif
-#define __exit
-#endif
-
int __init init_mtdblock(void)
{
int i;
+ spin_lock_init(&mtdblks_lock);
+#ifdef CONFIG_DEVFS_FS
+ if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_BLOCK_MAJOR);
+ return -EAGAIN;
+ }
+
+ devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+ register_mtd_user(¬ifier);
+#else
if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_BLOCK_MAJOR);
return -EAGAIN;
}
+#endif
/* We fill it in at open() time. */
for (i=0; i< MAX_MTD_DEVICES; i++) {
mtd_sizes[i] = 0;
+ mtd_blksizes[i] = BLOCK_SIZE;
}
-
+ init_waitqueue_head(&thr_wq);
/* Allow the block size to default to BLOCK_SIZE. */
- blksize_size[MAJOR_NR] = NULL;
+ blksize_size[MAJOR_NR] = mtd_blksizes;
blk_size[MAJOR_NR] = mtd_sizes;
#if LINUX_VERSION_CODE < 0x20320
#else
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
#endif
+ kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
return 0;
}
static void __exit cleanup_mtdblock(void)
{
+ leaving = 1;
+ wake_up(&thr_wq);
+ down(&thread_sem);
+#ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(¬ifier);
+ devfs_unregister(devfs_dir_handle);
+ devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
+#else
unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+#endif
+#if LINUX_VERSION_CODE < 0x20320
+ blk_dev[MAJOR_NR].request_fn = NULL;
+#else
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+#endif
+ blksize_size[MAJOR_NR] = NULL;
+ blk_size[MAJOR_NR] = NULL;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_mtdblock);
module_exit(cleanup_mtdblock);
-#endif
/*
- * $Id: mtdchar.c,v 1.7 2000/06/30 15:54:19 dwmw2 Exp $
+ * Almost: $Id: mtdchar.c,v 1.21 2000/12/09 21:15:12 dwmw2 Exp $
+ * (With some of the compatibility for previous kernels taken out)
*
* Character-device access to raw MTD devices.
*
#include <linux/mtd/mtd.h>
#include <linux/malloc.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 struct mtd_notifier notifier = {
+ mtd_notify_add,
+ mtd_notify_remove,
+ NULL
+};
+static devfs_handle_t devfs_dir_handle = NULL;
+static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
+static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
+#endif
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
#else
struct mtd_info *mtd=(struct mtd_info *)file->private_data;
switch (orig) {
- case 0:
+ case 0:
/* SEEK_SET */
file->f_pos = offset;
break;
- case 1:
+ case 1:
/* SEEK_CUR */
file->f_pos += offset;
break;
/* SEEK_END */
file->f_pos =mtd->size + offset;
break;
- default:
+ default:
return -EINVAL;
}
- if (file->f_pos < 0)
+ if (file->f_pos < 0)
file->f_pos = 0;
else if (file->f_pos >= mtd->size)
file->f_pos = mtd->size - 1;
int devnum = minor >> 1;
struct mtd_info *mtd;
- DEBUG(0, "MTD_open\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
if (devnum >= MAX_MTD_DEVICES)
return -ENODEV;
{
struct mtd_info *mtd;
- DEBUG(0, "MTD_close\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
mtd = (struct mtd_info *)file->private_data;
int ret=0;
char *kbuf;
- DEBUG(0,"MTD_read\n");
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
if (FILE_POS + count > mtd->size)
count = mtd->size - FILE_POS;
return 0;
/* FIXME: Use kiovec in 2.3 or 2.2+rawio, or at
- * least split the IO into smaller chunks.
+ * least split the IO into smaller chunks.
*/
kbuf = vmalloc(count);
size_t retlen;
int ret=0;
- DEBUG(0,"MTD_write\n");
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
if (FILE_POS == mtd->size)
return -ENOSPC;
int ret = 0;
u_long size;
- DEBUG(0, "MTD_ioctl\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (cmd & IOC_IN) {
switch (cmd) {
case MEMGETINFO:
- copy_to_user((struct mtd_info *)arg, mtd,
- sizeof(struct mtd_info_user));
+ if (copy_to_user((struct mtd_info *)arg, mtd,
+ sizeof(struct mtd_info_user)))
+ return -EFAULT;
break;
case MEMERASE:
init_waitqueue_head(&waitq);
memset (erase,0,sizeof(struct erase_info));
- copy_from_user(&erase->addr, (u_long *)arg,
- 2 * sizeof(u_long));
+ if (copy_from_user(&erase->addr, (u_long *)arg,
+ 2 * sizeof(u_long))) {
+ kfree(erase);
+ return -EFAULT;
+ }
erase->mtd = mtd;
erase->callback = mtd_erase_callback;
erase->priv = (unsigned long)&waitq;
- /* FIXME: Allow INTERRUPTIBLE. Which means
- not having the wait_queue head on the stack
-
- Does it? Why? Who wrote this? Was it my alter
- ago - the intelligent one? Or was it the stupid
- one, and now I'm being clever I don't know what
- it was on about?
-
- dwmw2.
-
- It was the intelligent one. If the wq_head is
- on the stack, and we leave because we got
- interrupted, then the wq_head is no longer
- there when the callback routine tries to
- wake us up --> BOOM!.
-
+ /*
+ FIXME: Allow INTERRUPTIBLE. Which means
+ not having the wait_queue head on the stack.
+
+ If the wq_head is on the stack, and we
+ leave because we got interrupted, then the
+ wq_head is no longer there when the
+ callback routine tries to wake us up.
*/
current->state = TASK_UNINTERRUPTIBLE;
add_wait_queue(&waitq, &wait);
void *databuf;
ssize_t retlen;
- copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf));
+ if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
if (!databuf)
return -ENOMEM;
- copy_from_user(databuf, buf.ptr, buf.length);
+ if (copy_from_user(databuf, buf.ptr, buf.length))
+ return -EFAULT;
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t));
+ if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)))
+ ret = -EFAULT;
kfree(databuf);
break;
void *databuf;
ssize_t retlen;
- copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf));
+ if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t));
-
- if (retlen)
- copy_to_user(buf.ptr, databuf, retlen);
-
+ if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)))
+ ret = -EFAULT;
+ else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+ ret = -EFAULT;
+
kfree(databuf);
break;
}
-
-
-
-
+ case MEMLOCK:
+ {
+ unsigned long adrs[2];
+
+ if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
+ return -EFAULT;
+
+ if (!mtd->lock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->lock(mtd, adrs[0], adrs[1]);
+ }
+
+ case MEMUNLOCK:
+ {
+ unsigned long adrs[2];
+
+ if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
+ return -EFAULT;
+
+ if (!mtd->unlock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->unlock(mtd, adrs[0], adrs[1]);
+ }
+
+
default:
printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO);
ret = -EINVAL;
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#ifdef CONFIG_DEVFS_FS
+/* Notification that a new device has been added. Create the devfs entry for
+ * it. */
+
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+ char name[8];
+
+ if (!mtd)
+ return;
+
+ sprintf(name, "%d", mtd->index);
+ devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+
+ sprintf(name, "%dro", mtd->index);
+ devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+
+ devfs_unregister(devfs_rw_handle[mtd->index]);
+ devfs_unregister(devfs_ro_handle[mtd->index]);
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtdchar init_module
#define cleanup_mtdchar cleanup_module
#endif
-#endif
mod_init_t init_mtdchar(void)
{
-
- if (register_chrdev(MTD_CHAR_MAJOR,"mtd",&mtd_fops)) {
+#ifdef CONFIG_DEVFS_FS
+ int i;
+ char name[8];
+ struct mtd_info* mtd;
+
+ if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_CHAR_MAJOR);
+ return -EAGAIN;
+ }
+
+ devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
+
+ register_mtd_user(¬ifier);
+#else
+ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
+#endif
return 0;
}
mod_exit_t cleanup_mtdchar(void)
{
- unregister_chrdev(MTD_CHAR_MAJOR,"mtd");
+#ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(¬ifier);
+ devfs_unregister(devfs_dir_handle);
+ devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+#else
+ unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+#endif
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_mtdchar);
module_exit(cleanup_mtdchar);
-#endif
/*
- * $Id: mtdcore.c,v 1.13 2000/07/13 14:27:37 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.27 2000/12/10 01:10:09 dwmw2 Exp $
*
- * Core registration and callback routines for MTD
+ * Core registration and callback routines for MTD
* drivers and users.
*
*/
-#ifdef MTD_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
-#ifdef MTD_DEBUG
-static int debug = MTD_DEBUG;
-MODULE_PARM(debug, "i");
-#endif
-
-/* Init code required for 2.2 kernels */
-
-#if LINUX_VERSION_CODE < 0x20300
-
-#ifdef CONFIG_MTD_DOC1000
-extern int init_doc1000(void);
-#endif
-#ifdef CONFIG_MTD_DOCPROBE
-extern int init_doc(void);
-#endif
-#ifdef CONFIG_MTD_PHYSMAP
-extern int init_physmap(void);
-#endif
-#ifdef CONFIG_MTD_RPXLITE
-extern int init_rpxlite(void);
-#endif
-#ifdef CONFIG_MTD_OCTAGON
-extern int init_octagon5066(void);
-#endif
-#ifdef CONFIG_MTD_PNC2000
-extern int init_pnc2000(void);
-#endif
-#ifdef CONFIG_MTD_VMAX
-extern int init_vmax301(void);
-#endif
-#ifdef CONFIG_MTD_MIXMEM
-extern int init_mixmem(void);
-#endif
-#ifdef CONFIG_MTD_PMC551
-extern int init_pmc551(void);
-#endif
-#ifdef CONFIG_MTD_NORA
-extern int init_nora(void);
-#endif
-#ifdef CONFIG_FTL
-extern int init_ftl(void);
-#endif
-#ifdef CONFIG_NFTL
-extern int init_nftl(void);
-#endif
-#ifdef CONFIG_MTD_BLOCK
-extern int init_mtdblock(void);
-#endif
-#ifdef CONFIG_MTD_CHAR
-extern int init_mtdchar(void);
-#endif
-
-#endif /* LINUX_VERSION_CODE < 0x20300 */
-
-
static DECLARE_MUTEX(mtd_table_mutex);
static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
static struct mtd_notifier *mtd_notifiers = NULL;
/**
- * add_mtd_device - register an MTD device
+ * add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
*
* Add a device to the list of MTD devices present in the system, and
struct mtd_notifier *not=mtd_notifiers;
mtd_table[i] = mtd;
+ mtd->index = i;
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
while (not)
{
}
/**
- * del_mtd_device - unregister an MTD device
+ * del_mtd_device - unregister an MTD device
* @mtd: pointer to MTD device info structure
*
* Remove a device from the list of MTD devices present in the system,
* @new: pointer to notifier info structure
*
* Removes a callback function pair from the list of 'users' to be
- * notified upon addition or removal of MTD devices. Causes the
+ * notified upon addition or removal of MTD devices. Causes the
* 'remove' callback to be immediately invoked for each MTD device
* currently present in the system.
*/
* @mtd: last known address of the required MTD device
* @num: internal device number of the required MTD device
*
- * Given a number and NULL address, return the num'th entry in the device
+ * Given a number and NULL address, return the num'th entry in the device
* table, if any. Given an address and num == -1, search the device table
* for a device with that address and return if it's still present. Given
* both, return the num'th driver only if its address matches. Return NULL
{
struct mtd_info *this = mtd_table[i];
- if (!this)
+ if (!this)
return 0;
- return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size,
+ return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size,
this->name);
}
{
int len = 0, l, i;
off_t begin = 0;
-
+
down(&mtd_table_mutex);
for (i=0; i< MAX_MTD_DEVICES; i++) {
/*====================================================================*/
/* Init code */
-#if LINUX_VERSION_CODE < 0x20300
-
-static inline void init_others(void)
-{
- /* Shedloads of calls to init functions of all the
- * other drivers and users of MTD, which we can
- * ditch in 2.3 because of the sexy new way of
- * finding init routines.
- */
-#ifdef CONFIG_MTD_DOC1000
- init_doc1000();
-#endif
-#ifdef CONFIG_MTD_DOCPROBE
- init_doc(); /* This covers both the DiskOnChip 2000
- * and the DiskOnChip Millennium.
- * Theoretically all other DiskOnChip
- * devices too. */
-#endif
-#ifdef CONFIG_MTD_PHYSMAP
- init_physmap();
-#endif
-#ifdef CONFIG_MTD_RPXLITE
- init_rpxlite();
-#endif
-#ifdef CONFIG_MTD_OCTAGON
- init_octagon5066();
-#endif
-#ifdef CONFIG_MTD_PNC2000
- init_pnc2000();
-#endif
-#ifdef CONFIG_MTD_VMAX
- init_vmax301();
-#endif
-#ifdef CONFIGF_MTD_MIXMEM
- init_mixmem();
-#endif
-#ifdef CONFIG_MTD_PMC551
- init_pmc551();
-#endif
-#ifdef CONFIG_MTD_NORA
- init_nora();
-#endif
-#ifdef CONFIG_MTD_MTDRAM
- init_mtdram();
-#endif
-#ifdef CONFIG_FTL
- init_ftl();
-#endif
-#ifdef CONFIG_NFTL
- init_nftl();
-#endif
-#ifdef CONFIG_MTD_BLOCK
- init_mtdblock();
-#endif
-#ifdef CONFIG_MTD_CHAR
- init_mtdchar();
-#endif
-}
-
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtd init_module
#define cleanup_mtd cleanup_module
#endif
-#endif /* LINUX_VERSION_CODE < 0x20300 */
-
mod_init_t init_mtd(void)
{
- int i;
- DEBUG(1, "INIT_MTD:\n");
- for (i=0; i<MAX_MTD_DEVICES; i++)
- mtd_table[i]=NULL;
-
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
#else
proc_register_dynamic(&proc_root,&mtd_proc_entry);
#endif
-
#endif
-#if LINUX_VERSION_CODE < 0x20300
- init_others();
+#if LINUX_VERSION_CODE < 0x20212
+ init_mtd_devices();
#endif
+
#ifdef CONFIG_PM
mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);
#endif
mod_exit_t cleanup_mtd(void)
{
- unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
#ifdef CONFIG_PM
if (mtd_pm_dev) {
pm_unregister(mtd_pm_dev);
mtd_pm_dev = NULL;
}
#endif
+
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if (proc_mtd)
#endif
#endif
}
-
-#if LINUX_VERSION_CODE > 0x20300
+
module_init(init_mtd);
module_exit(cleanup_mtd);
-#endif
--- /dev/null
+/*
+ * Simple MTD partitioning layer
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is GPL
+ *
+ * $Id: mtdpart.c,v 1.7 2000/12/09 23:29:47 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/list.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+
+/* Our partition linked list */
+static LIST_HEAD(mtd_partitions);
+
+/* Our partition node structure */
+struct mtd_part {
+ struct mtd_info mtd;
+ struct mtd_info *master;
+ loff_t offset;
+ int index;
+ struct list_head list;
+};
+
+/*
+ * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
+ * the pointer to that structure with this macro.
+ */
+#define PART(x) ((struct mtd_part *)(x))
+
+
+/*
+ * MTD methods which simply translate the effective address and pass through
+ * to the _real_ device.
+ */
+
+static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
+ return part->master->read (part->master, from + part->offset,
+ len, retlen, buf);
+}
+
+static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
+}
+
+static int part_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return part->master->writev (part->master, vecs, count,
+ to + part->offset, retlen);
+}
+
+static int part_readv (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->readv (part->master, vecs, count,
+ from + part->offset, retlen);
+}
+
+static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct mtd_part *part = PART(mtd);
+ 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);
+}
+
+static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->lock(part->master, ofs + part->offset, len);
+}
+
+static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->unlock(part->master, ofs + part->offset, len);
+}
+
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
+ * attached to the given master MTD object.
+ */
+
+int del_mtd_partitions(struct mtd_info *master)
+{
+ struct list_head *node;
+ struct mtd_part *slave;
+
+ for (node = mtd_partitions.next;
+ node != &mtd_partitions;
+ node = node->next) {
+ slave = list_entry(node, struct mtd_part, list);
+ if (slave->master == master) {
+ struct list_head *prev = node->prev;
+ __list_del(prev, node->next);
+ del_mtd_device(&slave->mtd);
+ kfree(slave);
+ node = prev;
+ MOD_DEC_USE_COUNT;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * This function, given a master MTD object and a partition table, creates
+ * and registers slave MTD objects which are bound to the master according to
+ * the partition definitions.
+ * (Q: should we register the master MTD object as well?)
+ */
+
+int add_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition *parts,
+ int nbparts)
+{
+ struct mtd_part *slave;
+ u_long cur_offset = 0;
+ int i;
+
+ for (i = 0; i < nbparts; i++) {
+ /* allocate the partition structure */
+ slave = kmalloc (sizeof(*slave), GFP_KERNEL);
+ if (!slave) {
+ printk ("memory allocation error while creating partitions for \"%s\"\n",
+ master->name);
+ del_mtd_partitions(master);
+ return -ENOMEM;
+ }
+ list_add(&slave->list, &mtd_partitions);
+
+ /* set up the MTD object for this partition */
+ slave->mtd = *master;
+ slave->mtd.name = parts[i].name;
+ slave->mtd.size = parts[i].size;
+ slave->mtd.flags &= ~parts[i].mask_flags;
+ slave->mtd.read = part_read;
+ slave->mtd.write = part_write;
+ if (slave->mtd.writev)
+ slave->mtd.writev = part_writev;
+ if (slave->mtd.readv)
+ slave->mtd.readv = part_readv;
+ if (slave->mtd.lock)
+ slave->mtd.lock = part_lock;
+ if (slave->mtd.unlock)
+ slave->mtd.unlock = part_unlock;
+ slave->mtd.erase = part_erase;
+ slave->master = master;
+ slave->offset = parts[i].offset;
+ slave->index = i;
+
+ if (slave->offset == 0)
+ slave->offset = cur_offset;
+ if (slave->mtd.size == 0)
+ slave->mtd.size = master->size - slave->offset;
+ cur_offset = slave->offset + slave->mtd.size;
+
+ /* let's do some sanity checks */
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (parts[i].offset % master->erasesize)) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+ parts[i].name);
+ }
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (parts[i].size % master->erasesize)) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+ parts[i].name);
+ }
+ if (parts[i].offset >= master->size) {
+ /* let's register it anyway to preserve ordering */
+ slave->offset = 0;
+ slave->mtd.size = 0;
+ printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
+ parts[i].name);
+ }
+ if (parts[i].offset + parts[i].size > master->size) {
+ slave->mtd.size = master->size - parts[i].offset;
+ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#lx\n",
+ parts[i].name, master->name, slave->mtd.size);
+ }
+
+ /* register our partition */
+ add_mtd_device(&slave->mtd);
+ MOD_INC_USE_COUNT;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(add_mtd_partitions);
+EXPORT_SYMBOL(del_mtd_partitions);
-
/* Linux driver for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@infradead.org> */
-/* $Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $ */
+/* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $ */
/*
- The contents of this file are distributed under the GNU Public
- Licence version 2 ("GPL"). The legal note below refers only to the
- _use_ of the code in some jurisdictions, and does not in any way
- affect the copying, distribution and modification of this code,
- which is permitted under the terms of the GPL.
+ The contents of this file are distributed under the GNU General
+ Public License version 2 ("GPL"). The author places no additional
+ restrictions of any kind on it. However, local legislation in some
+ countries may restrict the use of the algorithms implemented by this
+ code in certain circumstances.
- Section 0 of the GPL says:
+ The legal note below refers only to the _use_ of the code in the
+ affected jurisdictions, and does not in any way affect the copying,
+ distribution and modification of this code, which are permitted, and
+ indeed required, under the terms of the GPL.
+ Section 0 of the GPL says:
"Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope."
You may copy, distribute and modify this code to your hearts'
content - it's just that in some jurisdictions, you may only _use_
- it under the terms of the licence below. This puts it in a similar
- situation to the ISDN code, which you may need telco approval to
- use, and indeed any code which has uses that may be restricted in
- law. For example, certain malicious uses of the networking stack
- may be illegal, but that doesn't prevent the networking code from
- being under GPL.
+ it under the terms of the patent grant below. This puts it in a
+ similar situation to the ISDN code, which you may need telco
+ approval to use, and indeed any code which has uses that may be
+ restricted in law. For example, certain malicious uses of the
+ networking stack may be illegal, but that doesn't prevent the
+ networking code from being under GPL.
In fact the ISDN case is worse than this, because modification of
- the code automatically invalidates its approval. Modificiation,
+ the code automatically invalidates its approval. Modification,
unlike usage, _is_ one of the rights which is protected by the
GPL. Happily, the law in those places where approval is required
doesn't actually prevent you from modifying the code - it's just
because usage isn't addressed by the GPL, that's just fine.
dwmw2@infradead.org
- 6/7/0
+ 30/10/0
LEGAL NOTE: The NFTL format is patented by M-Systems. They have
granted a licence for its use with their DiskOnChip products:
#define PRERELEASE
-#ifdef NFTL_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/init.h>
+#include <linux/blkpg.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/compatmac.h>
-#undef WE_KNOW_WTF_THIS_DOES_NOT_WORK
+/* maximum number of loops while examining next block, to have a
+ chance to detect consistency problems (they should never happen
+ because of the checks done in the mounting */
+
+#define MAX_LOOPS 10000
/* NFTL block device stuff */
#define MAJOR_NR NFTL_MAJOR
#define DEVICE_REQUEST nftl_request
#define DEVICE_OFF(device)
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
-#define LOCAL_END_REQUEST
-#endif
-#include <linux/blk.h>
-#include <linux/hdreg.h>
-
-
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
-
-static void nftl_end_request(struct request *req, int res)
-{
- req->sector += req->current_nr_sectors;
- req->nr_sectors -= req->current_nr_sectors;
-
- if (end_that_request_first( req, res, "nftl" ))
- return;
- end_that_request_last( req );
-}
-#endif
-
-#ifdef NFTL_DEBUG
-static int debug = NFTL_DEBUG;
-MODULE_PARM(debug, "i");
-#endif
+#include <linux/blk.h>
+#include <linux/hdreg.h>
/* Linux-specific block device functions */
* encountered, except ...
*/
-static int nftl_sizes[256]={0,};
+static int nftl_sizes[256] = {0,};
static int nftl_blocksizes[256] = {0,};
/* .. for the Linux partition table handling. */
-
struct hd_struct part_table[256] = {{0,0},};
#if LINUX_VERSION_CODE < 0x20328
#endif
static struct gendisk nftl_gendisk = {
- NFTL_MAJOR, /* Major number */
- "nftl", /* Major name */
+ MAJOR_NR, /* Major number */
+ "nftl", /* Major name */
4, /* Bits to shift to get real from partition */
15, /* Number of partitions per real */
#if LINUX_VERSION_CODE < 0x20328
NULL /* next */
};
-
struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL};
-static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs,
- struct NFTLMediaHeader *hdr)
+static void NFTL_setup(struct mtd_info *mtd)
{
int i;
- struct NFTLrecord *thisNFTL;
+ struct NFTLrecord *nftl;
unsigned long temp;
int firstfree = -1;
- DEBUG(1,"NFTL_setup\n");
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
- for (i=0; i < MAX_NFTLS; i++) {
- if (!NFTLs[i] && firstfree==-1)
+ for (i = 0; i < MAX_NFTLS; i++) {
+ if (!NFTLs[i] && firstfree == -1)
firstfree = i;
- else if (NFTLs[i] && NFTLs[i]->mtd == mtd &&
- NFTLs[i]->MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN) {
+ else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
/* This is a Spare Media Header for an NFTL we've already found */
- DEBUG(1, "Spare Media Header for NFTL %d found at %lx\n",i, ofs);
- NFTLs[i]->SpareMediaUnit = ofs / mtd->erasesize;
+ DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
return;
}
}
-
-
- /* OK, it's a new one. Set up all the data structures. */
-#ifdef PSYCHO_DEBUG
- printk("Found new NFTL nftl%c at offset %lx\n",firstfree + 'a', ofs);
-#endif
- if (hdr->UnitSizeFactor != 0xff) {
- printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n");
+ if (firstfree == -1) {
+ printk(KERN_WARNING "No more NFTL slot available\n");
return;
- }
-
- thisNFTL = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
- if (!thisNFTL) {
+ }
+
+ nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
+ if (!nftl) {
printk(KERN_WARNING "Out of memory for NFTL data structures\n");
return;
}
- init_MUTEX(&thisNFTL->mutex);
- thisNFTL->EraseSize = mtd->erasesize;
- memcpy(&thisNFTL->MediaHdr, hdr, sizeof(*hdr));
- thisNFTL->mtd = mtd;
- thisNFTL->MediaUnit = ofs / mtd->erasesize;
- thisNFTL->SpareMediaUnit = 0xffff;
- thisNFTL->numvunits = le32_to_cpu(thisNFTL->MediaHdr.FormattedSize) / 8192;
- thisNFTL->nr_sects = thisNFTL->numvunits * (thisNFTL->EraseSize / 512);
- thisNFTL->usecount = 0;
- thisNFTL->cylinders = 1024;
- thisNFTL->heads = 16;
+ init_MUTEX(&nftl->mutex);
- temp = thisNFTL->cylinders * thisNFTL->heads;
- thisNFTL->sectors = thisNFTL->nr_sects / temp;
+ /* get physical parameters */
+ nftl->EraseSize = mtd->erasesize;
+ nftl->nb_blocks = mtd->size / mtd->erasesize;
+ nftl->mtd = mtd;
- if (thisNFTL->nr_sects % temp) {
-
- thisNFTL->sectors++;
- temp = thisNFTL->cylinders * thisNFTL->sectors;
- thisNFTL->heads = thisNFTL->nr_sects / temp;
-
- if (thisNFTL->nr_sects & temp) {
- thisNFTL->heads++;
- temp = thisNFTL->heads * thisNFTL->sectors;
-
- thisNFTL->cylinders = thisNFTL->nr_sects / temp;
- }
- }
- if (thisNFTL->nr_sects != thisNFTL->heads * thisNFTL->cylinders *
- thisNFTL->sectors) {
- printk(KERN_WARNING "Cannot calculate an NFTL geometry to match size of 0x%lx.\n", thisNFTL->nr_sects);
- printk(KERN_WARNING "Using C:%d H:%d S:%d (== %lx sects)\n",
- thisNFTL->cylinders, thisNFTL->heads ,
- thisNFTL->sectors,
- (long)thisNFTL->cylinders * (long)thisNFTL->heads *
- (long)thisNFTL->sectors );
-
- /* Oh no we don't
- * thisNFTL->nr_sects = thisNFTL->heads * thisNFTL->cylinders * thisNFTL->sectors;
- */
- }
-
-
- thisNFTL->EUNtable = kmalloc( 2 * thisNFTL->numvunits,
- GFP_KERNEL);
- if (!thisNFTL->EUNtable) {
- printk("ENOMEM\n");
- kfree(thisNFTL);
+ if (NFTL_mount(nftl) < 0) {
+ printk(KERN_WARNING "Could not mount NFTL device\n");
+ kfree(nftl);
return;
- }
- memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits);
-
- thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
- if (!thisNFTL->VirtualUnitTable) {
- printk("ENOMEM\n");
- kfree(thisNFTL->EUNtable);
- kfree(thisNFTL);
- return;
- }
- memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits));
-
- thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
- if (!thisNFTL->ReplUnitTable) {
- printk("ENOMEM\n");
- kfree(thisNFTL->VirtualUnitTable);
- kfree(thisNFTL->EUNtable);
- kfree(thisNFTL);
- return;
- }
- memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) );
-
- /* Ought to check the media header for bad blocks */
- thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) +
- le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1;
- thisNFTL->numfreeEUNs = 0;
+ }
- /* Scan each physical Erase Unit for validity and to find the
- Virtual Erase Unit Chain to which it belongs */
-
- for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
- i <= thisNFTL->lastEUN; i++) {
-
- union nftl_uci uci;
- unsigned long ofs;
- size_t retlen;
- ofs = i * thisNFTL->EraseSize;
-
- MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 512 + 8, 8, &retlen, (char *)&uci);
-
- if (uci.b.EraseMark != cpu_to_le16(0x3c69) ||
- uci.b.EraseMark1 != cpu_to_le16(0x3c69)) {
- printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n",
- i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1));
- thisNFTL->VirtualUnitTable[i] = 0x7fff;
- thisNFTL->ReplUnitTable[i] = 0xffff;
- continue;
- }
-
- MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 8, 8, &retlen, (u_char *)&uci);
-
- if (uci.a.VirtUnitNum != uci.a.SpareVirtUnitNum)
- printk("EUN %d: VirtualUnitNumber (%x) != SpareVirtualUnitNumber (%x)\n",
- i, le16_to_cpu(uci.a.VirtUnitNum),
- le16_to_cpu(uci.a.SpareVirtUnitNum));
-
- if (uci.a.ReplUnitNum != uci.a.SpareReplUnitNum)
- printk("EUN %d: ReplacementUnitNumber (%x) != SpareReplacementUnitNumber (%x)\n",
- i, le16_to_cpu(uci.a.ReplUnitNum),
- le16_to_cpu(uci.a.SpareReplUnitNum));
-
- /* We don't actually _do_ anything about the above, just whinge */
-
- thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum);
- thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum);
-
- /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */
- if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits)
- thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i;
-
- if (uci.a.VirtUnitNum == 0xffff) {
- /* Free block */
- thisNFTL->LastFreeEUN = i;
- thisNFTL->numfreeEUNs++;
- }
-
- }
- NFTLs[firstfree] = thisNFTL;
- thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
-
- //#define PSYCHO_DEBUG
+ /* OK, it's a new one. Set up all the data structures. */
#ifdef PSYCHO_DEBUG
- for (i=0; i < 10/* thisNFTL->numvunits*/; i++) {
- u16 curEUN = thisNFTL->EUNtable[i];
- int sillycount=100;
-
- printk("Virtual Unit #%d: ",i);
- if (!curEUN || curEUN == 0xffff) {
- printk("Not present\n");
- continue;
- }
- printk("%d", curEUN);
-
- while ((curEUN = thisNFTL->ReplUnitTable[curEUN]) != 0xffff && --sillycount) {
- printk(", %d", curEUN & 0xffff);
-
+ printk("Found new NFTL nftl%c\n", firstfree + 'a');
+#endif
+
+ /* linux stuff */
+ nftl->usecount = 0;
+ nftl->cylinders = 1024;
+ nftl->heads = 16;
+
+ temp = nftl->cylinders * nftl->heads;
+ nftl->sectors = nftl->nr_sects / temp;
+ if (nftl->nr_sects % temp) {
+ nftl->sectors++;
+ temp = nftl->cylinders * nftl->sectors;
+ nftl->heads = nftl->nr_sects / temp;
+
+ if (nftl->nr_sects % temp) {
+ nftl->heads++;
+ temp = nftl->heads * nftl->sectors;
+ nftl->cylinders = nftl->nr_sects / temp;
}
- printk("\n");
}
-#endif
- /* OK. Now we deal with the fact that we're in the real world. Sometimes
- things don't actually happen the way they're supposed to. Find, fix,
- and whinge about the most common deviations from spec that we have
- been known to encounter.
- */
- /* Except that I haven't implemented that bit yet :) */
+ if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
+ printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
+ "match size of 0x%lx.\n", nftl->nr_sects);
+ printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
+ nftl->cylinders, nftl->heads , nftl->sectors,
+ (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+ /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
+ }
+ NFTLs[firstfree] = nftl;
/* Finally, set up the block device sizes */
- nftl_sizes[firstfree * 16]=thisNFTL->nr_sects;
-// nftl_blocksizes[firstfree*16] = 512;
- part_table[firstfree * 16].nr_sects = thisNFTL->nr_sects;
+ nftl_sizes[firstfree * 16] = nftl->nr_sects;
+ //nftl_blocksizes[firstfree*16] = 512;
+ part_table[firstfree * 16].nr_sects = nftl->nr_sects;
+
+ /* partition check ... */
#if LINUX_VERSION_CODE < 0x20328
resetup_one_dev(&nftl_gendisk, firstfree);
#else
- grok_partitions(&nftl_gendisk, firstfree, 1<<4, thisNFTL->nr_sects);
+ grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects);
#endif
-
}
-
static void NFTL_unsetup(int i)
{
- struct NFTLrecord *thisNFTL = NFTLs[i];
+ struct NFTLrecord *nftl = NFTLs[i];
- DEBUG(1, "NFTL_unsetup %d\n", i);
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
NFTLs[i] = NULL;
- if (thisNFTL->VirtualUnitTable)
- kfree(thisNFTL->VirtualUnitTable);
- if (thisNFTL->ReplUnitTable)
- kfree(thisNFTL->ReplUnitTable);
- if (thisNFTL->EUNtable)
- kfree(thisNFTL->EUNtable);
+ if (nftl->ReplUnitTable)
+ kfree(nftl->ReplUnitTable);
+ if (nftl->EUNtable)
+ kfree(nftl->EUNtable);
- kfree(thisNFTL);
+ kfree(nftl);
}
-
-
-
/* Search the MTD device for NFTL partitions */
static void NFTL_notify_add(struct mtd_info *mtd)
{
- int i;
- unsigned long ofs;
- struct NFTLMediaHeader hdr;
-
- DEBUG(1, "NFTL_notify_add for %s\n", mtd->name);
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
if (mtd) {
- if (!mtd->read_oob) /* If this MTD doesn't have out-of-band data,
- then there's no point continuing */
- {
- DEBUG(1, "No OOB data, quitting\n");
+ if (!mtd->read_oob) {
+ /* If this MTD doesn't have out-of-band data,
+ then there's no point continuing */
+ DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
return;
}
- DEBUG(3, "mtd->read = %p,size = %d, erasesize = %d\n",
- mtd->read, mtd->size, mtd->erasesize);
- for (ofs = 0; ofs < mtd->size ; ofs += mtd->erasesize) {
- size_t retlen = 0;
- MTD_READ(mtd, ofs, sizeof(hdr), &retlen, (u_char *)&hdr);
-
- if (retlen < sizeof(hdr))
- {
- continue;
- }
-
- if (!strncmp(hdr.DataOrgID, "ANAND", 6)) {
- DEBUG(2, "Valid NFTL partition at ofs %ld\n", ofs);
- NFTL_setup(mtd, ofs, &hdr);
- }
- else {
- DEBUG(3,"No valid NFTL Partition at ofs %d\n", ofs);
- for(i = 0; i < 6; i++) {
- DEBUG(3,"%x, ", hdr.DataOrgID[i]);
- }
- DEBUG(3," = %s\n", hdr.DataOrgID);
- DEBUG(3,"%d, %d, %d, %d\n", hdr.NumEraseUnits, hdr.FirstPhysicalEUN,
- hdr.FormattedSize, hdr.UnitSizeFactor);
+ DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
+ mtd->read, mtd->size, mtd->erasesize);
- }
- }
- return;
+ NFTL_setup(mtd);
}
}
{
int i;
- for (i=0; i< MAX_NFTLS; i++) {
+ for (i = 0; i < MAX_NFTLS; i++) {
if (NFTLs[i] && NFTLs[i]->mtd == mtd)
NFTL_unsetup(i);
}
}
-
#ifdef CONFIG_NFTL_RW
/* Actual NFTL access routines */
-
-
-static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate )
+/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
+ * when the give Virtual Unit Chain
+ */
+static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
{
/* For a given Virtual Unit Chain: find or create a free block and
add it to the chain */
/* We're passed the number of the last EUN in the chain, to save us from
having to look it up again */
-
- u16 pot = thisNFTL->LastFreeEUN;
+ u16 pot = nftl->LastFreeEUN;
int silly = -1;
/* Normally, we force a fold to happen before we run out of free blocks completely */
-
- if (!desperate && thisNFTL->numfreeEUNs < 2) {
- // printk("NFTL_findfreeblock: there are too few free EUNs\n");
+ if (!desperate && nftl->numfreeEUNs < 2) {
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
return 0xffff;
}
/* Scan for a free block */
-
do {
- if (thisNFTL->VirtualUnitTable[pot] == 0xffff) {
- thisNFTL->LastFreeEUN = pot;
- thisNFTL->numfreeEUNs--;
+ if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
+ nftl->LastFreeEUN = pot;
+ nftl->numfreeEUNs--;
return pot;
}
- if (++pot > thisNFTL->lastEUN)
- pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
+ /* This will probably point to the MediaHdr unit itself,
+ right at the beginning of the partition. But that unit
+ (and the backup unit too) should have the UCI set
+ up so that it's not selected for overwriting */
+ if (++pot > nftl->lastEUN)
+ pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
if (!silly--) {
- printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n",
- thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN));
+ printk("Argh! No free blocks found! LastFreeEUN = %d, "
+ "FirstEUN = %d\n", nftl->LastFreeEUN,
+ le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
return 0xffff;
}
-
- } while (pot != thisNFTL->LastFreeEUN);
+ } while (pot != nftl->LastFreeEUN);
return 0xffff;
}
-
-
-
-
-static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock )
+static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
{
- u16 BlockMap[thisNFTL->EraseSize / 512];
- unsigned char BlockLastState[thisNFTL->EraseSize / 512];
- unsigned char BlockFreeFound[thisNFTL->EraseSize / 512];
- u16 thisEUN;
+ u16 BlockMap[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
+ unsigned int thisEUN;
int block;
- int silly = -1;
- u16 targetEUN = 0xffff;
+ int silly;
+ unsigned int targetEUN;
struct nftl_oob oob;
int inplace = 1;
+ size_t retlen;
memset(BlockMap, 0xff, sizeof(BlockMap));
memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
- thisEUN = thisNFTL->EUNtable[thisVUC];
+ thisEUN = nftl->EUNtable[thisVUC];
- if (thisEUN == 0xffff) {
- printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC);
- return 0xffff;
+ if (thisEUN == BLOCK_NIL) {
+ printk(KERN_WARNING "Trying to fold non-existent "
+ "Virtual Unit Chain %d!\n", thisVUC);
+ return BLOCK_NIL;
}
/* Scan to find the Erase Unit which holds the actual data for each
512-byte block within the Chain.
*/
+ silly = MAX_LOOPS;
+ targetEUN = BLOCK_NIL;
+ while (thisEUN <= nftl->lastEUN ) {
+ unsigned int status, foldmark;
- while( thisEUN <= thisNFTL->lastEUN ) {
- size_t retlen;
-
targetEUN = thisEUN;
-
- for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) {
-
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob);
-
+ for (block = 0; block < nftl->EraseSize / 512; block ++) {
+ MTD_READOOB(nftl->mtd,
+ (thisEUN * nftl->EraseSize) + (block * 512),
+ 16 , &retlen, (char *)&oob);
if (block == 2) {
- if (oob.u.c.WriteInh != 0xffffffff) {
- printk("Write Inhibited on EUN %d\n", thisEUN);
+ foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
+ if (foldmark == FOLD_MARK_IN_PROGRESS) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "Write Inhibited on EUN %d\n", thisEUN);
inplace = 0;
} else {
/* There's no other reason not to do inplace,
inplace = 1;
}
}
+ status = oob.b.Status | oob.b.Status1;
+ BlockLastState[block] = status;
- BlockLastState[block] = (unsigned char) oob.b.Status & 0xff;
-
- switch(oob.b.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
- BlockFreeFound[block]=1;
+ switch(status) {
+ case SECTOR_FREE:
+ BlockFreeFound[block] = 1;
break;
- case __constant_cpu_to_le16(BLOCK_USED):
+ case SECTOR_USED:
if (!BlockFreeFound[block])
BlockMap[block] = thisEUN;
else
- printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block);
+ printk(KERN_WARNING
+ "SECTOR_USED found after SECTOR_FREE "
+ "in Virtual Unit Chain %d for block %d\n",
+ thisVUC, block);
break;
- case __constant_cpu_to_le16(BLOCK_IGNORE):
- case __constant_cpu_to_le16(BLOCK_DELETED):
+ case SECTOR_IGNORE:
+ case SECTOR_DELETED:
break;
default:
- printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status);
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
}
}
if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
- return 0xffff;
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
+ return BLOCK_NIL;
}
- thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
+ thisEUN = nftl->ReplUnitTable[thisEUN];
}
if (inplace) {
/* We're being asked to be a fold-in-place. Check
- that all blocks are either present or BLOCK_FREE
+ that all blocks are either present or SECTOR_FREE
in the target block. If not, we're going to have
to fold out-of-place anyway.
*/
-
- for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
-
- if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) &&
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
+ if (BlockLastState[block] != SECTOR_FREE &&
BlockMap[block] != targetEUN) {
- DEBUG(1, "Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n",
- thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN);
-
+ DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
+ "block %d was %x lastEUN, "
+ "and is in EUN %d (%s) %d\n",
+ thisVUC, block, BlockLastState[block],
+ BlockMap[block],
+ BlockMap[block]== targetEUN ? "==" : "!=",
+ targetEUN);
inplace = 0;
break;
}
}
- if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) &&
- pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) &&
- BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] !=
- (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) {
- DEBUG(1, "Pending write not free in EUN %d. Folding out of place.\n", targetEUN);
+ if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
+ pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
+ BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
+ SECTOR_FREE) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
+ "Folding out of place.\n", targetEUN);
inplace = 0;
}
-
}
if (!inplace) {
- DEBUG(1, "Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", thisVUC);
+ DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
+ "Trying out-of-place\n", thisVUC);
/* We need to find a targetEUN to fold into. */
- targetEUN = NFTL_findfreeblock(thisNFTL, 1);
- if (targetEUN == 0xffff) {
- /* Ouch. Now we're screwed. We need to do a
- fold-in-place of another chain to make room
- for this one. We need a better way of selecting
- which chain to fold, because makefreeblock will
- only ask us to fold the same one again.
- */
- printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n");
- return 0xffff;
+ targetEUN = NFTL_findfreeblock(nftl, 1);
+ if (targetEUN == BLOCK_NIL) {
+ /* Ouch. Now we're screwed. We need to do a
+ fold-in-place of another chain to make room
+ for this one. We need a better way of selecting
+ which chain to fold, because makefreeblock will
+ only ask us to fold the same one again.
+ */
+ printk(KERN_WARNING
+ "NFTL_findfreeblock(desperate) returns 0xffff.\n");
+ return BLOCK_NIL;
}
-
- }
-
+ } else {
+ /* We put a fold mark in the chain we are folding only if
+ we fold in place to help the mount check code. If we do
+ not fold in place, it is possible to find the valid
+ chain by selecting the longer one */
+ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+ oob.u.c.unused = 0xffffffff;
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
/* OK. We now know the location of every block in the Virtual Unit Chain,
and the Erase Unit into which we are supposed to be copying.
Go for it.
*/
-
- DEBUG(1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
-
- for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
+ DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
unsigned char movebuf[512];
- struct nftl_oob oob;
- size_t retlen;
-
- memset(&oob, 0xff, sizeof(oob));
+ int ret;
/* If it's in the target EUN already, or if it's pending write, do nothing */
- if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) {
- /* Except if it's the first block, in which case we have to
- set the UnitNumbers */
- if (block == 0) {
-
- thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
- 16, &retlen, (char *)&oob);
-
- // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC,
- // le16_to_cpu(oob.u.a.VirtUnitNum));
-
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
-
- thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
- 16, &retlen, (char *)&oob);
- }
+ if (BlockMap[block] == targetEUN ||
+ (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
continue;
}
- oob.b.Status = BLOCK_USED;
-
- switch(block) {
- case 0:
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
- // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC);
-
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
- break;
-
- case 1:
- oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do
- oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
- break;
-
- case 2:
- default:
- oob.u.c.WriteInh = 0xffffffff;
- oob.u.c.unused = 0xffffffff;
- }
- if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob) == -EIO) {
- if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob) != -EIO)
- printk("Error went away on retry.\n");
- }
-
- thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob);
-
-
- /* FIXME: Add some error checking.... */
- thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
- 16, &retlen, (char *)&oob);
-
+ /* copy only in non free block (free blocks can only
+ happen in case of media errors or deleted blocks) */
+ if (BlockMap[block] == BLOCK_NIL)
+ continue;
+
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen, movebuf, (char *)&oob);
+ if (ret < 0) {
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen,
+ movebuf, (char *)&oob);
+ if (ret != -EIO)
+ printk("Error went away on retry.\n");
+ }
+ MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob);
}
+
+ /* add the header so that it is now a valid chain */
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
+ = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
+ 8, &retlen, (char *)&oob.u);
/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
shouldn't actually lose data in this case. It's just that when we load up on a medium which
has duplicate chains, we need to free one of the chains because it's not necessary any more.
*/
-
-
- thisEUN = thisNFTL->EUNtable[thisVUC];
+ thisEUN = nftl->EUNtable[thisVUC];
+ DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
- DEBUG(1,"Want to erase\n");
/* For each block in the old chain (except the targetEUN of course),
free it and make it available for future use */
-
- while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) {
- size_t retlen;
- struct erase_info *instr;
- u16 EUNtmp;
-
- instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
- if (!instr) {
- printk(KERN_WARNING "Out of memory for struct erase_info\n");
-
- EUNtmp = thisEUN;
-
- thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
- thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff;
- thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
- } else {
- memset(instr, 0, sizeof(struct erase_info));
- instr->addr = thisEUN * thisNFTL->EraseSize;
- instr->len = thisNFTL->EraseSize;
-
- MTD_ERASE(thisNFTL->mtd, instr);
- /* This is an async interface. Or will be. At which point
- this code will break. */
-
-#if 0
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
-
- printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- thisEUN, oob.b.ECCSig[0],
- oob.b.ECCSig[1],
- oob.b.ECCSig[2],
- oob.b.ECCSig[3],
- oob.b.ECCSig[4],
- oob.b.ECCSig[5]);
-#endif
- memset(&oob, 0xff, sizeof(oob));
- oob.u.b.WearInfo = cpu_to_le32(3);
- oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
-
- MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
-
- EUNtmp = thisEUN;
-
- thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
- thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff;
- thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
-
- thisNFTL->numfreeEUNs++;
-
- }
-
- // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
-
+ while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
+ unsigned int EUNtmp;
+
+ EUNtmp = nftl->ReplUnitTable[thisEUN];
+
+ if (NFTL_formatblock(nftl, thisEUN) < 0) {
+ /* could not erase : mark block as reserved
+ * FixMe: Update Bad Unit Table on disk
+ */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
+ } else {
+ /* correctly erased : mark it as free */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
+ nftl->numfreeEUNs++;
+ }
+ thisEUN = EUNtmp;
}
/* Make this the new start of chain for thisVUC */
- thisNFTL->VirtualUnitTable[targetEUN] = thisVUC;
- thisNFTL->ReplUnitTable[targetEUN] = 0xffff;
+ nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
+ nftl->EUNtable[thisVUC] = targetEUN;
- thisNFTL->EUNtable[thisVUC] = targetEUN;
return targetEUN;
-
}
-u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock)
+u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
{
/* This is the part that needs some cleverness applied.
For now, I'm doing the minimum applicable to actually
and we also need to do some assessment of the results when
the system loses power half-way through the routine.
*/
-
u16 LongestChain = 0;
u16 ChainLength = 0, thislen;
u16 chain, EUN;
-
- for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) {
- EUN = thisNFTL->EUNtable[chain];
-
+ for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) {
+ EUN = nftl->EUNtable[chain];
thislen = 0;
- while (EUN <= thisNFTL->lastEUN) {
+ while (EUN <= nftl->lastEUN) {
thislen++;
- // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
- EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff;
+ //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
+ EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
if (thislen > 0xff00) {
- printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN);
+ printk("Endless loop in Virtual Chain %d: Unit %x\n",
+ chain, EUN);
}
if (thislen > 0xff10) {
/* Actually, don't return failure. Just ignore this chain and
thislen = 0;
break;
}
-
}
-
if (thislen > ChainLength) {
- // printk("New longest chain is %d with length %d\n", chain, thislen);
+ //printk("New longest chain is %d with length %d\n", chain, thislen);
ChainLength = thislen;
LongestChain = chain;
}
- }
+ }
if (ChainLength < 2) {
- printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n");
+ printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
+ "Failing request\n");
return 0xffff;
}
-
- return NFTL_foldchain (thisNFTL, LongestChain, pendingblock);
+
+ return NFTL_foldchain (nftl, LongestChain, pendingblock);
}
/* NFTL_findwriteunit: Return the unit number into which we can write
for this block. Make it available if it isn't already
*/
-
-static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block)
+static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
{
u16 lastEUN;
- u16 thisVUC = block / (thisNFTL->EraseSize / 512);
- u16 writeEUN;
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
+ u16 thisVUC = block / (nftl->EraseSize / 512);
+ unsigned int writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
size_t retlen;
- int silly = 0x10000, silly2 = 3;
+ int silly, silly2 = 3;
struct nftl_oob oob;
- int debug=0;
do {
-
/* Scan the media to find a unit in the VUC which has
a free space for the block in question.
*/
/* This condition catches the 0x[7f]fff cases, as well as
being a sanity check for past-end-of-media access
*/
- lastEUN = 0xffff;
- writeEUN = thisNFTL->EUNtable[thisVUC];
-
- while(writeEUN <= thisNFTL->lastEUN) {
+ lastEUN = BLOCK_NIL;
+ writeEUN = nftl->EUNtable[thisVUC];
+ silly = MAX_LOOPS;
+ while (writeEUN <= nftl->lastEUN) {
struct nftl_bci bci;
size_t retlen;
-
+ unsigned int status;
+
lastEUN = writeEUN;
+
+ MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci);
- MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize)
- + blockofs,8, &retlen, (char *)&bci);
-
- if (debug)
- printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status));
+ DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+ block , writeEUN, le16_to_cpu(bci.Status));
- switch(bci.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
+ status = bci.Status | bci.Status1;
+ switch(status) {
+ case SECTOR_FREE:
return writeEUN;
- case __constant_cpu_to_le16(BLOCK_DELETED):
- case __constant_cpu_to_le16(BLOCK_USED):
- case __constant_cpu_to_le16(BLOCK_IGNORE):
+ case SECTOR_DELETED:
+ case SECTOR_USED:
+ case SECTOR_IGNORE:
break;
default:
// Invalid block. Don't use it any more. Must implement.
}
if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
+ printk(KERN_WARNING
+ "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
return 0xffff;
}
/* Skip to next block in chain */
-
- writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff;
+ writeEUN = nftl->ReplUnitTable[writeEUN];
}
/* OK. We didn't find one in the existing chain, or there
is no existing chain. */
/* Try to find an already-free block */
+ writeEUN = NFTL_findfreeblock(nftl, 0);
- writeEUN = NFTL_findfreeblock(thisNFTL, 0);
-
- if (writeEUN == 0xffff) {
+ if (writeEUN == BLOCK_NIL) {
/* That didn't work - there were no free blocks just
waiting to be picked up. We're going to have to fold
a chain to make room.
*/
/* First remember the start of this chain */
- // u16 startEUN = thisNFTL->EUNtable[thisVUC];
+ //u16 startEUN = nftl->EUNtable[thisVUC];
//printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
- writeEUN = NFTL_makefreeblock(thisNFTL, block);
+ writeEUN = NFTL_makefreeblock(nftl, 0xffff);
- if (writeEUN == 0xffff) {
+ if (writeEUN == BLOCK_NIL) {
/* Ouch. This should never happen - we should
always be able to make some room somehow.
If we get here, we've allocated more storage
routine is missing something.
*/
printk(KERN_WARNING "Cannot make free space.\n");
- return 0xffff;
+ return BLOCK_NIL;
}
- // printk("Restarting scan\n");
- lastEUN = 0xffff;
- // debug = 1;
+ //printk("Restarting scan\n");
+ lastEUN = BLOCK_NIL;
continue;
-#if 0
- if (startEUN != thisNFTL->EUNtable[thisVUC]) {
- /* The fold operation has moved the chain
- that we're looking at. Start the scan again.
- */
- continue;
- }
-#endif
}
/* We've found a free block. Insert it into the chain. */
- if (lastEUN != 0xffff) {
- /* Addition to an existing chain. Make the previous
- last block in the chain point to this one.
- */
-
- //printk("Linking EUN %d to EUN %d in VUC %d\n",
- // lastEUN, writeEUN, thisVUC);
- /* Both in our cache... */
- thisNFTL->ReplUnitTable[lastEUN] = writeEUN;
-
-
- /* ... and on the flash itself */
- MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
- (char *)&oob);
+ if (lastEUN != BLOCK_NIL) {
+ thisVUC |= 0x8000; /* It's a replacement block */
+ } else {
+ /* The first block in a new chain */
+ nftl->EUNtable[thisVUC] = writeEUN;
+ }
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN);
+ /* set up the actual EUN we're writing into */
+ /* Both in our cache... */
+ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
- MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
- (char *)&oob);
+ /* ... and on the flash itself */
+ MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
- thisVUC |= 0x8000; /* It's a replacement block */
- } else {
- /* The first block in a new chain */
- thisNFTL->EUNtable[thisVUC] = writeEUN;
- }
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
- /* Now set up the actual EUN we're writing into */
+ MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+ /* we link the new block to the chain only after the
+ block is ready. It avoids the case where the chain
+ could point to a free block */
+ if (lastEUN != BLOCK_NIL) {
/* Both in our cache... */
- thisNFTL->VirtualUnitTable[writeEUN] = thisVUC;
- thisNFTL->ReplUnitTable[writeEUN] = 0xffff;
-
+ nftl->ReplUnitTable[lastEUN] = writeEUN;
/* ... and on the flash itself */
- MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
- &retlen, (char *)&oob);
+ MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
+ = cpu_to_le16(writeEUN);
- MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
- &retlen, (char *)&oob);
+ MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
return writeEUN;
} while (silly2--);
- printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC);
+ printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
+ thisVUC);
return 0xffff;
}
-static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block,
- char *buffer)
+static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
u16 writeEUN;
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
size_t retlen;
- u16 eccbuf[8];
-
- // if (thisEUN == 0xffff) thisEUN = 0;
-
- writeEUN = NFTL_findwriteunit(thisNFTL, block);
+ u8 eccbuf[6];
-// printk("writeblock(%d): Write to Unit %d\n", block, writeEUN);
+ writeEUN = NFTL_findwriteunit(nftl, block);
- if (writeEUN == 0xffff) {
- printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n");
+ if (writeEUN == BLOCK_NIL) {
+ printk(KERN_WARNING
+ "NFTL_writeblock(): Cannot find block to write to\n");
/* If we _still_ haven't got a block to use, we're screwed */
return 1;
}
-// printk("Writing block %lx to EUN %x\n",block, writeEUN);
-
- thisNFTL->mtd->write_ecc(thisNFTL->mtd,
- (writeEUN * thisNFTL->EraseSize) + blockofs,
- 512, &retlen, (char *)buffer, (char *)eccbuf);
- eccbuf[3] = BLOCK_USED;
- eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff;
-
- thisNFTL->mtd->write_oob(thisNFTL->mtd,
- (writeEUN * thisNFTL->EraseSize) + blockofs,
- 16, &retlen, (char *)eccbuf);
+ MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 512, &retlen, (char *)buffer, (char *)eccbuf);
+ /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
return 0;
}
-
#endif /* CONFIG_NFTL_RW */
-static int NFTL_readblock(struct NFTLrecord *thisNFTL,
- unsigned block, char *buffer)
+static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
- u16 lastgoodEUN = 0xffff;
- u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)];
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
-
- int silly = -1;
+ u16 lastgoodEUN;
+ u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ unsigned int status;
+ int silly = MAX_LOOPS;
+ size_t retlen;
+ struct nftl_bci bci;
+
+ lastgoodEUN = BLOCK_NIL;
+
+ if (thisEUN != BLOCK_NIL) {
+ while (thisEUN < nftl->nb_blocks) {
+ if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+ status = bci.Status | bci.Status1;
+
+ switch (status) {
+ case SECTOR_FREE:
+ /* no modification of a sector should follow a free sector */
+ goto the_end;
+ case SECTOR_DELETED:
+ lastgoodEUN = BLOCK_NIL;
+ break;
+ case SECTOR_USED:
+ lastgoodEUN = thisEUN;
+ break;
+ case SECTOR_IGNORE:
+ break;
+ default:
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
+ break;
+ }
- if (thisEUN == 0xffff) thisEUN = 0;
-
- while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) {
- struct nftl_bci bci;
- size_t retlen;
-
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci);
-
- switch(bci.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
- thisEUN = 0;
- break;
- case __constant_cpu_to_le16(BLOCK_USED):
- lastgoodEUN = thisEUN;
- break;
- case __constant_cpu_to_le16(BLOCK_IGNORE):
- case __constant_cpu_to_le16(BLOCK_DELETED):
- break;
- default:
- printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status);
+ if (!silly--) {
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ block / (nftl->EraseSize / 512));
+ return 1;
+ }
+ thisEUN = nftl->ReplUnitTable[thisEUN];
}
+ }
- if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512));
- return 1;
- }
- if (thisEUN)
- thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
- }
- if (lastgoodEUN == 0xffff) {
+ the_end:
+ if (lastgoodEUN == BLOCK_NIL) {
+ /* the requested block is not on the media, return all 0x00 */
memset(buffer, 0, 512);
} else {
- loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs;
+ loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
size_t retlen;
u_char eccbuf[6];
- thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf);
+ if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf))
+ return -EIO;
}
return 0;
}
-
-static int nftl_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
+static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
{
- struct NFTLrecord *thisNFTL;
-
- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
+ struct NFTLrecord *nftl;
- if (!thisNFTL) return -EINVAL;
+ nftl = NFTLs[MINOR(inode->i_rdev) / 16];
+ if (!nftl) return -EINVAL;
switch (cmd) {
case HDIO_GETGEO: {
struct hd_geometry g;
- g.heads = thisNFTL->heads;
- g.sectors = thisNFTL->sectors;
- g.cylinders = thisNFTL->cylinders;
+ g.heads = nftl->heads;
+ g.sectors = nftl->sectors;
+ g.cylinders = nftl->cylinders;
g.start = part_table[MINOR(inode->i_rdev)].start_sect;
return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
}
case BLKGETSIZE: /* Return device size */
- if (!arg) return -EINVAL;
+ if (!arg) return -EINVAL;
return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
case BLKFLSBUF:
- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
- if (thisNFTL->mtd->sync)
- thisNFTL->mtd->sync(thisNFTL->mtd);
+ if (nftl->mtd->sync)
+ nftl->mtd->sync(nftl->mtd);
return 0;
case BLKRRPART:
if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- if (thisNFTL->usecount > 1) {
- // printk("Use count %d\n", thisNFTL->usecount);
- return -EBUSY;
- }
+ if (nftl->usecount > 1) return -EBUSY;
#if LINUX_VERSION_CODE < 0x20328
- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_dev) / 16);
+ resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16);
#else
- grok_partitions(&nftl_gendisk, MINOR(inode->i_dev) / 16, 1<<4, thisNFTL->nr_sects);
+ grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16,
+ 1<<4, nftl->nr_sects);
#endif
return 0;
-
- // RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+
+#if (LINUX_VERSION_CODE < 0x20303)
+ RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+#else
+ case BLKROSET:
+ case BLKROGET:
+ case BLKSSZGET:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+#endif
+
default:
return -EINVAL;
}
}
-
void nftl_request(RQFUNC_ARG)
{
unsigned int dev, block, nsect;
- struct NFTLrecord *thisNFTL;
+ struct NFTLrecord *nftl;
char *buffer;
struct request *req;
int res;
while (1) {
INIT_REQUEST; /* blk.h */
-
- req = CURRENT;
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
- blkdev_dequeue_request(req);
- spin_unlock_irq(&io_request_lock);
-#else
req = CURRENT;
-#endif
- DEBUG(2,"NFTL_request\n");
- DEBUG(3,"NFTL %d request, %lx, %lx", req->cmd,
- req->sector, req->current_nr_sectors);
+ /* We can do this because the generic code knows not to
+ touch the request at the head of the queue */
+ spin_unlock_irq(&io_request_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
+ DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
+ (req->cmd == READ) ? "Read " : "Write",
+ req->sector, req->current_nr_sectors);
+
dev = MINOR(req->rq_dev);
block = req->sector;
nsect = req->current_nr_sectors;
res = 1; /* succeed */
if (dev >= MAX_NFTLS * 16) {
- printk("fl: bad minor number: device=%s\n",
+ /* there is no such partition */
+ printk("nftl: bad minor number: device = %s\n",
kdevname(req->rq_dev));
res = 0; /* fail */
goto repeat;
}
- thisNFTL = NFTLs[dev / 16];
- DEBUG(3,"Waiting for mutex\n");
- down(&thisNFTL->mutex);
- DEBUG(3,"Got mutex\n");
-
- if (block + nsect >= part_table[dev].nr_sects) {
- printk("nftl%c%d: bad access: block=%d, count=%d\n",
+ nftl = NFTLs[dev / 16];
+ DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+ down(&nftl->mutex);
+ DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
+
+ if (block + nsect > part_table[dev].nr_sects) {
+ /* access past the end of device */
+ printk("nftl%c%d: bad access: block = %d, count = %d\n",
(MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
- up(&thisNFTL->mutex);
+ up(&nftl->mutex);
res = 0; /* fail */
goto repeat;
}
block += part_table[dev].start_sect;
if (req->cmd == READ) {
- DEBUG(2,"NFTL read\n");
- for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
+
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
/* Read a single sector to req->buffer + (512 * i) */
-
- if (NFTL_readblock(thisNFTL, block, buffer)) {
- DEBUG(2,"NFTL read request failed\n");
- up(&thisNFTL->mutex);
+ if (NFTL_readblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
}
- DEBUG(2,"NFTL read request completed OK\n");
- up(&thisNFTL->mutex);
+
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
+ up(&nftl->mutex);
goto repeat;
- }
- else if (req->cmd == WRITE) {
- DEBUG(2,"NFTL write request of 0x%x sectors @ %x (req->nr_sectors == %lx\n",nsect, block, req->nr_sectors);
+ } else if (req->cmd == WRITE) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block,
+ req->nr_sectors);
#ifdef CONFIG_NFTL_RW
- for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
/* Read a single sector to req->buffer + (512 * i) */
-
- if (NFTL_writeblock(thisNFTL, block, buffer)) {
- DEBUG(1,"NFTL write request failed\n");
-
- up(&thisNFTL->mutex);
+ if (NFTL_writeblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
}
- DEBUG(2,"NFTL write request completed OK\n");
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
#else
- res=0; /* Writes always fail */
+ res = 0; /* Writes always fail */
#endif /* CONFIG_NFTL_RW */
- up(&thisNFTL->mutex);
+ up(&nftl->mutex);
goto repeat;
- }
- else {
- DEBUG(0,"NFTL ??? request\n");
- up(&thisNFTL->mutex);
+ } else {
+ DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
repeat:
- DEBUG(3,"end_request(%d)\n", res);
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
+ DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
spin_lock_irq(&io_request_lock);
- nftl_end_request(req, res);
-#else
end_request(res);
-#endif
}
}
static int nftl_open(struct inode *ip, struct file *fp)
{
+ int nftlnum = MINOR(ip->i_rdev) / 16;
struct NFTLrecord *thisNFTL;
- thisNFTL = NFTLs[MINOR(ip->i_rdev) / 16];
+ thisNFTL = NFTLs[nftlnum];
- DEBUG(2,"NFTL_open\n");
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
+#ifdef CONFIG_KMOD
+ if (!thisNFTL && nftlnum == 0) {
+ request_module("docprobe");
+ thisNFTL = NFTLs[nftlnum];
+ }
+#endif
if (!thisNFTL) {
- DEBUG(2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
- MINOR(ip->i_rdev) / 16,ip->i_rdev,ip, fp);
+ DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
+ nftlnum, ip->i_rdev, ip, fp);
return -ENODEV;
}
+
#ifndef CONFIG_NFTL_RW
if (fp->f_mode & FMODE_WRITE)
- return -EROFS;
+ return -EROFS;
#endif /* !CONFIG_NFTL_RW */
+
thisNFTL->usecount++;
MOD_INC_USE_COUNT;
if (!get_mtd_device(thisNFTL->mtd, -1)) {
thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
- DEBUG(2, "NFTL_release\n");
-
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+
fsync_dev(inode->i_rdev);
if (sb)
invalidate_inodes(sb);
}
#if LINUX_VERSION_CODE < 0x20326
static struct file_operations nftl_fops = {
- read: block_read,
- write: block_write,
- ioctl: nftl_ioctl,
- open: nftl_open,
- release: nftl_release,
- fsync: block_fsync,
+ read: block_read,
+ write: block_write,
+ ioctl: nftl_ioctl,
+ open: nftl_open,
+ release: nftl_release,
+ fsync: block_fsync,
};
#else
static struct block_device_operations nftl_fops =
{
- open: nftl_open,
- release: nftl_release,
- ioctl: nftl_ioctl
+ open: nftl_open,
+ release: nftl_release,
+ ioctl: nftl_ioctl
};
#endif
*
****************************************************************************/
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_nftl init_module
#define cleanup_nftl cleanup_module
#endif
-#define __exit
-#endif
static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL};
-
/* static int __init init_nftl(void) */
int __init init_nftl(void)
{
int i;
- printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
+ printk(KERN_NOTICE
+ "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
#ifdef PRERELEASE
- printk(KERN_INFO"$Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $\n");
+ printk(KERN_INFO"$Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $\n");
#endif
- if (register_blkdev(NFTL_MAJOR, "nftl", &nftl_fops)){
- printk("unable to register NFTL block device\n");
+ if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
+ printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
+ return -EBUSY;
} else {
#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = nftl_request;
+ blk_dev[MAJOR_NR].request_fn = nftl_request;
#else
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
#endif
- for (i=0; i < 256 ; i++) {
+ /* set block size to 1kB each */
+ for (i = 0; i < 256; i++) {
nftl_blocksizes[i] = 1024;
}
- blksize_size[NFTL_MAJOR] = nftl_blocksizes;
+ blksize_size[MAJOR_NR] = nftl_blocksizes;
+
nftl_gendisk.next = gendisk_head;
gendisk_head = &nftl_gendisk;
}
static void __exit cleanup_nftl(void)
{
- struct gendisk *gd, **gdp;
+ struct gendisk *gd, **gdp;
- unregister_mtd_user(&nftl_notifier);
-
- unregister_blkdev(NFTL_MAJOR, "nftl");
+ unregister_mtd_user(&nftl_notifier);
+ unregister_blkdev(MAJOR_NR, "nftl");
+
#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = 0;
+ blk_dev[MAJOR_NR].request_fn = 0;
#else
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
#endif
- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
- if (*gdp == &nftl_gendisk) {
- gd = *gdp; *gdp = gd->next;
- break;
- }
-
+
+ /* remove ourself from generic harddisk list
+ FIXME: why can't I found this partition on /proc/partition */
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &nftl_gendisk) {
+ gd = *gdp; *gdp = gd->next;
+ break;
+ }
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_nftl);
module_exit(cleanup_nftl);
-#endif
--- /dev/null
+/*
+ * NFTL mount code with extensive checks
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: nftlmount.c,v 1.11 2000/11/17 12:24:09 ollie Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+#include <linux/mtd/compatmac.h>
+
+#define SECTORSIZE 512
+
+/* 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
+ * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
+ * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
+ */
+static int find_boot_record(struct NFTLrecord *nftl)
+{
+ struct nftl_uci1 h1;
+ struct nftl_oob oob;
+ unsigned int block, boot_record_count;
+ int retlen;
+ u8 buf[SECTORSIZE];
+ struct NFTLMediaHeader *mh = &nftl->MediaHdr;
+
+ nftl->MediaUnit = BLOCK_NIL;
+ nftl->SpareMediaUnit = BLOCK_NIL;
+ boot_record_count = 0;
+
+ /* search for a valid boot record */
+ for (block = 0; block < nftl->nb_blocks; block++) {
+ unsigned int erase_mark;
+
+ /* read ANAND header. To be safer with BIOS, also use erase mark as discriminant */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&h1) < 0)
+ continue;
+
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+ if (erase_mark != ERASE_MARK)
+ continue;
+
+ if (MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
+ &retlen, buf, (char *)&oob) < 0)
+ continue;
+
+ memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
+ if (memcmp(mh->DataOrgID, "ANAND", 6) == 0) {
+ /* first boot record */
+ if (boot_record_count == 0) {
+ unsigned int i;
+ /* header found : read the bad block table data */
+ if (mh->UnitSizeFactor != 0xff) {
+ printk("Sorry, we don't support UnitSizeFactor "
+ "of != 1 yet\n");
+ goto ReplUnitTable;
+ }
+
+ nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
+ if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks)
+ goto ReplUnitTable; /* small consistency check */
+
+ nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
+ if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2))
+ goto ReplUnitTable; /* small consistency check */
+
+ /* FixMe: with bad blocks, the total size available is not FormattedSize any
+ more !!! */
+ nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
+ nftl->MediaUnit = block;
+
+ /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
+ for (i = 0; i < nftl->nb_blocks; i++) {
+ if ((i & (SECTORSIZE - 1)) == 0) {
+ /* read one sector for every SECTORSIZE of blocks */
+ if (MTD_READECC(nftl->mtd, block * nftl->EraseSize +
+ i + SECTORSIZE, SECTORSIZE,
+ &retlen, buf, (char *)&oob) < 0)
+ goto ReplUnitTable;
+ }
+ /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
+ if (buf[i & (SECTORSIZE - 1)] != 0xff)
+ nftl->ReplUnitTable[i] = BLOCK_RESERVED;
+ }
+
+ boot_record_count++;
+ } else if (boot_record_count == 1) {
+ nftl->SpareMediaUnit = block;
+ boot_record_count++;
+ break;
+ }
+ }
+ ReplUnitTable:
+ }
+
+ if (boot_record_count == 0) {
+ /* no boot record found */
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int memcmpb(void *a, int c, int n)
+{
+ int i;
+ for (i = 0; i < n; i++) {
+ if (c != ((unsigned char *)a)[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */
+static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
+ int check_oob)
+{
+ int i, retlen;
+ u8 buf[SECTORSIZE];
+
+ for (i = 0; i < len; i += SECTORSIZE) {
+ /* we want to read the sector without ECC check here since a free
+ sector does not have ECC syndrome on it yet */
+ if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+ return -1;
+
+ if (check_oob) {
+ if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
+ &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
+ return -1;
+ }
+ address += SECTORSIZE;
+ }
+
+ return 0;
+}
+
+/* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and
+ * Update NFTL metadata. Each erase operation is checked with check_free_sectors
+ *
+ * Return: 0 when succeed, -1 on error.
+ *
+ * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
+ * 2. UnitSizeFactor != 0xFF
+ */
+int NFTL_formatblock(struct NFTLrecord *nftl, int block)
+{
+ int retlen;
+ unsigned int nb_erases, erase_mark;
+ struct nftl_uci1 uci;
+ struct erase_info *instr = &nftl->instr;
+
+ /* Read the Unit Control Information #1 for Wear-Leveling */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ goto default_uci1;
+
+ erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
+ if (erase_mark != ERASE_MARK) {
+ default_uci1:
+ uci.EraseMark = cpu_to_le16(ERASE_MARK);
+ uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ uci.WearInfo = cpu_to_le32(0);
+ }
+
+ memset(instr, 0, sizeof(struct erase_info));
+
+ /* XXX: use async erase interface, XXX: test return code */
+ instr->addr = block * nftl->EraseSize;
+ instr->len = nftl->EraseSize;
+ MTD_ERASE(nftl->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 {
+ /* increase and write Wear-Leveling info */
+ nb_erases = le32_to_cpu(uci.WearInfo);
+ nb_erases++;
+
+ /* wrap (almost impossible with current flashs) or free block */
+ if (nb_erases == 0)
+ nb_erases = 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(nftl, instr->addr, nftl->EraseSize, 1) != 0)
+ return -1;
+
+ uci.WearInfo = le32_to_cpu(nb_erases);
+ if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&uci) < 0)
+ return -1;
+ return 0;
+ }
+}
+
+/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
+ * Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
+ * was being folded when NFTL was interrupted.
+ *
+ * The check_free_sectors in this function is neceressary. There is a possible
+ * situation that after writing the Data area, the Block Control Information is
+ * not updated according (due to power failure or something) which leaves the block
+ * in an umconsistent state. So we have to check if a block is really FREE in this
+ * case. */
+static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int block, i, status;
+ struct nftl_bci bci;
+ int sectors_per_block, retlen;
+
+ sectors_per_block = nftl->EraseSize / SECTORSIZE;
+ block = first_block;
+ for (;;) {
+ for (i = 0; i < sectors_per_block; i++) {
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+ status = bci.Status | bci.Status1;
+
+ switch(status) {
+ case SECTOR_FREE:
+ /* verify that the sector is really free. If not, mark
+ as ignore */
+ if (memcmpb(&bci, 0xff, 8) != 0 ||
+ check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
+ SECTORSIZE, 0) != 0) {
+ printk("Incorrect free sector %d in block %d: "
+ "marking it as ignored\n",
+ i, block);
+
+ /* sector not free actually : mark it as SECTOR_IGNORE */
+ bci.Status = SECTOR_IGNORE;
+ bci.Status1 = SECTOR_IGNORE;
+ MTD_WRITEOOB(nftl->mtd,
+ block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* proceed to next Erase Unit on the chain */
+ block = nftl->ReplUnitTable[block];
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+}
+
+/* calc_chain_lenght: Walk through a Virtual Unit Chain and estimate chain length */
+static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int length = 0, block = first_block;
+
+ for (;;) {
+ length++;
+ /* avoid infinite loops, although this is guaranted not to
+ happen because of the previous checks */
+ if (length >= nftl->nb_blocks) {
+ printk("nftl: length too long %d !\n", length);
+ break;
+ }
+
+ block = nftl->ReplUnitTable[block];
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+ return length;
+}
+
+/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
+ * Virtual Unit Chain, i.e. all the units are disconnected.
+ *
+ * It is not stricly correct to begin from the first block of the chain because
+ * if we stop the code, we may see again a valid chain if there was a first_block
+ * flag in a block inside it. But is it really a problem ?
+ *
+ * FixMe: Figure out what the last statesment means. What if power failure when we are
+ * in the for (;;) loop formatting blocks ??
+ */
+static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int block = first_block, block1;
+
+ printk("Formatting chain at block %d\n", first_block);
+
+ for (;;) {
+ block1 = nftl->ReplUnitTable[block];
+
+ 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 */
+ nftl->ReplUnitTable[block] = BLOCK_RESERVED;
+ } else {
+ nftl->ReplUnitTable[block] = BLOCK_FREE;
+ }
+
+ /* goto next block on the chain */
+ block = block1;
+
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+}
+
+/* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or
+ * totally free (only 0xff).
+ *
+ * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
+ * following critia:
+ * 1. */
+static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
+{
+ struct nftl_uci1 h1;
+ unsigned int erase_mark;
+ int i, retlen;
+ unsigned char buf[SECTORSIZE];
+
+ /* check erase mark. */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+ if (erase_mark != ERASE_MARK) {
+ /* if no erase mark, the block must be totally free. This is
+ possible in two cases : empty filsystem or interrupted erase (very unlikely) */
+ if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
+ return -1;
+
+ /* free block : write erase mark */
+ h1.EraseMark = cpu_to_le16(ERASE_MARK);
+ h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ h1.WearInfo = cpu_to_le32(0);
+ if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+ } else {
+#if 0
+ /* if erase mark present, need to skip it when doing check */
+ for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
+ /* check free sector */
+ if (check_free_sectors (nftl, block * nftl->EraseSize + i,
+ SECTORSIZE, 0) != 0)
+ return -1;
+
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i,
+ 16, &retlen, buf) < 0)
+ return -1;
+ if (i == SECTORSIZE) {
+ /* skip erase mark */
+ if (memcmpb(buf, 0xff, 8))
+ return -1;
+ } else {
+ if (memcmpb(buf, 0xff, 16))
+ return -1;
+ }
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
+ * to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
+ * is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
+ * for some reason. A clean up/check of the VUC is neceressary in this case.
+ *
+ * WARNING: return 0 if read error
+ */
+static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
+{
+ struct nftl_uci2 uci;
+ int retlen;
+
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ return 0;
+
+ return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
+}
+
+int NFTL_mount(struct NFTLrecord *s)
+{
+ int i;
+ unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark;
+ unsigned int block, first_block, is_first_block;
+ int chain_length, do_format_chain;
+ struct nftl_uci0 h0;
+ struct nftl_uci1 h1;
+ int retlen;
+
+ /* XXX: will be suppressed */
+ s->lastEUN = s->nb_blocks - 1;
+
+ /* memory alloc */
+ s->EUNtable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL);
+ s->ReplUnitTable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL);
+ if (!s->EUNtable || !s->ReplUnitTable) {
+ fail:
+ if (s->EUNtable)
+ kfree(s->EUNtable);
+ if (s->ReplUnitTable)
+ kfree(s->ReplUnitTable);
+ return -1;
+ }
+
+ /* mark all blocks as potentially containing data */
+ for (i = 0; i < s->nb_blocks; i++) {
+ s->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
+ }
+
+ /* search for NFTL MediaHeader and Spare NFTL Media Header */
+ if (find_boot_record(s) < 0) {
+ printk("Could not find valid boot record\n");
+ goto fail;
+ }
+
+ /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
+ for (i = 0; i < s->nb_boot_blocks; i++)
+ s->ReplUnitTable[i] = BLOCK_RESERVED;
+
+ /* also mark the boot records (NFTL MediaHeader) blocks as reserved */
+ if (s->MediaUnit != BLOCK_NIL)
+ s->ReplUnitTable[s->MediaUnit] = BLOCK_RESERVED;
+ if (s->SpareMediaUnit != BLOCK_NIL)
+ s->ReplUnitTable[s->SpareMediaUnit] = BLOCK_RESERVED;
+
+ /* init the logical to physical table */
+ for (i = 0; i < s->nb_blocks; i++) {
+ s->EUNtable[i] = BLOCK_NIL;
+ }
+
+ /* first pass : explore each block chain */
+ first_logical_block = 0;
+ for (first_block = 0; first_block < s->nb_blocks; first_block++) {
+ /* if the block was not already explored, we can look at it */
+ if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
+ block = first_block;
+ chain_length = 0;
+ do_format_chain = 0;
+
+ for (;;) {
+ /* read the block header. If error, we format the chain */
+ if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8,
+ &retlen, (char *)&h0) < 0 ||
+ MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0) {
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+ break;
+ }
+
+ logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
+ rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
+ nb_erases = le32_to_cpu (h1.WearInfo);
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+
+ is_first_block = !(logical_block >> 15);
+ logical_block = logical_block & 0x7fff;
+
+ /* invalid/free block test */
+ if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
+ if (chain_length == 0) {
+ /* if not currently in a chain, we can handle it safely */
+ if (check_and_mark_free_block(s, block) < 0) {
+ /* not really free: format it */
+ printk("Formatting block %d\n", block);
+ if (NFTL_formatblock(s, block) < 0) {
+ /* could not format: reserve the block */
+ s->ReplUnitTable[block] = BLOCK_RESERVED;
+ } else {
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ } else {
+ /* free block: mark it */
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ /* directly examine the next block. */
+ goto examine_ReplUnitTable;
+ } else {
+ /* the block was in a chain : this is bad. We
+ must format all the chain */
+ printk("Block %d: free but referenced in chain %d\n",
+ block, first_block);
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+ break;
+ }
+ }
+
+ /* we accept only first blocks here */
+ if (chain_length == 0) {
+ /* this block is not the first block in chain :
+ ignore it, it will be included in a chain
+ later, or marked as not explored */
+ if (!is_first_block)
+ goto examine_ReplUnitTable;
+ first_logical_block = logical_block;
+ } else {
+ if (logical_block != first_logical_block) {
+ printk("Block %d: incorrect logical block: %d expected: %d\n",
+ block, logical_block, first_logical_block);
+ /* the chain is incorrect : we must format it,
+ but we need to read it completly */
+ do_format_chain = 1;
+ }
+ if (is_first_block) {
+ /* we accept that a block is marked as first
+ block while being last block in a chain
+ only if the chain is being folded */
+ if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
+ rep_block != 0xffff) {
+ printk("Block %d: incorrectly marked as first block in chain\n",
+ block);
+ /* the chain is incorrect : we must format it,
+ but we need to read it completly */
+ do_format_chain = 1;
+ } else {
+ printk("Block %d: folding in progress - ignoring first block flag\n",
+ block);
+ }
+ }
+ }
+ chain_length++;
+ if (rep_block == 0xffff) {
+ /* no more blocks after */
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ break;
+ } else if (rep_block >= s->nb_blocks) {
+ printk("Block %d: referencing invalid block %d\n",
+ block, rep_block);
+ do_format_chain = 1;
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ break;
+ } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
+ /* same problem as previous 'is_first_block' test:
+ we accept that the last block of a chain has
+ the first_block flag set if folding is in
+ progress. We handle here the case where the
+ last block appeared first */
+ if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
+ s->EUNtable[first_logical_block] == rep_block &&
+ get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
+ /* EUNtable[] will be set after */
+ printk("Block %d: folding in progress - ignoring first block flag\n",
+ rep_block);
+ s->ReplUnitTable[block] = rep_block;
+ s->EUNtable[first_logical_block] = BLOCK_NIL;
+ } else {
+ printk("Block %d: referencing block %d already in another chain\n",
+ block, rep_block);
+ /* XXX: should handle correctly fold in progress chains */
+ do_format_chain = 1;
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ }
+ break;
+ } else {
+ /* this is OK */
+ s->ReplUnitTable[block] = rep_block;
+ block = rep_block;
+ }
+ }
+
+ /* the chain was completely explored. Now we can decide
+ what to do with it */
+ if (do_format_chain) {
+ /* invalid chain : format it */
+ format_chain(s, first_block);
+ } else {
+ unsigned int first_block1, chain_to_format, chain_length1;
+ int fold_mark;
+
+ /* valid chain : get foldmark */
+ fold_mark = get_fold_mark(s, first_block);
+ if (fold_mark == 0) {
+ /* cannot get foldmark : format the chain */
+ printk("Could read foldmark at block %d\n", first_block);
+ format_chain(s, first_block);
+ } else {
+ if (fold_mark == FOLD_MARK_IN_PROGRESS)
+ check_sectors_in_chain(s, first_block);
+
+ /* now handle the case where we find two chains at the
+ same virtual address : we select the longer one,
+ because the shorter one is the one which was being
+ folded if the folding was not done in place */
+ first_block1 = s->EUNtable[first_logical_block];
+ if (first_block1 != BLOCK_NIL) {
+ /* XXX: what to do if same length ? */
+ chain_length1 = calc_chain_length(s, first_block1);
+ printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
+ first_block1, chain_length1, first_block, chain_length);
+
+ if (chain_length >= chain_length1) {
+ chain_to_format = first_block1;
+ s->EUNtable[first_logical_block] = first_block;
+ } else {
+ chain_to_format = first_block;
+ }
+ format_chain(s, chain_to_format);
+ } else {
+ s->EUNtable[first_logical_block] = first_block;
+ }
+ }
+ }
+ }
+ examine_ReplUnitTable:
+ }
+
+ /* second pass to format unreferenced blocks and init free block count */
+ s->numfreeEUNs = 0;
+ s->LastFreeEUN = BLOCK_NIL;
+
+ for (block = 0; block < s->nb_blocks; block++) {
+ if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
+ printk("Unreferenced block %d, formatting it\n", block);
+ if (NFTL_formatblock(s, block) < 0)
+ s->ReplUnitTable[block] = BLOCK_RESERVED;
+ else
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ if (s->ReplUnitTable[block] == BLOCK_FREE) {
+ s->numfreeEUNs++;
+ s->LastFreeEUN = block;
+ }
+ }
+
+ return 0;
+}
/*
- * $Id: nora.c,v 1.12 2000/07/13 10:32:33 dwmw2 Exp $
+ * $Id: nora.c,v 1.17 2000/12/03 19:32:21 dwmw2 Exp $
*
* This is so simple I love it.
*/
}
struct map_info nora_map = {
- "NORA",
- WINDOW_SIZE,
- 2,
- nora_read8,
- nora_read16,
- nora_read32,
- nora_copy_from,
- nora_write8,
- nora_write16,
- nora_write32,
- nora_copy_to,
- 0,
- 0
+ name: "NORA",
+ size: WINDOW_SIZE,
+ buswidth: 2,
+ read8: nora_read8,
+ read16: nora_read16,
+ read32: nora_read32,
+ copy_from: nora_copy_from,
+ write8: nora_write8,
+ write16: nora_write16,
+ write32: nora_write32,
+ copy_to: nora_copy_to
};
{
type: MTD_NORFLASH,
flags: MTD_CAP_NORFLASH,
- size: 0xf00000,
+ size: 0x900000,
erasesize: 0x20000,
name: "NORA root filesystem",
module: THIS_MODULE,
{
type: MTD_NORFLASH,
flags: MTD_CAP_NORFLASH,
- size: 0x1000000,
+ size: 0x1600000,
erasesize: 0x20000,
- name: "NORA main filesystem",
+ name: "NORA second filesystem",
module: THIS_MODULE,
erase: nora_mtd_erase,
read: nora_mtd_read,
suspend: nora_mtd_suspend,
resume: nora_mtd_resume,
sync: nora_mtd_sync,
- priv: (void *)0x1000000
+ priv: (void *)0xa00000
}
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_nora init_module
#define cleanup_nora cleanup_module
#endif
-#endif
int __init init_nora(void)
{
mymtd->module = &__this_module;
#endif
- add_mtd_device(&nora_mtds[3]);
+ add_mtd_device(&nora_mtds[2]);
add_mtd_device(&nora_mtds[0]);
add_mtd_device(&nora_mtds[1]);
- add_mtd_device(&nora_mtds[2]);
+ add_mtd_device(&nora_mtds[3]);
return 0;
}
static void __exit cleanup_nora(void)
{
if (mymtd) {
- del_mtd_device(&nora_mtds[2]);
+ del_mtd_device(&nora_mtds[3]);
del_mtd_device(&nora_mtds[1]);
del_mtd_device(&nora_mtds[0]);
- del_mtd_device(&nora_mtds[3]);
+ del_mtd_device(&nora_mtds[2]);
map_destroy(mymtd);
}
}
+
+module_init(init_nora);
+module_exit(cleanup_nora);
-// $Id: octagon-5066.c,v 1.10 2000/07/13 14:04:23 dwmw2 Exp $
+// $Id: octagon-5066.c,v 1.12 2000/11/27 08:50:22 dwmw2 Exp $
/* ######################################################################
Octagon 5066 MTD Driver.
static struct map_info oct5066_map[2] = {
{
- "Octagon 5066 Socket",
- 512 * 1024,
- 1,
- oct5066_read8,
- oct5066_read16,
- oct5066_read32,
- oct5066_copy_from,
- oct5066_write8,
- oct5066_write16,
- oct5066_write32,
- oct5066_copy_to,
- 1<<6
+ name: "Octagon 5066 Socket",
+ size: 512 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 1<<6
},
{
- "Octagon 5066 Internal Flash",
- 2 * 1024 * 1024,
- 1,
- oct5066_read8,
- oct5066_read16,
- oct5066_read32,
- oct5066_copy_from,
- oct5066_write8,
- oct5066_write16,
- oct5066_write32,
- oct5066_copy_to,
- 2<<6
+ name: "Octagon 5066 Internal Flash",
+ size: 2 * 1024 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 2<<6
}
};
return 0;
}
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_oct5066 init_module
#define cleanup_oct5066 cleanup_module
#endif
-#define __exit
-#endif
void cleanup_oct5066(void)
{
return 0;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_oct5066);
module_exit(cleanup_oct5066);
-#endif
/*
- * $Id: physmap.c,v 1.2 2000/07/11 09:42:32 dwmw2 Exp $
+ * $Id: physmap.c,v 1.8 2000/11/27 08:50:22 dwmw2 Exp $
*
* Normal mappings of chips in physical memory
*/
#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 = {
- "Physically mapped flash",
- WINDOW_SIZE,
- 2,
- physmap_read8,
- physmap_read16,
- physmap_read32,
- physmap_copy_from,
- physmap_write8,
- physmap_write16,
- physmap_write32,
- physmap_copy_to,
- 0,
- 0
+ name: "Physically mapped flash",
+ size: WINDOW_SIZE,
+ buswidth: BUSWIDTH,
+ read8: physmap_read8,
+ read16: physmap_read16,
+ read32: physmap_read32,
+ copy_from: physmap_copy_from,
+ write8: physmap_write8,
+ write16: physmap_write16,
+ write32: physmap_write32,
+ copy_to: physmap_copy_to
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_physmap init_module
#define cleanup_physmap cleanup_module
#endif
-#endif
int __init init_physmap(void)
{
printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_SIZE, WINDOW_ADDR);
+ physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!physmap_map.map_priv_1) {
printk("Failed to ioremap\n");
return 0;
}
+ iounmap((void *)physmap_map.map_priv_1);
return -ENXIO;
}
physmap_map.map_priv_1 = 0;
}
}
+
+module_init(init_physmap);
+module_exit(cleanup_physmap);
+
*
* This code is GPL
*
- * $Id: pnc2000.c,v 1.1 2000/07/12 09:34:32 dwmw2 Exp $
+ * $Id: pnc2000.c,v 1.4 2000/11/27 08:50:22 dwmw2 Exp $
*/
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
#define WINDOW_ADDR 0xbf000000
}
struct map_info pnc_map = {
- "PNC-2000",
- WINDOW_SIZE,
- 4,
- pnc_read8,
- pnc_read16,
- pnc_read32,
- pnc_copy_from,
- pnc_write8,
- pnc_write16,
- pnc_write32,
- pnc_copy_to,
- 0,
- 0
+ name: "PNC-2000",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: pnc_read8,
+ read16: pnc_read16,
+ read32: pnc_read32,
+ copy_from: pnc_copy_from,
+ write8: pnc_write8,
+ write16: pnc_write16,
+ write32: pnc_write32,
+ copy_to: pnc_copy_to
};
/*
* MTD 'PARTITIONING' STUFF
*/
-
-/*
- * This is the _real_ MTD device for which all the others are just
- * auto-relocating aliases.
- */
-static struct mtd_info *mymtd;
-
-/*
- * MTD methods which simply translate the effective address and pass through
- * to the _real_ device.
- */
-
-static int pnc_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int pnc_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
- return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int pnc_mtd_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- instr->addr += (unsigned long)mtd->priv;
- return mymtd->erase(mymtd, instr);
-}
-
-static void pnc_mtd_sync (struct mtd_info *mtd)
-{
- mymtd->sync(mymtd);
-}
-
-static int pnc_mtd_suspend (struct mtd_info *mtd)
-{
- return mymtd->suspend(mymtd);
-}
-
-static void pnc_mtd_resume (struct mtd_info *mtd)
-{
- mymtd->resume(mymtd);
-}
-
-
-static struct mtd_info pnc_mtds[3] = { /* boot, kernel, fs */
+static struct mtd_partition pnc_partitions[3] = {
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x20000,
- erasesize: 0x20000,
name: "PNC-2000 boot firmware",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0
+ size: 0x20000,
+ offset: 0
},
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x1a0000,
- erasesize: 0x20000,
name: "PNC-2000 kernel",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0x20000
+ size: 0x1a0000,
+ offset: 0x20000
},
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x240000,
- erasesize: 0x20000,
name: "PNC-2000 filesystem",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0x1c0000
+ size: 0x240000,
+ offset: 0x1c0000
}
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+/*
+ * This is the master MTD device for which all the others are just
+ * auto-relocating aliases.
+ */
+static struct mtd_info *mymtd;
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_pnc init_module
#define cleanup_pnc cleanup_module
#endif
-#endif
int __init init_pnc(void)
{
- printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+ printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
mymtd = do_cfi_probe(&pnc_map);
if (mymtd) {
mymtd->module = THIS_MODULE;
-
- add_mtd_device(&pnc_mtds[0]); /* boot */
- add_mtd_device(&pnc_mtds[1]); /* kernel */
- add_mtd_device(&pnc_mtds[2]); /* file system */
- return 0;
+ return add_mtd_partitions(mymtd, pnc_partitions, 3);
}
return -ENXIO;
static void __exit cleanup_pnc(void)
{
if (mymtd) {
- del_mtd_device(&pnc_mtds[2]);
- del_mtd_device(&pnc_mtds[1]);
- del_mtd_device(&pnc_mtds[0]);
+ del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
}
+
+module_init(init_pnc);
+module_exit(cleanup_pnc);
/*
- * $Id: rpxlite.c,v 1.2 2000/07/04 12:16:26 dwmw2 Exp $
+ * $Id: rpxlite.c,v 1.8 2000/12/09 22:00:31 dwmw2 Exp $
*
- * Handle the strange 16-in-32-bit mapping on the RPXLite board
+ * Handle mapping of the flash on the RPX Lite and CLLF boards
*/
#include <linux/module.h>
#include <linux/mtd/map.h>
-#define WINDOW_ADDR 0x8000000
-#define WINDOW_SIZE 0x2000000
-
-#define MAP_TO_ADR(x) ( ( ( x & ~1 ) << 1 ) | (x&1) )
+#define WINDOW_ADDR 0xfe000000
+#define WINDOW_SIZE 0x800000
static struct mtd_info *mymtd;
__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
{
- return readb(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readb(map->map_priv_1 * ofs);
}
__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
{
- return readw(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readw(map->map_priv_1 + ofs);
}
__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
{
- return readl(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readl(map->map_priv_1 + ofs);
}
void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- if (from & 1) {
- *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from));
- from++;
- len--;
- }
- /* Can't do this if it's not aligned */
- if (!((unsigned long)to & 1)) {
- unsigned long fromadr = MAP_TO_ADR(from);
-
- while (len > 1) {
- *(__u16 *)to = readw(map->map_priv_1 + fromadr);
- to += 2;
- fromadr += 4;
- from += 2;
- len -= 2;
- }
- }
- while(len) {
- *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from));
- to++;
- from++;
- len--;
- }
+ memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
}
void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
{
- writeb(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writeb(d, map->map_priv_1 + adr);
}
void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
{
- writew(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writew(d, map->map_priv_1 + adr);
}
void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
{
- writel(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writel(d, map->map_priv_1 + adr);
}
void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
- if (to & 1) {
- writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to));
- from++;
- len--;
- }
- /* Can't do this if it's not aligned */
- if (!((unsigned long)from & 1)) {
- unsigned long toadr = map->map_priv_1 + MAP_TO_ADR(to);
-
- while (len > 1) {
- writew(*(__u16 *)from, toadr);
- from += 2;
- toadr += 4;
- to += 2;
- len -= 2;
- }
- }
- while(len) {
- writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to));
- to++;
- from++;
- len--;
- }
+ memcpy_toio((void *)(map->map_priv_1 + to), from, len);
}
struct map_info rpxlite_map = {
- "RPXLITE",
- WINDOW_SIZE,
- 2,
- rpxlite_read8,
- rpxlite_read16,
- rpxlite_read32,
- rpxlite_copy_from,
- rpxlite_write8,
- rpxlite_write16,
- rpxlite_write32,
- rpxlite_copy_to,
- 0,
- 0
+ name: "RPX",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: rpxlite_read8,
+ read16: rpxlite_read16,
+ read32: rpxlite_read32,
+ copy_from: rpxlite_copy_from,
+ write8: rpxlite_write8,
+ write16: rpxlite_write16,
+ write32: rpxlite_write32,
+ copy_to: rpxlite_copy_to
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_rpxlite init_module
#define cleanup_rpxlite cleanup_module
#endif
-#endif
int __init init_rpxlite(void)
{
- printk(KERN_NOTICE "rpxlite flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 2);
+ printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+ rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
if (!rpxlite_map.map_priv_1) {
printk("Failed to ioremap\n");
rpxlite_map.map_priv_1 = 0;
}
}
+
+module_init(init_rpxlite);
+module_exit(cleanup_rpxlite);
-// $Id: vmax301.c,v 1.13 2000/07/03 10:01:38 dwmw2 Exp $
+// $Id: vmax301.c,v 1.15 2000/11/27 08:50:22 dwmw2 Exp $
/* ######################################################################
Tempustech VMAX SBC301 MTD Driver.
static struct map_info vmax_map[2] = {
{
- "VMAX301 Internal Flash",
- 3*2*1024*1024,
- 1,
- vmax301_read8,
- vmax301_read16,
- vmax301_read32,
- vmax301_copy_from,
- vmax301_write8,
- vmax301_write16,
- vmax301_write32,
- vmax301_copy_to,
- WINDOW_START + WINDOW_LENGTH,
- 0xFFFFFFFF
+ name: "VMAX301 Internal Flash",
+ size: 3*2*1024*1024,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + WINDOW_LENGTH,
+ map_priv_2: 0xFFFFFFFF
},
{
- "VMAX301 Socket",
- 0,
- 1,
- vmax301_read8,
- vmax301_read16,
- vmax301_read32,
- vmax301_copy_from,
- vmax301_write8,
- vmax301_write16,
- vmax301_write32,
- vmax301_copy_to,
- WINDOW_START + (3*WINDOW_LENGTH),
- 0xFFFFFFFF
+ name: "VMAX301 Socket",
+ size: 0,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
+ map_priv_2: 0xFFFFFFFF
}
};
static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_vmax301 init_module
#define cleanup_vmax301 cleanup_module
#endif
-#define __exit
-#endif
static void __exit cleanup_vmax301(void)
{
return 0;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_vmax301);
module_exit(cleanup_vmax301);
-#endif
/*
* Setup the bottom half rx ring refill handler
*/
- ap->immediate.next = NULL;
+ INIT_LIST_HEAD(&ap->immediate.list);
ap->immediate.sync = 0;
ap->immediate.routine = (void *)(void *)ace_bh;
ap->immediate.data = dev;
priv->command_semaphore_on = 0;
priv->unlock_command_postponed = 0;
- priv->immediate_bh.next = NULL;
+ INIT_LIST_HEAD(&priv->immediate_bh.list);
priv->immediate_bh.sync = 0;
priv->immediate_bh.routine = (void *)(void *)awc_bh;
priv->immediate_bh.data = dev;
{
int i;
int ioaddr;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+ SET_MODULE_OWNER(dev);
+
if(eth16i_debug > 4)
printk(KERN_DEBUG "Probing started for %s\n", cardname);
outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
-
return 0;
}
outb(0x00, ioaddr + CONFIG_REG_1);
- MOD_DEC_USE_COUNT;
-
return 0;
}
int __init ethertap_probe(struct net_device *dev)
{
+ SET_MODULE_OWNER(dev);
+
memcpy(dev->dev_addr, "\xFE\xFD\x00\x00\x00\x00", 6);
if (dev->mem_start & 0xf)
ethertap_debug = dev->mem_start & 0x7;
if (ethertap_debug > 2)
printk("%s: Doing ethertap_open()...", dev->name);
- MOD_INC_USE_COUNT;
-
lp->nl = netlink_kernel_create(dev->base_addr, ethertap_rx);
- if (lp->nl == NULL) {
- MOD_DEC_USE_COUNT;
+ if (lp->nl == NULL)
return -ENOBUFS;
- }
netif_start_queue(dev);
return 0;
}
sock_release(sk->socket);
}
- MOD_DEC_USE_COUNT;
return 0;
}
int tmp = num_ewrk3s, status = -ENODEV;
u_long iobase = dev->base_addr;
+ SET_MODULE_OWNER(dev);
+
if ((iobase == 0) && loading_module) {
printk("Autoprobing is not supported when loading a module based driver.\n");
status = -EIO;
return -EINVAL;
}
- MOD_INC_USE_COUNT;
-
return status;
}
if (!lp->hard_strapped) {
free_irq(dev->irq, dev);
}
- MOD_DEC_USE_COUNT;
-
return 0;
}
}
#ifdef MODULE
-static char devicename[9] =
-{0,};
-static struct net_device thisEthwrk =
-{
- devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0x300, 5, /* I/O address, IRQ */
- 0, 0, 0, NULL, ewrk3_probe};
-
+static struct net_device thisEthwrk;
static int io = 0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
static int irq = 5; /* or use the insmod io= irq= options */
{
thisEthwrk.base_addr = io;
thisEthwrk.irq = irq;
+ thisEthwrk.init = ewrk3_probe;
if (register_netdev(&thisEthwrk) != 0)
return -EIO;
return 0;
int i;
int base_addr = dev->base_addr;
+ SET_MODULE_OWNER(dev);
+
if (base_addr > 0x1ff) /* Check a single specified location. */
return fmv18x_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
/* Enable both Tx and Rx interrupts */
outw(0x8182, ioaddr+TX_INTR);
- MOD_INC_USE_COUNT;
-
return 0;
}
/* Power-down the chip. Green, green, green! */
outb(0x00, ioaddr + CONFIG_1);
- MOD_DEC_USE_COUNT;
-
/* Set the ethernet adaptor disable IRQ */
outb(0x00, ioaddr + FJ_CONFIG1);
iounmap((char *)ioaddr);
return -ENOMEM;
}
+ SET_MODULE_OWNER(dev);
#ifdef TX_CHECKSUM
printk("check that skbcopy in ip_queue_xmit isn't happening\n");
u_int32_t rx_int_var, tx_int_var;
u_int16_t fifo_info;
- MOD_INC_USE_COUNT;
-
- if (request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev)) {
- MOD_DEC_USE_COUNT;
- return -EAGAIN;
- }
+ i = request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev);
+ if (i)
+ return i;
if (hamachi_debug > 1)
printk(KERN_DEBUG "%s: hamachi_open() irq %d.\n",
writeb(0x00, ioaddr + LEDCtrl);
- MOD_DEC_USE_COUNT;
-
return 0;
}
struct baycom_state *bc;
struct parport *pp;
const struct tq_struct run_bh = {
- 0, 0, (void *)(void *)epp_bh, dev
+ routine: (void *)(void *)epp_bh,
+ data: dev
};
unsigned int i, j;
unsigned char tmp[128];
/* ---------------------------------------------------------------------- */
-static void inline do_kiss_params(struct hdlcdrv_state *s,
+static inline void do_kiss_params(struct hdlcdrv_state *s,
unsigned char *data, unsigned long len)
{
/* --------------------------------------------------------------------- */
-#ifdef MODULE
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
-
-/* --------------------------------------------------------------------- */
-
-int __init init_module(void)
+static int __init hdlcdrv_init_driver(void)
{
printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n");
printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n");
/* --------------------------------------------------------------------- */
-void cleanup_module(void)
+static void __exit hdlcdrv_cleanup_driver(void)
{
printk(KERN_INFO "hdlcdrv: cleanup\n");
}
-#endif /* MODULE */
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+module_init(hdlcdrv_init_driver);
+module_exit(hdlcdrv_cleanup_driver);
+
/* --------------------------------------------------------------------- */
int __init hp_plus_probe(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) /* Check a single specified location. */
return hpp_probe1(dev, base_addr);
outw(Perf_Page, ioaddr + HP_PAGING);
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
ioaddr + HPP_OPTION);
- MOD_DEC_USE_COUNT;
return 0;
}
\f
#ifdef MODULE
#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */
-static struct net_device dev_hpp[MAX_HPP_CARDS] = {
- {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, NULL
- },
-};
-
-static int io[MAX_HPP_CARDS] = { 0, };
-static int irq[MAX_HPP_CARDS] = { 0, };
+static struct net_device dev_hpp[MAX_HPP_CARDS];
+static int io[MAX_HPP_CARDS];
+static int irq[MAX_HPP_CARDS];
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HPP_CARDS) "i");
#define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */
int hp_probe(struct net_device *dev);
-int hp_probe1(struct net_device *dev, int ioaddr);
+static int hp_probe1(struct net_device *dev, int ioaddr);
static int hp_open(struct net_device *dev);
static int hp_close(struct net_device *dev);
int __init hp_probe(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) /* Check a single specified location. */
return hp_probe1(dev, base_addr);
return -ENODEV;
}
-int __init hp_probe1(struct net_device *dev, int ioaddr)
+static int __init hp_probe1(struct net_device *dev, int ioaddr)
{
int i, retval, board_id, wordmode;
const char *name;
static unsigned version_printed;
if (!request_region(ioaddr, HP_IO_EXTENT, dev->name))
- return -ENODEV;
+ return -EBUSY;
/* Check for the HP physical address, 08 00 09 xx xx xx. */
/* This really isn't good enough: we may pick up HP LANCE boards
hp_open(struct net_device *dev)
{
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
hp_close(struct net_device *dev)
{
ei_close(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
#define MAX_HP_CARDS 4 /* Max number of HP cards per module */
-static struct net_device dev_hp[MAX_HP_CARDS] = {
- {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, NULL
- },
-};
-
-static int io[MAX_HP_CARDS] = { 0, };
-static int irq[MAX_HP_CARDS] = { 0, };
+static struct net_device dev_hp[MAX_HP_CARDS];
+static int io[MAX_HP_CARDS];
+static int irq[MAX_HP_CARDS];
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HP_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_HP_CARDS) "i");
#define HP100_PCI_IDS_SIZE (sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id))
+#if LINUX_VERSION_CODE >= 0x20400
+static struct pci_device_id hp100_pci_tbl[] __initdata = {
+ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_COMPEX2, PCI_DEVICE_ID_COMPEX2_100VG, PCI_ANY_ID, PCI_ANY_ID, },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, hp100_pci_tbl);
+#endif /* LINUX_VERSION_CODE >= 0x20400 */
+
static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
static int hp100_mode = 1;
/* Reset statistics (counters) */
hp100_clear_stats( lp, ioaddr );
+ SET_MODULE_OWNER(dev);
ether_setup( dev );
/* If busmaster mode is wanted, a dma-capable memory area is needed for
return -EAGAIN;
}
- MOD_INC_USE_COUNT;
-
dev->trans_start = jiffies;
netif_start_queue(dev);
printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
#endif
- MOD_DEC_USE_COUNT;
return 0;
}
#include <asm/bitops.h>
#include <asm/io.h>
-#ifdef MODULE
#include <linux/module.h>
-#endif
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
dev->interrupt = 0;
dev->tbusy = 0;
dev->start = 1;
-#endif
-
-#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
free_irq(dev->irq, dev);
dev->irq = 0;
-#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x02032a)
MOD_DEC_USE_COUNT;
#endif
ibmlana_priv *priv;
ibmlana_medium medium;
+#if (LINUX_VERSION_CODE >= 0x02032a)
+ SET_MODULE_OWNER(dev);
+#endif
+
/* can't work without an MCA bus ;-) */
if (MCA_bus == 0)
#define DEVMAX 5
-static struct IBMLANA_NETDEV moddevs[DEVMAX] = {
- { init: ibmlana_probe }, { init: ibmlana_probe },
- { init: ibmlana_probe }, { init: ibmlana_probe },
- { init: ibmlana_probe }
-};
-
-int irq = 0;
-int io = 0;
+static struct IBMLANA_NETDEV moddevs[DEVMAX];
+static int irq;
+static int io;
+MODULE_PARM(irq, "i");
+MODULE_PARM(io, "i");
int init_module(void)
{
startslot = 0;
for (z = 0; z < DEVMAX; z++) {
- strcpy(moddevs[z].name, " ");
+ moddevs[z].init = ibmlana_probe;
+ moddevs[z].irq = irq;
+ moddevs[z].base_addr = io;
res = register_netdev(moddevs + z);
if (res != 0)
return (z > 0) ? 0 : -EIO;
/* Example routines you must write ;->. */
#define tx_done(dev) 1
-extern void hardware_send_packet(short ioaddr, char *buf, int length);
-extern void chipset_init(struct net_device *dev, int startp);
+static void hardware_send_packet(short ioaddr, char *buf, int length);
+static void chipset_init(struct net_device *dev, int startp);
/*
* Check for a network adaptor of this type, and return '0' iff one exists.
netcard_probe(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) /* Check a single specified location. */
return netcard_probe1(dev, base_addr);
*/
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
-
return 0;
}
/* Update the statistics here. */
- MOD_DEC_USE_COUNT;
-
return 0;
}
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
- cli();
/* Update the statistics from the device registers. */
lp->stats.rx_missed_errors = inw(ioaddr+1);
- sti();
-
return &lp->stats;
}
#ifdef MODULE
-static struct net_device this_device = { init: netcard_probe };
+static struct net_device this_device;
static int io = 0x300;
static int irq;
static int dma;
this_device.irq = irq;
this_device.dma = dma;
this_device.mem_start = mem;
+ this_device.init = netcard_probe;
if ((result = register_netdev(&this_device)) != 0)
return result;
#include "8390.h"
int lne390_probe(struct net_device *dev);
-int lne390_probe1(struct net_device *dev, int ioaddr);
+static int lne390_probe1(struct net_device *dev, int ioaddr);
static int lne390_open(struct net_device *dev);
static int lne390_close(struct net_device *dev);
unsigned short ioaddr = dev->base_addr;
int ret;
+ SET_MODULE_OWNER(dev);
+
if (ioaddr > 0x1ff) { /* Check a single specified location. */
- if (!request_region(ioaddr, LNE390_IO_EXTENT, "lne390"))
+ if (!request_region(ioaddr, LNE390_IO_EXTENT, dev->name))
return -EBUSY;
ret = lne390_probe1(dev, ioaddr);
if (ret)
/* EISA spec allows for up to 16 slots, but 8 is typical. */
for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
- if (!request_region(ioaddr, LNE390_IO_EXTENT, "lne390"))
+ if (!request_region(ioaddr, LNE390_IO_EXTENT, dev->name))
continue;
if (lne390_probe1(dev, ioaddr) == 0)
return 0;
return -ENODEV;
}
-int __init lne390_probe1(struct net_device *dev, int ioaddr)
+static int __init lne390_probe1(struct net_device *dev, int ioaddr)
{
int i, revision, ret;
unsigned long eisa_id;
}
printk(" IRQ %d,", dev->irq);
- if (request_irq(dev->irq, ei_interrupt, 0, "lne390", dev)) {
+ if ((ret = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) {
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return -EAGAIN;
+ return ret;
}
if (dev->mem_start == 0) {
static int lne390_open(struct net_device *dev)
{
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
printk("%s: Shutting down ethercard.\n", dev->name);
ei_close(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
#define MAX_LNE_CARDS 4 /* Max number of LNE390 cards per module */
-static struct net_device dev_lne[MAX_LNE_CARDS] = {
- {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, NULL
- },
-};
-
+static struct net_device dev_lne[MAX_LNE_CARDS];
static int io[MAX_LNE_CARDS];
static int irq[MAX_LNE_CARDS];
static int mem[MAX_LNE_CARDS];
u_long address;
if (!MACH_IS_MVME147 || called)
- return(ENODEV);
+ return(-ENODEV);
called++;
+ SET_MODULE_OWNER(dev);
+
dev->priv = kmalloc(sizeof(struct m147lance_private), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
m147_pcc->lan_cntrl=0; /* clear the interrupts (if any) */
m147_pcc->lan_cntrl=0x08 | 0x04; /* Enable irq 4 */
- MOD_INC_USE_COUNT;
return 0;
}
/* disable interrupts at boardlevel */
m147_pcc->lan_cntrl=0x0; /* disable interrupts */
lance_close(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
dev = init_etherdev(NULL, sizeof (struct netdev_private));
if (!dev)
return -ENOMEM;
+ SET_MODULE_OWNER(dev);
{
void *mmio;
/* Do we need to reset the chip??? */
- MOD_INC_USE_COUNT;
-
- if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
- MOD_DEC_USE_COUNT;
- return -EAGAIN;
- }
+ i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
+ if (i) return i;
if (debug > 1)
printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */
#endif
- MOD_DEC_USE_COUNT;
-
return 0;
}
printk(KERN_INFO "pcnet32_probe_pci: found device %#08x.%#08x\n", ent->vendor, ent->device);
ioaddr = pci_resource_start (pdev, 0);
- printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0));
+ printk(KERN_INFO " ioaddr=%#08lx resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0));
if (!ioaddr) {
printk (KERN_ERR "no PCI IO resources, aborting\n");
return -ENODEV;
/* There is a 16 byte station address PROM at the base address.
The first six bytes are the station address. */
for (i = 0; i < 6; i++)
- printk( KERN_INFO " %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */
i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */
- printk(KERN_INFO"\n tx_start_pt(0x%04x):",i);
+ printk("\n" KERN_INFO " tx_start_pt(0x%04x):",i);
switch(i>>10) {
- case 0: printk(KERN_INFO " 20 bytes,"); break;
- case 1: printk(KERN_INFO " 64 bytes,"); break;
- case 2: printk(KERN_INFO " 128 bytes,"); break;
- case 3: printk(KERN_INFO "~220 bytes,"); break;
+ case 0: printk(" 20 bytes,"); break;
+ case 1: printk(" 64 bytes,"); break;
+ case 2: printk(" 128 bytes,"); break;
+ case 3: printk("~220 bytes,"); break;
}
i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */
- printk(KERN_INFO" BCR18(%x):",i&0xffff);
- if (i & (1<<5)) printk(KERN_INFO "BurstWrEn ");
- if (i & (1<<6)) printk(KERN_INFO "BurstRdEn ");
- if (i & (1<<7)) printk(KERN_INFO "DWordIO ");
- if (i & (1<<11)) printk(KERN_INFO"NoUFlow ");
+ printk(" BCR18(%x):",i&0xffff);
+ if (i & (1<<5)) printk("BurstWrEn ");
+ if (i & (1<<6)) printk("BurstRdEn ");
+ if (i & (1<<7)) printk("DWordIO ");
+ if (i & (1<<11)) printk("NoUFlow ");
i = a->read_bcr(ioaddr, 25);
- printk(KERN_INFO "\n SRAMSIZE=0x%04x,",i<<8);
+ printk("\n" KERN_INFO " SRAMSIZE=0x%04x,",i<<8);
i = a->read_bcr(ioaddr, 26);
- printk(KERN_INFO " SRAM_BND=0x%04x,",i<<8);
+ printk(" SRAM_BND=0x%04x,",i<<8);
i = a->read_bcr(ioaddr, 27);
- if (i & (1<<14)) printk(KERN_INFO "LowLatRx,");
+ if (i & (1<<14)) printk("LowLatRx");
}
dev->base_addr = ioaddr;
memset(lp, 0, sizeof(*lp));
lp->dma_addr = lp_dma_addr;
lp->pci_dev = pdev;
- printk(KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x\n", lp, lp_dma_addr);
+ printk("\n" KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x", lp, lp_dma_addr);
spin_lock_init(&lp->lock);
}
if (dev->irq >= 2)
- printk(KERN_INFO " assigned IRQ %d.\n", dev->irq);
+ printk(" assigned IRQ %d.\n", dev->irq);
else {
unsigned long irq_mask = probe_irq_on();
dev->irq = probe_irq_off (irq_mask);
if (dev->irq)
- printk(KERN_INFO ", probed IRQ %d.\n", dev->irq);
+ printk(", probed IRQ %d.\n", dev->irq);
else {
- printk(KERN_ERR ", failed to detect IRQ line.\n");
+ printk(", failed to detect IRQ line.\n");
return -ENODEV;
}
}
lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
lp->cur_rx);
for (i = 0 ; i < RX_RING_SIZE; i++)
- printk(KERN_DEBUG "%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status);
for (i = 0 ; i < TX_RING_SIZE; i++)
- printk(KERN_DEBUG "%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
+ printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ",
lp->tx_ring[i].base, -lp->tx_ring[i].length,
lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
- printk(KERN_DEBUG "\n");
+ printk("\n");
}
pcnet32_restart(dev, 0x0042);
nl->nibble = PLIP_NIBBLE_WAIT;
/* Initialize task queue structures */
- nl->immediate.next = NULL;
+ INIT_LIST_HEAD(&nl->immediate.list);
nl->immediate.sync = 0;
nl->immediate.routine = (void (*)(void *))plip_bh;
nl->immediate.data = dev;
- nl->deferred.next = NULL;
+ INIT_LIST_HEAD(&nl->deferred.list);
nl->deferred.sync = 0;
nl->deferred.routine = (void (*)(void *))plip_kick_bh;
nl->deferred.data = dev;
if (dev->irq == -1) {
- nl->timer.next = NULL;
+ INIT_LIST_HEAD(&nl->timer.list);
nl->timer.sync = 0;
nl->timer.routine = (void (*)(void *))plip_timer_bh;
nl->timer.data = dev;
skb_reserve(skb, dev->hard_header_len);
skb->nh.raw = skb->data;
- skb->rx_dev = skb->dev = dev;
- dev_hold(skb->rx_dev);
+ skb->dev = dev;
skb->priority = sk->priority;
skb->protocol = __constant_htons(ETH_P_PPP_SES);
skb->nh.raw = skb->data;
- /* Change device of skb, update reference counts */
- if(skb->rx_dev)
- dev_put(skb->rx_dev);
- skb->rx_dev = skb->dev = dev;
- dev_hold(skb->rx_dev);
+ skb->dev = dev;
dev->hard_header(skb, dev, ETH_P_PPP_SES,
sk->protinfo.pppox->pppoe_pa.remote,
RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
{
unsigned long off;
- unsigned long *pMsg;
+ PU32 pMsg;
PPAB pPab;
int i;
long timeout = 0;
dev->rmem_end, serial_number, dev->irq);
dev = init_etherdev(dev, 0);
+ if (!dev)
+ return -ENOMEM;
+ SET_MODULE_OWNER(dev);
/* Make up a SB1000-specific-data structure. */
dev->priv = kmalloc(sizeof(struct sb1000_private), GFP_KERNEL);
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
return 0; /* Always succeed */
}
dev_kfree_skb(lp->rx_skb[i]);
}
}
- MOD_DEC_USE_COUNT;
return 0;
}
MODULE_PARM(io, "1-2i");
MODULE_PARM(irq, "i");
-static struct net_device dev_sb1000 = {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, sb1000_probe };
-
-static int io[2] = {0, 0};
-static int irq = 0;
+static struct net_device dev_sb1000;
+static int io[2];
+static int irq;
int
init_module(void)
printk(KERN_ERR "sb1000: can't register any device cm<n>\n");
return -ENFILE;
}
+ dev_sb1000.init = sb1000_probe;
dev_sb1000.base_addr = io[0];
/* rmem_end holds the second I/O address - fv */
dev_sb1000.rmem_end = io[1];
extern int comx_init(void);
extern int lmc_setup(void);
-extern int abyss_probe(void);
extern int madgemc_probe(void);
-extern int tms_pci_probe(void);
/* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */
#define __PAD6 "\0\0\0\0\0\0\0\0\0"
/*
* Token Ring Drivers
*/
-#ifdef CONFIG_ABYSS
- {abyss_probe, 0},
-#endif
#ifdef CONFIG_MADGEMC
{madgemc_probe, 0},
#endif
-#ifdef CONFIG_TMSPCI
- {tms_pci_probe, 0},
-#endif
-
{NULL, 0},
};
net_dev = init_etherdev(NULL, sizeof(struct sis900_private));
if (!net_dev)
return -ENOMEM;
+ SET_MODULE_OWNER(net_dev);
if (!request_region(ioaddr, SIS900_TOTAL_SIZE, net_dev->name)) {
printk(KERN_ERR "sis900.c: can't allocate I/O space at 0x%lX\n", ioaddr);
struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
long ioaddr = net_dev->base_addr;
u8 revision;
-
- MOD_INC_USE_COUNT;
+ int ret;
/* Soft reset the chip. */
sis900_reset(net_dev);
if (revision == SIS630E_REV || revision == SIS630EA1_REV)
sis630e_set_eq(net_dev);
- if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
- MOD_DEC_USE_COUNT;
- return -EAGAIN;
- }
+ ret = request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev);
+ if (ret)
+ return ret;
sis900_init_rxfilter(net_dev);
/* Green! Put the chip in low-power mode. */
- MOD_DEC_USE_COUNT;
-
return 0;
}
dev->interrupt = 0;
dev->tbusy = 0;
dev->start = 0;
-#endif
-
-#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
free_irq(dev->irq, dev);
dev->irq = 0;
-#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x02032a)
MOD_DEC_USE_COUNT;
#endif
if (MCA_bus == 0)
return -ENODEV;
+ SET_MODULE_OWNER(dev);
+
/* start address of 1 --> forced detection */
if (dev->mem_start == 1)
char *name;
};
-const struct smc_mca_adapters_t smc_mca_adapters[] = {
+static const struct smc_mca_adapters_t smc_mca_adapters[] = {
{ 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" },
{ 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" },
{ 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" },
int adapter = 0;
int tbase = 0;
int tirq = 0;
- int base_addr = dev ? dev->base_addr : 0;
- int irq = dev ? dev->irq : 0;
+ int base_addr = dev->base_addr;
+ int irq = dev->irq;
if (!MCA_bus) {
return -ENODEV;
}
+ SET_MODULE_OWNER(dev);
+
if (base_addr || irq) {
printk(KERN_INFO "Probing for SMC MCA adapter");
if (base_addr) {
*/
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
* "just in case"...
*/
- MOD_DEC_USE_COUNT;
-
return 0;
}
#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */
-static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS] =
-{
- {
- "",
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, NULL
- },
-};
-
-static int io[MAX_ULTRAMCA_CARDS] = { 0, };
-static int irq[MAX_ULTRAMCA_CARDS] = { 0, };
+static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS];
+static int io[MAX_ULTRAMCA_CARDS];
+static int irq[MAX_ULTRAMCA_CARDS];
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
int __init ultra_probe(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) /* Check a single specified location. */
return ultra_probe1(dev, base_addr);
outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
outb(0xff, dev->base_addr + EN0_ERWCNT);
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
/* We should someday disable shared memory and change to 8-bit mode
"just in case"... */
- MOD_DEC_USE_COUNT;
-
return 0;
}
#include "8390.h"
int ultra32_probe(struct net_device *dev);
-int ultra32_probe1(struct net_device *dev, int ioaddr);
+static int ultra32_probe1(struct net_device *dev, int ioaddr);
static int ultra32_open(struct net_device *dev);
static void ultra32_reset_8390(struct net_device *dev);
static void ultra32_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int __init ultra32_probe(struct net_device *dev)
{
- const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
- int ioaddr, edge, media;
+ int ioaddr;
if (!EISA_bus) return -ENODEV;
+ SET_MODULE_OWNER(dev);
+
/* EISA spec allows for up to 16 slots, but 8 is typical. */
for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000)
- if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 &&
- inb(ioaddr + ULTRA32_IDPORT) != 0xff &&
- inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) {
- media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
- edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
- printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
- ioaddr >> 12, ifmap[media],
- (edge ? "Edge Triggered" : "Level Sensitive"));
if (ultra32_probe1(dev, ioaddr) == 0)
- return 0;
- }
+ return 0;
+
return -ENODEV;
}
-int __init ultra32_probe1(struct net_device *dev, int ioaddr)
+static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
{
- int i;
+ int i, edge, media, retval;
int checksum = 0;
const char *model_name;
- static unsigned version_printed = 0;
+ static unsigned version_printed;
/* Values from various config regs. */
- unsigned char idreg = inb(ioaddr + 7);
- unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+ unsigned char idreg;
+ unsigned char reg4;
+ const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
+
+ if (!request_region(ioaddr, ULTRA32_IO_EXTENT, dev->name))
+ return -EBUSY;
+
+ if (inb(ioaddr + ULTRA32_IDPORT) == 0xff ||
+ inl(ioaddr + ULTRA32_IDPORT) != ULTRA32_ID) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ media = inb(ioaddr + ULTRA32_CFG7) & 0x03;
+ edge = inb(ioaddr + ULTRA32_CFG5) & 0x08;
+ printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n",
+ ioaddr >> 12, ifmap[media],
+ (edge ? "Edge Triggered" : "Level Sensitive"));
+
+ idreg = inb(ioaddr + 7);
+ reg4 = inb(ioaddr + 4) & 0x7f;
/* Check the ID nibble. */
- if ((idreg & 0xf0) != 0x20) /* SMC Ultra */
- return -ENODEV;
+ if ((idreg & 0xf0) != 0x20) { /* SMC Ultra */
+ retval = -ENODEV;
+ goto out;
+ }
/* Select the station address register set. */
outb(reg4, ioaddr + 4);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
- if ((checksum & 0xff) != 0xff)
- return -ENODEV;
+ if ((checksum & 0xff) != 0xff) {
+ retval = -ENODEV;
+ goto out;
+ }
if (ei_debug && version_printed++ == 0)
printk(version);
if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) {
printk("\nsmc-ultra32: Card RAM is disabled! "
"Run EISA config utility.\n");
- return -ENODEV;
+ retval = -ENODEV;
+ goto out;
}
if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0)
printk("\nsmc-ultra32: Ignoring Bus-Master enable bit. "
int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07];
if (irq == 0) {
printk(", failed to detect IRQ line.\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto out;
}
dev->irq = irq;
}
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (", no memory for dev->priv.\n");
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto out;
}
- /* OK, we are certain this is going to work. Setup the device. */
- request_region(ioaddr, ULTRA32_IO_EXTENT, model_name);
-
/* The 8390 isn't at the base address, so fake the offset */
dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET;
NS8390_init(dev, 0);
return 0;
+out:
+ release_region(ioaddr, ULTRA32_IO_EXTENT);
+ return retval;
}
static int ultra32_open(struct net_device *dev)
{
int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ;
+ int retval;
- if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev))
- return -EAGAIN;
+ retval = request_irq(dev->irq, ei_interrupt, irq_flags, dev->name, dev);
+ if (retval)
+ return retval;
outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */
outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
outb(0xff, dev->base_addr + EN0_ERWCNT);
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
NS8390_init(dev, 0);
- MOD_DEC_USE_COUNT;
-
return 0;
}
. Test if a given location contains a chip, trying to cause as
. little damage as possible if it's not a SMC chip.
*/
-static int smc_probe( int ioaddr );
-
-/*
- . this routine initializes the cards hardware, prints out the configuration
- . to the system log as well as the vanity message, and handles the setup
- . of a device parameter.
- . It will give an error if it can't initialize the card.
-*/
-static int smc_initcard( struct net_device *, int ioaddr );
+static int smc_probe(struct net_device *dev, int ioaddr);
/*
. A rather simple routine to print out a packet for debugging purposes.
int __init smc_init(struct net_device *dev)
{
int i;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
/* try a specific location */
- if (base_addr > 0x1ff) {
- int error;
- error = smc_probe(base_addr);
- if ( 0 == error ) {
- return smc_initcard( dev, base_addr );
- }
- return error;
- } else {
- if ( 0 != base_addr ) {
- return -ENXIO;
- }
- }
+ if (base_addr > 0x1ff)
+ return smc_probe(dev, base_addr);
+ else if (base_addr != 0)
+ return -ENXIO;
/* check every ethernet address */
- for (i = 0; smc_portlist[i]; i++) {
- int ioaddr = smc_portlist[i];
-
- /* check if the area is available */
- if (check_region( ioaddr , SMC_IO_EXTENT))
- continue;
-
- /* check this specific address */
- if ( smc_probe( ioaddr ) == 0) {
- return smc_initcard( dev, ioaddr );
- }
- }
+ for (i = 0; smc_portlist[i]; i++)
+ if (smc_probe(dev, smc_portlist[i]) == 0)
+ return 0;
/* couldn't find anything */
return -ENODEV;
.---------------------------------------------------------------------
*/
-static int __init smc_probe( int ioaddr )
+/*---------------------------------------------------------------
+ . Here I do typical initialization tasks.
+ .
+ . o Initialize the structure if needed
+ . o print out my vanity message if not done so already
+ . o print out what type of hardware is detected
+ . o print out the ethernet address
+ . o find the IRQ
+ . o set up my private data
+ . o configure the dev structure with my subroutines
+ . o actually GRAB the irq.
+ . o GRAB the region
+ .-----------------------------------------------------------------
+*/
+static int __init smc_probe(struct net_device *dev, int ioaddr)
{
- unsigned int bank;
- word revision_register;
- word base_address_register;
+ int i, memory, retval;
+ static unsigned version_printed;
+ unsigned int bank;
+
+ const char *version_string;
+ const char *if_string;
+
+ /* registers */
+ word revision_register;
+ word base_address_register;
+ word configuration_register;
+ word memory_info_register;
+ word memory_cfg_register;
+
+ /* Grab the region so that no one else tries to probe our ioports. */
+ if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name))
+ return -EBUSY;
/* First, see if the high byte is 0x33 */
bank = inw( ioaddr + BANK_SELECT );
if ( (bank & 0xFF00) != 0x3300 ) {
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
/* The above MIGHT indicate a device, but I need to write to further
test this. */
outw( 0x0, ioaddr + BANK_SELECT );
bank = inw( ioaddr + BANK_SELECT );
if ( (bank & 0xFF00 ) != 0x3300 ) {
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
/* well, we've already written once, so hopefully another time won't
hurt. This time, I need to switch the bank register to bank 1,
ioaddr, base_address_register >> 3 & 0x3E0 );
/* well, the base address register didn't match. Must not have
been a SMC chip after all. */
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
/* check if the revision register is something that I recognize.
printk(CARDNAME ": IO %x: Unrecognized revision register:"
" %x, Contact author. \n", ioaddr, revision_register );
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
/* at this point I'll assume that the chip is an SMC9xxx.
It might be prudent to check a listing of MAC addresses
against the hardware address, or do some other tests. */
- return 0;
-}
-
-/*---------------------------------------------------------------
- . Here I do typical initialization tasks.
- .
- . o Initialize the structure if needed
- . o print out my vanity message if not done so already
- . o print out what type of hardware is detected
- . o print out the ethernet address
- . o find the IRQ
- . o set up my private data
- . o configure the dev structure with my subroutines
- . o actually GRAB the irq.
- . o GRAB the region
- .-----------------------------------------------------------------
-*/
-static int __init smc_initcard(struct net_device *dev, int ioaddr)
-{
- int i;
-
- static unsigned version_printed = 0;
-
- /* registers */
- word revision_register;
- word configuration_register;
- word memory_info_register;
- word memory_cfg_register;
-
- const char * version_string;
- const char * if_string;
- int memory;
-
- int irqval;
if (version_printed++ == 0)
printk("%s", version);
version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ];
if ( !version_string ) {
/* I shouldn't get here because this call was done before.... */
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
/* is it using AUI or 10BaseT ? */
}
if (dev->irq == 0 ) {
printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
- return -ENODEV;
+ retval = -ENODEV;
+ goto err_out;
}
if (dev->irq == 2) {
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
/* now, print out the card info, in a short format.. */
- printk(CARDNAME ": %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ",
+ printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
version_string, revision_register & 0xF, ioaddr, dev->irq,
if_string, memory );
/*
/* Initialize the private structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
+ if (dev->priv == NULL) {
+ retval = -ENOMEM;
+ goto err_out;
+ }
}
/* set the private data to zero by default */
memset(dev->priv, 0, sizeof(struct smc_local));
ether_setup(dev);
/* Grab the IRQ */
- irqval = request_irq(dev->irq, &smc_interrupt, 0, CARDNAME, dev);
- if (irqval) {
- printk(CARDNAME": unable to get IRQ %d (irqval=%d).\n",
- dev->irq, irqval);
- return -EAGAIN;
+ retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
+ if (retval) {
+ printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, retval);
+ kfree(dev->priv);
+ dev->priv = NULL;
+ goto err_out;
}
- /* Grab the region so that no one else tries to probe our ioports. */
- request_region(ioaddr, SMC_IO_EXTENT, CARDNAME);
-
dev->open = smc_open;
dev->stop = smc_close;
dev->hard_start_xmit = smc_wait_to_send_packet;
dev->set_multicast_list = smc_set_multicast_list;
return 0;
+
+err_out:
+ release_region(ioaddr, SMC_IO_EXTENT);
+ return retval;
}
#if SMC_DEBUG > 2
/* clear out all the junk that was put here before... */
memset(dev->priv, 0, sizeof(struct smc_local));
- MOD_INC_USE_COUNT;
-
/* reset the hardware */
smc_reset( ioaddr );
smc_shutdown( dev->base_addr );
/* Update the statistics here. */
- MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
-static struct net_device devSMC9194 = { init: smc_init };
-
+static struct net_device devSMC9194;
static int io;
static int irq;
static int ifport;
devSMC9194.base_addr = io;
devSMC9194.irq = irq;
devSMC9194.if_port = ifport;
+ devSMC9194.init = smc_init;
if ((result = register_netdev(&devSMC9194)) != 0)
return result;
dev = init_etherdev(NULL, sizeof(*np));
if (!dev)
return -ENOMEM;
+ SET_MODULE_OWNER(dev);
#ifdef USE_IO_OPS
ioaddr = pci_resource_start(pdev, 0);
/* Do we need to reset the chip??? */
- MOD_INC_USE_COUNT;
-
- if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
- MOD_DEC_USE_COUNT;
- return -EAGAIN;
- }
+ i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
+ if (i)
+ return i;
if (debug > 1)
printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
np->tx_skbuff[i] = 0;
}
- MOD_DEC_USE_COUNT;
-
return 0;
}
-/* $Id: sunhme.c,v 1.104 2000/11/17 01:40:00 davem Exp $
+/* $Id: sunhme.c,v 1.105 2000/12/05 02:00:36 anton Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
/* Shut up the MIF. */
HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ",
- hme_read32(hp, &tregs->int_mask)));
+ hme_read32(hp, tregs + TCVR_IMASK)));
hme_write32(hp, tregs + TCVR_IMASK, 0xffff);
/* See if we can enable the MIF frame on this card to speak to the DP83840. */
if (hp->happy_flags & HFLAG_FENABLE) {
HMD(("use frame old[%08x], ",
- hme_read32(hp, &tregs->cfg)));
+ hme_read32(hp, tregs + TCVR_CFG)));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_BENABLE));
} else {
HMD(("use bitbang old[%08x], ",
- hme_read32(hp, &tregs->cfg)));
+ hme_read32(hp, tregs + TCVR_CFG)));
hme_write32(hp, tregs + TCVR_CFG,
hme_read32(hp, tregs + TCVR_CFG) | TCV_CFG_BENABLE);
}
printk(KERN_ERR "TLAN: Could not allocate memory for device.\n");
return -ENOMEM;
}
+ SET_MODULE_OWNER(dev);
priv = dev->priv;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
int err;
- MOD_INC_USE_COUNT;
-
priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
if ( err ) {
printk(KERN_ERR "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
- MOD_DEC_USE_COUNT;
return err;
}
TLan_FreeLists( dev );
TLAN_DBG( TLAN_DEBUG_GNRL, "Device %s closed.\n", dev->name );
- MOD_DEC_USE_COUNT;
-
return 0;
} /* TLan_Close */
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(__sparc_)
+ || defined(__sparc_) || defined(__ia64__)
static int rx_copybreak = 1518;
#else
static int rx_copybreak = 100;
ToDo: Non-Intel setting could be better.
*/
-#if defined(__alpha__)
+#if defined(__alpha__) || defined(__ia64__)
static int csr0 = 0x01A00000 | 0xE000;
#elif defined(__i386__) || defined(__powerpc__)
static int csr0 = 0x01A00000 | 0x8000;
card_idx);
goto err_out_free_dma;
}
+ SET_MODULE_OWNER(dev);
/* request all PIO and MMIO regions just to make sure
* noone else attempts to use any portion of our I/O space */
long ioaddr = dev->base_addr;
int i;
- MOD_INC_USE_COUNT;
-
/* Reset the chip. */
writew(CmdReset, ioaddr + ChipCmd);
- if (request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev)) {
- MOD_DEC_USE_COUNT;
- return -EBUSY;
- }
+ i = request_irq(dev->irq, &via_rhine_interrupt, SA_SHIRQ, dev->name, dev);
+ if (i)
+ return i;
if (debug > 1)
printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n",
&np->tx_bufs_dma);
if (np->tx_bufs == NULL) {
free_irq(dev->irq, dev);
- MOD_DEC_USE_COUNT;
return -ENOMEM;
}
pci_free_consistent(np->pdev, PKT_BUF_SZ * TX_RING_SIZE,
np->tx_bufs, np->tx_bufs_dma);
- MOD_DEC_USE_COUNT;
-
return 0;
}
struct mixcom_privdata {
u16 clock;
char channel;
- char txbusy;
+ long txbusy;
struct sk_buff *sending;
unsigned tx_ptr;
struct sk_buff *recving;
unsigned char line_status;
struct timer_list lineup_timer; // against line jitter
- int lineup_pending;
+ long int lineup_pending;
unsigned char lineup_delay;
#if 0
struct timer_list reset_timer; // for board resetting
- int reset_pending;
+ long reset_pending;
int reset_timeout;
#endif
static sdla_t* card_array; /* adapter data space */
/* Task queue element for creating a 'thread' */
-static struct tq_struct sdla_tq =
-{
- NULL, /* .next */
- 0, /* .sync */
- &sdla_poll, /* .routine */
- NULL /* .data */
-};
-
+static struct tq_struct sdla_tq = { routine: sdla_poll };
/******* Kernel Loadable Module Entry Points ********************************/
int mtu; /* Our mtu (to spot changes!) */
int buffsize; /* Max buffers sizes */
- unsigned int flags; /* Flag values/ mode etc */
+ unsigned long flags; /* Flag values/ mode etc */
#define SLF_INUSE 0 /* Channel in use */
#define SLF_ESCAPE 1 /* ESC received */
#define SLF_ERROR 2 /* Parity, etc. error */
{
int i;
struct resource *r;
- int base_addr = dev ? dev->base_addr : 0;
+ int base_addr = dev->base_addr;
+
+ SET_MODULE_OWNER(dev);
if (base_addr > 0x1ff) { /* Check a user specified location. */
r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
if (i != 0)
release_resource(r);
else
- r->name = ei_status.name;
+ r->name = dev->name;
return i;
}
else if (base_addr != 0) /* Don't probe at all. */
if (r == NULL)
continue;
if (wd_probe1(dev, ioaddr) == 0) {
- r->name = ei_status.name;
+ r->name = dev->name;
return 0;
}
release_resource(r);
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
- if (request_irq(dev->irq, ei_interrupt, 0, model_name, dev)) {
+ i = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
+ if (i) {
printk (" unable to get IRQ %d.\n", dev->irq);
kfree(dev->priv);
dev->priv = NULL;
- return -EAGAIN;
+ return i;
}
/* OK, were are certain this is going to work. Setup the device. */
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
ei_open(dev);
- MOD_INC_USE_COUNT;
return 0;
}
/* And disable the shared memory. */
outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
- MOD_DEC_USE_COUNT;
-
return 0;
}
dev = init_etherdev(NULL, sizeof(*np));
if (!dev)
return -ENOMEM;
+ SET_MODULE_OWNER(dev);
#ifdef USE_IO_OPS
ioaddr = pci_resource_start(pdev, 0);
writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */
- MOD_INC_USE_COUNT;
-
- if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
- MOD_DEC_USE_COUNT;
- return -EAGAIN;
- }
+ i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
+ if (i)
+ return i;
if (debug > 1)
printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n",
np->tx_skbuff[i] = 0;
}
- MOD_DEC_USE_COUNT;
-
return 0;
}
for status. */
struct yellowfin_desc rx_ring[RX_RING_SIZE];
struct yellowfin_desc tx_ring[TX_RING_SIZE*2];
- const char *product_name;
- struct net_device *next_module;
- void *priv_addr; /* Unaligned address for kfree */
/* The addresses of receive-in-place skbuffs. */
struct sk_buff* rx_skbuff[RX_RING_SIZE];
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
printk(KERN_DEBUG "%s: yellowfin_open() irq %d.\n",
dev->name, dev->irq);
- MOD_INC_USE_COUNT;
-
yellowfin_init_ring(dev);
YF_OUTL(virt_to_bus(yp->rx_ring), ioaddr + RxPtr);
dev->name, bogus_rx);
}
#endif
- MOD_DEC_USE_COUNT;
-
return 0;
}
chip_idx = ent->driver_data;
flags = chip_info[chip_idx].flags;
- dev = init_etherdev(NULL, 0);
+ dev = init_etherdev(NULL, sizeof(*yp));
if (!dev) {
printk (KERN_ERR PFX "cannot allocate ethernet device\n");
return -ENOMEM;
}
+ SET_MODULE_OWNER(dev);
- dev->priv = kmalloc(sizeof(*yp) + PRIV_ALIGN, GFP_KERNEL);
- if (!dev->priv)
- goto err_out_free_netdev;
- yp = (void *)(((long)dev->priv + PRIV_ALIGN) & ~PRIV_ALIGN);
- memset(yp, 0, sizeof(*yp));
- yp->priv_addr = dev->priv; /* store real addr for kfree */
- dev->priv = yp; /* use aligned addr */
+ yp = dev->priv;
if (!request_region (pci_resource_start (pdev, 0),
YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) {
printk (KERN_ERR PFX "cannot obtain I/O port region\n");
- goto err_out_free_priv;
+ goto err_out_free_netdev;
}
if (!request_mem_region (pci_resource_start (pdev, 1),
YELLOWFIN_SIZE, YELLOWFIN_MODULE_NAME)) {
goto err_out_free_pio_region;
}
- /* XXX check enable_device for failure */
- pci_enable_device (pdev);
+ if (pci_enable_device (pdev))
+ goto err_out_free_mmio_region;
pci_set_master (pdev);
#ifdef USE_IO_OPS
#else
real_ioaddr = ioaddr = pci_resource_start (pdev, 1);
ioaddr = (long) ioremap(ioaddr, YELLOWFIN_SIZE);
- /* XXX check for failure */
+ if (!ioaddr)
+ goto err_out_free_mmio_region;
#endif
irq = pdev->irq;
return 0;
+err_out_free_mmio_region:
+ release_mem_region (pci_resource_start (pdev, 1), YELLOWFIN_SIZE);
err_out_free_pio_region:
release_region (pci_resource_start (pdev, 0), YELLOWFIN_SIZE);
-err_out_free_priv:
- kfree (dev->priv);
err_out_free_netdev:
unregister_netdev (dev);
kfree (dev);
struct net_device *dev = pdev->driver_data;
struct yellowfin_private *np;
- if (!dev) {
- printk (KERN_ERR "remove non-existent device\n");
- return;
- }
- np = (struct yellowfin_private *) dev->priv;
+ if (!dev)
+ BUG();
+ np = dev->priv;
unregister_netdev (dev);
-#ifdef USE_IO_OPS
release_region (dev->base_addr, YELLOWFIN_SIZE);
-#else
- iounmap ((void *) dev->base_addr);
release_mem_region (dev->base_addr, YELLOWFIN_SIZE);
-#endif
- if (np->priv_addr)
- kfree (np->priv_addr);
+#ifndef USE_IO_OPS
+ iounmap ((void *) dev->base_addr);
+#endif
kfree (dev);
}
if (debug) /* Emit version even if no cards detected. */
printk(KERN_INFO "%s", version);
- if (pci_register_driver (&yellowfin_driver) > 0)
- return 0;
-
- pci_unregister_driver (&yellowfin_driver);
- return -ENODEV;
+ return pci_module_init (&yellowfin_driver);
}
+2000-11-21 Tim Waugh <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_ecp_write_block_pio): Fix
+ reverse-to-forward logic. Spotted by Roland Kuck
+ <rci@cityweb.de>.
+
2000-09-16 Cesar Eduardo Barros <cesarb@nitnet.com.br>
* parport_pc.c (sio_via_686a_probe): Handle case
obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o
obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o
obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o
+obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
--- /dev/null
+/*
+ * Low-level parallel-support for PC-style hardware integrated in the
+ * LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
+ *
+ * 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.
+ *
+ * by Helge Deller <deller@gmx.de>
+ *
+ *
+ * based on parport_pc.c by
+ * Grant Guenther <grant@torque.net>
+ * Phil Blundell <Philip.Blundell@pobox.com>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Jose Renau <renau@acm.org>
+ * David Campbell <campbell@torque.net>
+ * Andrea Arcangeli
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/sysctl.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#include <linux/parport.h>
+#include <asm/gsc.h>
+#include <asm/pdc.h>
+#include <asm/hardware.h>
+#include <asm/parport_gsc.h>
+
+
+#undef DEBUG /* undef for production */
+
+#ifdef DEBUG
+#define DPRINTK printk
+#else
+#define DPRINTK(stuff...)
+#endif
+
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ *
+ * This is also used in SPP detection.
+ */
+static int clear_epp_timeout(struct parport *pb)
+{
+ unsigned char r;
+
+ if (!(parport_gsc_read_status(pb) & 0x01))
+ return 1;
+
+ /* To clear timeout some chips require double read */
+ parport_gsc_read_status(pb);
+ r = parport_gsc_read_status(pb);
+ parport_writeb (r | 0x01, STATUS (pb)); /* Some reset by writing 1 */
+ parport_writeb (r & 0xfe, STATUS (pb)); /* Others by writing 0 */
+ r = parport_gsc_read_status(pb);
+
+ return !(r & 0x01);
+}
+
+/*
+ * Access functions.
+ *
+ * Most of these aren't static because they may be used by the
+ * parport_xxx_yyy macros. extern __inline__ versions of several
+ * of these are in parport_gsc.h.
+ */
+
+static void parport_gsc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ DPRINTK(__FILE__ ": got IRQ\n");
+ parport_generic_irq(irq, (struct parport *) dev_id, regs);
+}
+
+void parport_gsc_write_data(struct parport *p, unsigned char d)
+{
+ DPRINTK(__FILE__ ": write (0x%02x) %c \n", d, d);
+ parport_writeb (d, DATA (p));
+}
+
+unsigned char parport_gsc_read_data(struct parport *p)
+{
+#ifdef DEBUG
+ unsigned char c = parport_readb (DATA (p));
+ DPRINTK(__FILE__ ": read (0x%02x) %c\n", c,c);
+ return c;
+#else
+ return parport_readb (DATA (p));
+#endif
+}
+
+void parport_gsc_write_control(struct parport *p, unsigned char d)
+{
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ /* Take this out when drivers have adapted to the newer interface. */
+ if (d & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+ p->name, p->cad->name);
+ parport_gsc_data_reverse (p);
+ }
+
+ __parport_gsc_frob_control (p, wm, d & wm);
+}
+
+unsigned char parport_gsc_read_control(struct parport *p)
+{
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+ const struct parport_gsc_private *priv = p->physport->private_data;
+ return priv->ctr & wm; /* Use soft copy */
+}
+
+unsigned char parport_gsc_frob_control (struct parport *p, unsigned char mask,
+ unsigned char val)
+{
+ const unsigned char wm = (PARPORT_CONTROL_STROBE |
+ PARPORT_CONTROL_AUTOFD |
+ PARPORT_CONTROL_INIT |
+ PARPORT_CONTROL_SELECT);
+
+ /* Take this out when drivers have adapted to the newer interface. */
+ if (mask & 0x20) {
+ printk (KERN_DEBUG "%s (%s): use data_%s for this!\n",
+ p->name, p->cad->name,
+ (val & 0x20) ? "reverse" : "forward");
+ if (val & 0x20)
+ parport_gsc_data_reverse (p);
+ else
+ parport_gsc_data_forward (p);
+ }
+
+ /* Restrict mask and val to control lines. */
+ mask &= wm;
+ val &= wm;
+
+ return __parport_gsc_frob_control (p, mask, val);
+}
+
+unsigned char parport_gsc_read_status(struct parport *p)
+{
+ return parport_readb (STATUS (p));
+}
+
+void parport_gsc_disable_irq(struct parport *p)
+{
+ __parport_gsc_frob_control (p, 0x10, 0);
+}
+
+void parport_gsc_enable_irq(struct parport *p)
+{
+ __parport_gsc_frob_control (p, 0x10, 0x10);
+}
+
+void parport_gsc_data_forward (struct parport *p)
+{
+ __parport_gsc_frob_control (p, 0x20, 0);
+}
+
+void parport_gsc_data_reverse (struct parport *p)
+{
+ __parport_gsc_frob_control (p, 0x20, 0x20);
+}
+
+void parport_gsc_init_state(struct pardevice *dev, struct parport_state *s)
+{
+ s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+}
+
+void parport_gsc_save_state(struct parport *p, struct parport_state *s)
+{
+ s->u.pc.ctr = parport_readb (CONTROL (p));
+}
+
+void parport_gsc_restore_state(struct parport *p, struct parport_state *s)
+{
+ parport_writeb (s->u.pc.ctr, CONTROL (p));
+}
+
+void parport_gsc_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+void parport_gsc_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+struct parport_operations parport_gsc_ops =
+{
+ write_data: parport_gsc_write_data,
+ read_data: parport_gsc_read_data,
+
+ write_control: parport_gsc_write_control,
+ read_control: parport_gsc_read_control,
+ frob_control: parport_gsc_frob_control,
+
+ read_status: parport_gsc_read_status,
+
+ enable_irq: parport_gsc_enable_irq,
+ disable_irq: parport_gsc_disable_irq,
+
+ data_forward: parport_gsc_data_forward,
+ data_reverse: parport_gsc_data_reverse,
+
+ init_state: parport_gsc_init_state,
+ save_state: parport_gsc_save_state,
+ restore_state: parport_gsc_restore_state,
+
+ inc_use_count: parport_gsc_inc_use_count,
+ dec_use_count: parport_gsc_dec_use_count,
+
+ epp_write_data: parport_ieee1284_epp_write_data,
+ epp_read_data: parport_ieee1284_epp_read_data,
+ epp_write_addr: parport_ieee1284_epp_write_addr,
+ epp_read_addr: parport_ieee1284_epp_read_addr,
+
+ ecp_write_data: parport_ieee1284_ecp_write_data,
+ ecp_read_data: parport_ieee1284_ecp_read_data,
+ ecp_write_addr: parport_ieee1284_ecp_write_addr,
+
+ compat_write_data: parport_ieee1284_write_compat,
+ nibble_read_data: parport_ieee1284_read_nibble,
+ byte_read_data: parport_ieee1284_read_byte,
+};
+
+/* --- Mode detection ------------------------------------- */
+
+/*
+ * Checks for port existence, all ports support SPP MODE
+ */
+static int __devinit parport_SPP_supported(struct parport *pb)
+{
+ unsigned char r, w;
+
+ /*
+ * first clear an eventually pending EPP timeout
+ * I (sailer@ife.ee.ethz.ch) have an SMSC chipset
+ * that does not even respond to SPP cycles if an EPP
+ * timeout is pending
+ */
+ clear_epp_timeout(pb);
+
+ /* Do a simple read-write test to make sure the port exists. */
+ w = 0xc;
+ parport_writeb (w, CONTROL (pb));
+
+ /* Is there a control register that we can read from? Some
+ * ports don't allow reads, so read_control just returns a
+ * software copy. Some ports _do_ allow reads, so bypass the
+ * software copy here. In addition, some bits aren't
+ * writable. */
+ r = parport_readb (CONTROL (pb));
+ if ((r & 0xf) == w) {
+ w = 0xe;
+ parport_writeb (w, CONTROL (pb));
+ r = parport_readb (CONTROL (pb));
+ parport_writeb (0xc, CONTROL (pb));
+ if ((r & 0xf) == w)
+ return PARPORT_MODE_PCSPP;
+ }
+
+ /* Try the data register. The data lines aren't tri-stated at
+ * this stage, so we expect back what we wrote. */
+ w = 0xaa;
+ parport_gsc_write_data (pb, w);
+ r = parport_gsc_read_data (pb);
+ if (r == w) {
+ w = 0x55;
+ parport_gsc_write_data (pb, w);
+ r = parport_gsc_read_data (pb);
+ if (r == w)
+ return PARPORT_MODE_PCSPP;
+ }
+
+ return 0;
+}
+
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines. In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero. So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present.
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal. Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic.
+ */
+
+static int __devinit parport_PS2_supported(struct parport *pb)
+{
+ int ok = 0;
+
+ clear_epp_timeout(pb);
+
+ /* try to tri-state the buffer */
+ parport_gsc_data_reverse (pb);
+
+ parport_gsc_write_data(pb, 0x55);
+ if (parport_gsc_read_data(pb) != 0x55) ok++;
+
+ parport_gsc_write_data(pb, 0xaa);
+ if (parport_gsc_read_data(pb) != 0xaa) ok++;
+
+ /* cancel input mode */
+ parport_gsc_data_forward (pb);
+
+ if (ok) {
+ pb->modes |= PARPORT_MODE_TRISTATE;
+ } else {
+ struct parport_gsc_private *priv = pb->private_data;
+ priv->ctr_writable &= ~0x20;
+ }
+
+ return ok;
+}
+
+
+/* --- Initialisation code -------------------------------- */
+
+struct parport *__devinit parport_gsc_probe_port (unsigned long base,
+ unsigned long base_hi,
+ int irq, int dma,
+ struct pci_dev *dev)
+{
+ struct parport_gsc_private *priv;
+ struct parport_operations *ops;
+ struct parport tmp;
+ struct parport *p = &tmp;
+
+ if (check_region(base, 3))
+ return NULL;
+
+ priv = kmalloc (sizeof (struct parport_gsc_private), GFP_KERNEL);
+ if (!priv) {
+ printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
+ return NULL;
+ }
+ ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL);
+ if (!ops) {
+ printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n",
+ base);
+ kfree (priv);
+ return NULL;
+ }
+ memcpy (ops, &parport_gsc_ops, sizeof (struct parport_operations));
+ priv->ctr = 0xc;
+ priv->ctr_writable = 0xff;
+ priv->dma_buf = 0;
+ priv->dma_handle = 0;
+ priv->dev = dev;
+ p->base = base;
+ p->base_hi = base_hi;
+ p->irq = irq;
+ p->dma = dma;
+ p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
+ p->ops = ops;
+ p->private_data = priv;
+ p->physport = p;
+ if (!parport_SPP_supported (p)) {
+ /* No port. */
+ kfree (priv);
+ return NULL;
+ }
+ parport_PS2_supported (p);
+
+ if (!(p = parport_register_port(base, PARPORT_IRQ_NONE,
+ PARPORT_DMA_NONE, ops))) {
+ kfree (priv);
+ kfree (ops);
+ return NULL;
+ }
+
+ p->base_hi = base_hi;
+ p->modes = tmp.modes;
+ p->size = (p->modes & PARPORT_MODE_EPP)?8:3;
+ p->private_data = priv;
+
+ printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
+ p->irq = irq;
+ if (p->irq == PARPORT_IRQ_AUTO) {
+ p->irq = PARPORT_IRQ_NONE;
+ }
+ if (p->irq != PARPORT_IRQ_NONE) {
+ printk(", irq %d", p->irq);
+
+ if (p->dma == PARPORT_DMA_AUTO) {
+ p->dma = PARPORT_DMA_NONE;
+ }
+ }
+ if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq
+ is mandatory (see above) */
+ p->dma = PARPORT_DMA_NONE;
+
+ printk(" [");
+#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+ {
+ int f = 0;
+ printmode(PCSPP);
+ printmode(TRISTATE);
+ printmode(COMPAT)
+ printmode(EPP);
+// printmode(ECP);
+// printmode(DMA);
+ }
+#undef printmode
+ printk("]\n");
+ parport_proc_register(p);
+
+ request_region (p->base, 3, p->name);
+ if (p->size > 3)
+ request_region (p->base + 3, p->size - 3, p->name);
+ if (p->modes & PARPORT_MODE_ECP)
+ request_region (p->base_hi, 3, p->name);
+
+ if (p->irq != PARPORT_IRQ_NONE) {
+ if (request_irq (p->irq, parport_gsc_interrupt,
+ 0, p->name, p)) {
+ printk (KERN_WARNING "%s: irq %d in use, "
+ "resorting to polled operation\n",
+ p->name, p->irq);
+ p->irq = PARPORT_IRQ_NONE;
+ p->dma = PARPORT_DMA_NONE;
+ }
+ }
+
+ /* Done probing. Now put the port into a sensible start-up state. */
+
+ parport_gsc_write_data(p, 0);
+ parport_gsc_data_forward (p);
+
+ /* Now that we've told the sharing engine about the port, and
+ found out its characteristics, let the high-level drivers
+ know about it. */
+ parport_announce_port (p);
+
+ return p;
+}
+
+
+#define PARPORT_GSC_OFFSET 0x800
+
+static int __initdata parport_count;
+
+static int __init
+parport_init_chip(struct hp_device *d, struct pa_iodc_driver *dri)
+{
+ unsigned long port;
+ int irq;
+
+ irq = busdevice_alloc_irq(d);
+
+ if (!irq) {
+ printk("IRQ not found for parallel device at 0x%p\n", d->hpa);
+ return -ENODEV;
+ }
+
+ port = ((unsigned long) d->hpa) + PARPORT_GSC_OFFSET;
+
+ /*
+ some older machines with ASP-chip don't support the enhanced parport modes
+ */
+ if (!pdc_add_valid( (void *)(port+4))) {
+ /* Initialize bidirectional-mode (0x10) & data-tranfer-mode #1 (0x20) */
+ printk("%s: initialize bidirectional-mode.\n", __FUNCTION__);
+ parport_writeb ( (0x10 + 0x20), port + 4);
+ } else {
+ printk("%s: enhanced parport-modes not supported.\n", __FUNCTION__);
+ }
+
+ if (parport_gsc_probe_port(port, 0,
+ irq, /* PARPORT_IRQ_NONE */
+ PARPORT_DMA_NONE, NULL))
+ parport_count++;
+
+ return 0;
+}
+
+static struct pa_iodc_driver parport_drivers_for[] __initdata = {
+ {HPHW_FIO, 0x0, 0x0, 0x74, 0x0, 0, /* 715/64 */
+ DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE,
+ "parallel device", "HP 7xx - Series", (void *) parport_init_chip},
+ { 0 }
+};
+
+int __init
+parport_gsc_init ( void )
+{
+ parport_count = 0;
+
+ register_driver(parport_drivers_for);
+
+ return parport_count;
+}
+
+/* Exported symbols. */
+EXPORT_NO_SYMBOLS;
+
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Helge Deller <deller@gmx.de>");
+MODULE_DESCRIPTION("HP-PARISC PC-style parallel port driver");
+MODULE_SUPPORTED_DEVICE("integrated PC-style parallel port");
+
+int init_module(void)
+{
+ return !parport_gsc_init ();
+}
+
+void cleanup_module(void)
+{
+ struct parport *p = parport_enumerate(), *tmp;
+ while (p) {
+ tmp = p->next;
+ if (p->modes & PARPORT_MODE_PCSPP) {
+ struct parport_gsc_private *priv = p->private_data;
+ struct parport_operations *ops = p->ops;
+ if (p->dma != PARPORT_DMA_NONE)
+ free_dma(p->dma);
+ if (p->irq != PARPORT_IRQ_NONE)
+ free_irq(p->irq, p);
+ release_region(p->base, 3);
+ if (p->size > 3)
+ release_region(p->base + 3, p->size - 3);
+ if (p->modes & PARPORT_MODE_ECP)
+ release_region(p->base_hi, 3);
+ parport_proc_unregister(p);
+ if (priv->dma_buf)
+ pci_free_consistent(priv->dev, PAGE_SIZE,
+ priv->dma_buf,
+ priv->dma_handle);
+ kfree (p->private_data);
+ parport_unregister_port(p);
+ kfree (ops); /* hope no-one cached it */
+ }
+ p = tmp;
+ }
+}
+#endif
/* Switch to forward mode if necessary. */
if (port->physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
/* Event 47: Set nInit high. */
- parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+ parport_frob_control (port,
+ PARPORT_CONTROL_INIT
+ | PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_INIT
+ | PARPORT_CONTROL_AUTOFD);
- /* Event 40: PError goes high. */
+ /* Event 49: PError goes high. */
r = parport_wait_peripheral (port,
PARPORT_STATUS_PAPEROUT,
PARPORT_STATUS_PAPEROUT);
pci_write_config_dword(dev, reg+4, ~0);
pci_read_config_dword(dev, reg+4, &sz);
pci_write_config_dword(dev, reg+4, l);
- if (sz)
- res->end = res->start + (((unsigned long) ~sz) << 32);
+ if (~sz)
+ res->end = res->start + 0xffffffff +
+ (((unsigned long) ~sz) << 32);
#else
if (l) {
printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name);
}
}
-void __init
+void __init
pci_assign_unassigned_resources(void)
{
struct pbus_set_ranges_data ranges;
for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) {
struct pci_bus *b = pci_bus_b(ln);
- ranges.io_start = b->resource[0]->start;
- ranges.mem_start = b->resource[1]->start;
+ ranges.io_start = b->resource[0]->start + PCIBIOS_MIN_IO;
+ ranges.mem_start = b->resource[1]->start + PCIBIOS_MIN_MEM;
ranges.io_end = ranges.io_start;
ranges.mem_end = ranges.mem_start;
ranges.found_vga = 0;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r;
struct resource_list *list, *tmp;
+ unsigned long r_size;
/* PCI-PCI bridges may have I/O ports or
memory on the primary bus */
continue;
r = &dev->resource[i];
+ r_size = r->end - r->start;
+
if (!(r->flags & type_mask) || r->parent)
continue;
+ if (!r_size) {
+ printk(KERN_WARNING "PCI: Ignore bogus resource %d "
+ "[%lx:%lx] of %s\n",
+ i, r->start, r->end, dev->name);
+ continue;
+ }
for (list = head; ; list = list->next) {
unsigned long size = 0;
struct resource_list *ln = list->next;
if (ln)
size = ln->res->end - ln->res->start;
- if (r->end - r->start > size) {
+ if (r_size > size) {
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
tmp->next = ln;
tmp->res = r;
(*res)->mem32 = mem32;
}
+/*
+ * Parse card name for ISA PnP device.
+ */
+
+static void __init
+isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size)
+{
+ if (name[0] == '\0') {
+ unsigned short size1 = *size >= name_max ? (name_max - 1) : *size;
+ isapnp_peek(name, size1);
+ name[size1] = '\0';
+ *size -= size1;
+
+ /* clean whitespace from end of string */
+ while (size1 > 0 && name[--size1] == ' ')
+ name[size1] = '\0';
+ }
+}
+
/*
* Parse resource map for logical device.
*/
size = 0;
break;
case _LTAG_ANSISTR:
- if (dev->name[0] == '\0') {
- unsigned short size1 = size > 47 ? 47 : size;
- isapnp_peek(dev->name, size1);
- dev->name[size1] = '\0';
- size -= size1;
- }
+ isapnp_parse_name(dev->name, sizeof(dev->name), &size);
break;
case _LTAG_UNICODESTR:
/* silently ignore */
case _STAG_VENDOR:
break;
case _LTAG_ANSISTR:
- if (card->name[0] == '\0') {
- unsigned short size1 = size > 47 ? 47 : size;
- isapnp_peek(card->name, size1);
- card->name[size1] = '\0';
- size -= size1;
- }
+ isapnp_parse_name(card->name, sizeof(card->name), &size);
break;
case _LTAG_UNICODESTR:
/* silently ignore */
return -ENOMEM;
}
init_waitqueue_head(&privptr->channel[i].wait);
- privptr->channel[i].tq.next = NULL;
+ INIT_LIST_HEAD(&privptr->channel[i].tq.list);
privptr->channel[i].tq.sync = 0;
privptr->channel[i].tq.routine = (void *)(void *)ctc_irq_bh;
privptr->channel[i].tq.data = &privptr->channel[i];
dummy_chip->perchip_info.play.active = 1;
/* fake an "interrupt" to deal with this block */
- dummy_chip->tqueue.next = NULL;
+ INIT_LIST_HEAD(&dummy_chip->tqueue.list);
dummy_chip->tqueue.sync = 0;
dummy_chip->tqueue.routine = dummy_output_done_task;
dummy_chip->tqueue.data = drv;
-/* $Id: aurora.c,v 1.9 2000/11/08 05:33:03 davem Exp $
+/* $Id: aurora.c,v 1.10 2000/12/07 04:35:38 anton Exp $
* linux/drivers/sbus/char/aurora.c -- Aurora multiport driver
*
* Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro)
-/* $Id: sab82532.c,v 1.53 2000/11/15 07:28:09 davem Exp $
+/* $Id: sab82532.c,v 1.54 2000/12/07 04:35:39 anton Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
static inline void __init show_serial_version(void)
{
- char *revision = "$Revision: 1.53 $";
+ char *revision = "$Revision: 1.54 $";
char *version, *p;
version = strchr(revision, ' ');
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
-obj-$(CONFIG_SCSI) += scsi_mod.o scsi_syms.o
+obj-$(CONFIG_SCSI) += scsi_mod.o
obj-$(CONFIG_A4000T_SCSI) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A4091_SCSI) += amiga7xx.o 53c7xx.o
scsicam.o scsi_proc.o scsi_error.o \
scsi_obsolete.o scsi_queue.o scsi_lib.o \
scsi_merge.o scsi_dma.o scsi_scan.o \
+ scsi_syms.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
initio-objs := ini9100u.o i91uscsi.o
mode: IMM_AUTODETECT, \
host: -1, \
cur_cmd: NULL, \
- imm_tq: {0, 0, imm_interrupt, NULL}, \
+ imm_tq: { routine: imm_interrupt }, \
jstart: 0, \
failed: 0, \
dp: 0, \
struct Scsi_Host *hreg;
int ports;
int i, nhosts, try_again;
- struct parport *pb = parport_enumerate();
+ struct parport *pb;
+
+ /*
+ * unlock to allow the lowlevel parport driver to probe
+ * the irqs
+ */
+ spin_unlock_irq(&io_request_lock);
+ pb = parport_enumerate();
printk("imm: Version %s\n", IMM_VERSION);
nhosts = 0;
if (!pb) {
printk("imm: parport reports no devices.\n");
+ spin_lock_irq(&io_request_lock);
return 0;
}
retry_entry:
printk(KERN_ERR "imm%d: failed to claim parport because a "
"pardevice is owning the port for too longtime!\n",
i);
+ spin_lock_irq(&io_request_lock);
return 0;
}
}
nhosts++;
}
if (nhosts == 0) {
- if (try_again == 1)
+ if (try_again == 1) {
+ spin_lock_irq(&io_request_lock);
return 0;
+ }
try_again = 1;
goto retry_entry;
- } else
+ } else {
+ spin_lock_irq (&io_request_lock);
return 1; /* return number of hosts detected */
+ }
}
/* This is to give the imm driver a way to modify the timings (and other
#ifndef _IMM_H
#define _IMM_H
-#define IMM_VERSION "2.04 (for Linux 2.4.0)"
+#define IMM_VERSION "2.05 (for Linux 2.4.0)"
/*
* 10 Apr 1998 (Good Friday) - Received EN144302 by email from Iomega.
* added CONFIG_SCSI_IZIP_SLOW_CTR option
* [2.03]
* Fix kernel panic on scsi timeout. 20Aug00 [2.04]
+ *
+ * Avoid io_request_lock problems.
+ * John Cavan <johncavan@home.com> 16Nov00 [2.05]
*/
/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
char *ioctl_data; /* IOCTL data area */
u32 ioctl_datasize; /* IOCTL data size */
u32 cmd_in_progress; /* Current command in progress*/
- u32 flags; /* HA flags */
+ long flags; /* HA flags */
u8 waitflag; /* are we waiting for cmd */
u8 active;
u16 reset_count; /* number of resets */
mode: PPA_AUTODETECT, \
host: -1, \
cur_cmd: NULL, \
- ppa_tq: {0, 0, ppa_interrupt, NULL}, \
+ ppa_tq: { routine: ppa_interrupt }, \
jstart: 0, \
failed: 0, \
p_busy: 0 \
struct Scsi_Host *hreg;
int ports;
int i, nhosts, try_again;
- struct parport *pb = parport_enumerate();
+ struct parport *pb;
+
+ /*
+ * unlock to allow the lowlevel parport driver to probe
+ * the irqs
+ */
+ spin_unlock_irq(&io_request_lock);
+ pb = parport_enumerate();
printk("ppa: Version %s\n", PPA_VERSION);
nhosts = 0;
if (!pb) {
printk("ppa: parport reports no devices.\n");
+ spin_lock_irq(&io_request_lock);
return 0;
}
retry_entry:
printk(KERN_ERR "ppa%d: failed to claim parport because a "
"pardevice is owning the port for too longtime!\n",
i);
+ spin_lock_irq(&io_request_lock);
return 0;
}
}
printk(" cable is marked with \"AutoDetect\", this is what has\n");
printk(" happened.\n");
return 0;
+ spin_lock_irq(&io_request_lock);
}
try_again = 1;
goto retry_entry;
- } else
+ } else {
+ spin_lock_irq(&io_request_lock);
return 1; /* return number of hosts detected */
+ }
}
/* This is to give the ppa driver a way to modify the timings (and other
#ifndef _PPA_H
#define _PPA_H
-#define PPA_VERSION "2.05 (for Linux 2.2.x)"
+#define PPA_VERSION "2.06 (for Linux 2.2.x)"
/*
* this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
* Tim Waugh <twaugh@redhat.com>
* [2.04]
* Fix kernel panic on scsi timeout, 2000-08-18 [2.05]
+ *
+ * Avoid io_request_lock problems.
+ * John Cavan <johncavan@home.com> [2.06]
*/
/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
* ? merge ymf_pcm and state
* ? pcm interrupt no pointer
* ? underused structure members
+ * - Remove remaining P3 tags (debug messages).
+ * - Resolve XXX tagged questions.
+ * - Cannot play 5133Hz.
*/
#include <linux/module.h>
int pair, ymfpci_voice_t **rvoice);
static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice);
static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state);
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance);
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt);
static LIST_HEAD(ymf_devs);
char silence;
if ((ypcm = voice->ypcm) == NULL) {
-/* P3 */ printk("ymf_pcm_interrupt: voice %d: no ypcm\n", voice->number);
return;
}
if ((state = ypcm->state) == NULL) {
-/* P3 */ printk("ymf_pcm_interrupt: voice %d: no state\n", voice->number);
ypcm->running = 0; // lock it
return;
}
if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */
printk(KERN_ERR
"ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",
- codec->inst, voice->number,
+ codec->dev_audio, voice->number,
dmabuf->hwptr, dmabuf->dmasize);
pos = 0;
}
if (dmabuf->count == 0) {
printk("ymfpci%d: %d: strain: hwptr %d\n",
- codec->inst, voice->number, dmabuf->hwptr);
+ codec->dev_audio, voice->number, dmabuf->hwptr);
ymf_playback_trigger(codec, ypcm, 0);
}
*/
printk("ymfpci%d: %d: lost: delta %d"
" hwptr %d swptr %d distance %d count %d\n",
- codec->inst, voice->number, delta,
+ codec->dev_audio, voice->number, delta,
dmabuf->hwptr, swptr, distance, dmabuf->count);
} else {
/*
*/
// printk("ymfpci%d: %d: done: delta %d"
// " hwptr %d swptr %d distance %d count %d\n",
-// codec->inst, voice->number, delta,
+// codec->dev_audio, voice->number, delta,
// dmabuf->hwptr, swptr, distance, dmabuf->count);
}
played = dmabuf->count;
{
if (ypcm->voices[0] == NULL) {
-/* P3 */ printk("ymfpci: trigger %d no voice\n", cmd);
return -EINVAL;
}
if (cmd != 0) {
if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {
/* Cannot be unless we leak voices in ymf_release! */
printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",
- codec->inst);
+ codec->dev_audio);
return err;
}
}
}
-static int ymf_state_alloc(ymfpci_t *unit, int nvirt, int instance)
+static int ymf_state_alloc(ymfpci_t *unit, int nvirt)
{
ymfpci_pcm_t *ypcm;
struct ymf_state *state;
}
memset(state, 0, sizeof(struct ymf_state));
- init_waitqueue_head(&state->open_wait);
init_waitqueue_head(&state->dmabuf.wait);
ypcm = &state->ypcm;
return put_user(SOUND_VERSION, (int *)arg);
case SNDCTL_DSP_RESET:
- /* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
case SNDCTL_DSP_SPEED: /* set smaple rate */
if (get_user(val, (int *)arg))
return -EFAULT;
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SPEED %d\n", val); */
if (val >= 8000 && val <= 48000) {
- spin_lock_irqsave(&state->unit->reg_lock, flags);
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
}
stop_adc(state);
}
#endif
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
state->format.rate = val;
ymf_pcm_update_shift(&state->format);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, (int *)arg))
return -EFAULT;
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_STEREO %d\n", val); */
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
spin_lock_irqsave(&state->unit->reg_lock, flags);
return 0;
case SNDCTL_DSP_GETBLKSIZE:
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETBLKSIZE\n"); */
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
return val;
return -EINVAL;
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETFMTS\n"); */
return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
case SNDCTL_DSP_SETFMT: /* Select sample format */
if (get_user(val, (int *)arg))
return -EFAULT;
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETFMT 0x%x\n", val); */
if (val == AFMT_S16_LE || val == AFMT_U8) {
- spin_lock_irqsave(&state->unit->reg_lock, flags);
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
}
stop_adc(state);
}
#endif
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
state->format.format = val;
ymf_pcm_update_shift(&state->format);
return -EFAULT;
/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */
if (val != 0) {
- spin_lock_irqsave(&state->unit->reg_lock, flags);
if (file->f_mode & FMODE_WRITE) {
ymf_wait_dac(state);
if (val == 1 || val == 2) {
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
dmabuf->ready = 0;
state->format.voices = val;
ymf_pcm_update_shift(&state->format);
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
}
#if HAVE_RECORD
if (file->f_mode & FMODE_READ) {
+ spin_lock_irqsave(&state->unit->reg_lock, flags);
stop_adc(state);
dmabuf->ready = 0;
+ spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
#endif
- spin_unlock_irqrestore(&state->unit->reg_lock, flags);
}
return put_user(state->format.voices, (int *)arg);
return 0;
case SNDCTL_DSP_GETOSPACE:
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOSPACE\n"); */
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
#endif
case SNDCTL_DSP_NONBLOCK:
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_NONBLOCK\n"); */
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETCAPS\n"); */
/* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
(int *)arg); */
return put_user(0, (int *)arg);
#endif
case SNDCTL_DSP_GETOPTR:
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_GETOPTR\n"); */
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->unit->reg_lock, flags);
return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
case SNDCTL_DSP_SETDUPLEX: /* XXX TODO */
- /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_SETDUPLEX\n"); */
return -EINVAL;
#if 0 /* old */
return -ENOTTY;
default:
- /* P3 */ printk(KERN_WARNING "ymfpci: unknown ioctl cmd 0x%x\n", cmd);
/*
* Some programs mix up audio devices and ioctls
* or perhaps they expect "universal" ioctls,
{
struct list_head *list;
ymfpci_t *unit;
- int minor, instance;
+ int minor;
struct ymf_state *state;
int nvirt;
int err;
} else {
return -ENXIO;
}
- instance = (minor >> 4) & 0x0F;
nvirt = 0; /* Such is the partitioning of minor */
- /* XXX Semaphore here! */
for (list = ymf_devs.next; list != &ymf_devs; list = list->next) {
unit = list_entry(list, ymfpci_t, ymf_devs);
- if (unit->inst == instance)
+ if (((unit->dev_audio ^ minor) & ~0x0F) == 0)
break;
}
if (list == &ymf_devs)
return -ENODEV;
+ down(&unit->open_sem);
if (unit->states[nvirt] != NULL) {
- /* P3 */ printk("ymfpci%d: busy\n", unit->inst);
+ up(&unit->open_sem);
return -EBUSY;
}
- if ((err = ymf_state_alloc(unit, nvirt, instance)) != 0) {
+ if ((err = ymf_state_alloc(unit, nvirt)) != 0) {
+ up(&unit->open_sem);
return err;
}
state = unit->states[nvirt];
unit->states[state->virt] = NULL;
kfree(state);
+ up(&unit->open_sem);
return err;
}
ymfpci_writeb(codec, YDSXGR_TIMERCTRL,
(YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));
#endif
+ up(&unit->open_sem);
+ /* XXX Is it correct to have MOD_INC_USE_COUNT outside of sem.? */
MOD_INC_USE_COUNT;
return 0;
ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);
#endif
- /* XXX Use the semaphore to unrace us with opens */
-
if (state != codec->states[state->virt]) {
printk(KERN_ERR "ymfpci%d.%d: state mismatch\n",
- state->unit->inst, state->virt);
+ state->unit->dev_audio, state->virt);
return -EIO;
}
+ down(&codec->open_sem);
+
/*
* XXX Solve the case of O_NONBLOCK close - don't deallocate here.
* Deallocate when unloading the driver and we can wait.
codec->states[state->virt] = NULL;
kfree(state);
+ up(&codec->open_sem);
+
MOD_DEC_USE_COUNT;
return 0;
}
codec->codec_write = ymfpci_codec_write;
if (ac97_probe_codec(codec) == 0) {
- /* Alan does not have this printout. P3 */
printk("ymfpci: ac97_probe_codec failed\n");
goto out_kfree;
}
static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent)
{
u16 ctrl;
- static int ymf_instance; /* = 0 */
ymfpci_t *codec;
int err;
spin_lock_init(&codec->reg_lock);
spin_lock_init(&codec->voice_lock);
+ init_MUTEX(&codec->open_sem);
codec->pci = pcidev;
- codec->inst = ymf_instance;
- pci_read_config_byte(pcidev, PCI_REVISION_ID, (u8 *)&codec->rev);
+ pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
codec->reg_area_virt = ioremap(pci_resource_start(pcidev, 0), 0x8000);
- printk(KERN_INFO "ymfpci%d: %s at 0x%lx IRQ %d\n", ymf_instance,
+ printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
(char *)ent->driver_data, pci_resource_start(pcidev, 0), pcidev->irq);
ymfpci_aclink_reset(pcidev);
if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n",
- codec->inst, pcidev->irq);
+ codec->dev_audio, pcidev->irq);
goto out_memfree;
}
/* register /dev/dsp */
if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
- printk(KERN_ERR "ymfpci%d: unable to register dsp\n", codec->inst);
+ printk(KERN_ERR "ymfpci%d: unable to register dsp\n",
+ codec->dev_audio);
goto out_free_irq;
}
/* put it into driver list */
list_add_tail(&codec->ymf_devs, &ymf_devs);
pci_set_drvdata(pcidev, codec);
- ymf_instance++;
return 0;
};
struct ymf_unit {
- unsigned int rev; /* PCI revision */
+ u8 rev; /* PCI revision */
void *reg_area_virt;
void *work_ptr; // +
u16 ac97_features;
struct pci_dev *pci;
- int inst; /* Unit number (instance) */
spinlock_t reg_lock;
spinlock_t voice_lock;
/* soundcore stuff */
int dev_audio;
+ struct semaphore open_sem;
struct list_head ymf_devs;
struct ymf_state *states[1]; // *
struct ymf_state {
struct ymf_unit *unit; /* backpointer */
- /* single open lock mechanism, only used for recording */
- struct semaphore open_sem;
- wait_queue_head_t open_wait;
-
/* virtual channel number */
int virt; // * unused a.t.m.
init_waitqueue_head( &priv->dp_flush_wait );
priv->dp_in_close = 0;
init_waitqueue_head( &priv->dp_close_wait );
- priv->dp_wakeup_task.next = NULL;
+ INIT_LIST_HEAD(&priv->dp_wakeup_task.list);
priv->dp_wakeup_task.sync = 0;
priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock;
priv->dp_wakeup_task.data = (void *)(&serial->port[i]);
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
#define __LINUX_USB_SERIAL_KEYSPAN_H
+#include <linux/config.h>
/* Function prototypes for Keyspan serial converter */
static int keyspan_open (struct usb_serial_port *port,
if (!priv)
return (1); /* error */
init_waitqueue_head(&serial->port[0].write_wait);
- priv->wakeup_task.next = NULL;
+ INIT_LIST_HEAD(&priv->wakeup_task.list);
priv->wakeup_task.sync = 0;
priv->wakeup_task.routine = (void *)keyspan_pda_wakeup_write;
priv->wakeup_task.data = (void *)(&serial->port[0]);
- priv->unthrottle_task.next = NULL;
+ INIT_LIST_HEAD(&priv->unthrottle_task.list);
priv->unthrottle_task.sync = 0;
priv->unthrottle_task.routine = (void *)keyspan_pda_request_unthrottle;
priv->unthrottle_task.data = (void *)(serial);
static struct vc_data *mda_display_fg = NULL;
-#ifdef MODULE_PARM
MODULE_PARM(mda_first_vc, "1-255i");
MODULE_PARM(mda_last_vc, "1-255i");
-#endif
/* MDA register values
*/
}
#endif
-#ifdef MODULE
-static int mda_detect(void)
-#else
static int __init mda_detect(void)
-#endif
{
int count=0;
u16 *p, p_save;
return 1;
}
-#ifdef MODULE
-static void mda_initialize(void)
-#else
static void __init mda_initialize(void)
-#endif
{
write_mda_b(97, 0x00); /* horizontal total */
write_mda_b(80, 0x01); /* horizontal displayed */
outb_p(0x00, mda_gfx_port);
}
-#ifdef MODULE
-static const char *mdacon_startup(void)
-#else
static const char __init *mdacon_startup(void)
-#endif
{
mda_num_columns = 80;
mda_num_lines = 25;
con_invert_region: mdacon_invert_region,
};
-#ifdef MODULE
-void mda_console_init(void)
-#else
void __init mda_console_init(void)
-#endif
{
if (mda_first_vc > mda_last_vc)
return;
int pageind;
int bhind;
int offset;
- int sectors = size>>9;
unsigned long blocknr;
struct kiobuf * iobuf = NULL;
struct page * map;
tmp->b_this_page = tmp;
init_buffer(tmp, end_buffer_io_kiobuf, iobuf);
- tmp->b_rdev = tmp->b_dev = dev;
+ tmp->b_dev = dev;
tmp->b_blocknr = blocknr;
- tmp->b_rsector = blocknr*sectors;
tmp->b_state = (1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req);
if (rw == WRITE) {
atomic_inc(&iobuf->io_count);
- generic_make_request(rw, tmp);
+ submit_bh(rw, tmp);
/*
* Wait for IO if we have got too much
*/
dep_mbool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS $CONFIG_NCP_FS
dep_mbool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS $CONFIG_NCP_FS
dep_mbool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS $CONFIG_NCP_FS
-dep_mbool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR $CONFIG_NCP_FS
-dep_mbool ' NDS authentication support' CONFIG_NCPFS_NDS_DOMAINS $CONFIG_NCP_FS
dep_mbool ' Use Native Language Support' CONFIG_NCPFS_NLS $CONFIG_NCP_FS
dep_mbool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS $CONFIG_NCP_FS
return 0;
}
-#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
case NCP_IOC_GETROOT:
{
struct ncp_setroot_ioctl sr;
return 0;
}
-#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
#ifdef CONFIG_NCPFS_PACKET_SIGNING
case NCP_IOC_SIGN_INIT:
}
#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
-#ifdef CONFIG_NCPFS_NDS_DOMAINS
case NCP_IOC_GETOBJECTNAME:
if (current->uid != server->m.mounted_uid) {
return -EACCES;
if (old) ncp_kfree_s(old, oldlen);
return 0;
}
-#endif /* CONFIG_NCPFS_NDS_DOMAINS */
#ifdef CONFIG_NCPFS_NLS
/* Here we are select the iocharset and the codepage for NLS.
__u32 offset, __u32 length);
#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
-#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
int
ncp_mount_subdir(struct ncp_server *, struct nw_info_struct *,
__u8, __u8, __u32);
-#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
#ifdef CONFIG_NCPFS_NLS
-inline unsigned char ncp__tolower(struct nls_table *, unsigned char);
-inline unsigned char ncp__toupper(struct nls_table *, unsigned char);
+unsigned char ncp__tolower(struct nls_table *, unsigned char);
+unsigned char ncp__toupper(struct nls_table *, unsigned char);
int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *,
const unsigned char *, unsigned int, int);
int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *,
nfs_refresh_inode(inode, fattr);
}
+struct nfs_find_desc {
+ struct nfs_fh *fh;
+ struct nfs_fattr *fattr;
+};
+
/*
* In NFSv3 we can have 64bit inode numbers. In order to support
* this, and re-exported directories (also seen in NFSv2)
static int
nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
{
- struct nfs_fattr *fattr = (struct nfs_fattr *)opaque;
+ struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
+ struct nfs_fh *fh = desc->fh;
+ struct nfs_fattr *fattr = desc->fattr;
if (NFS_FSID(inode) != fattr->fsid)
return 0;
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
-
+ if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
+ return 0;
return 1;
}
* the vfs read_inode function because there is no way to pass the
* file handle or current attributes into the read_inode function.
*
- * We provide a special check for NetApp .snapshot directories to avoid
- * inode aliasing problems. All snapshot inodes are anonymous (unhashed).
*/
struct inode *
nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
dprintk("NFS: nfs_fhget(%s/%s fileid=%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
(long long)fattr->fileid);
-
-#ifdef CONFIG_NFS_SNAPSHOT
- /*
- * Check for NetApp snapshot dentries, and get an
- * unhashed inode to avoid aliasing problems.
- */
- if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
- (dentry->d_name.len == 9 &&
- memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
- struct inode *inode = new_inode(sb);
- if (!inode)
- goto out;
- inode->i_ino = nfs_fattr_to_ino_t(fattr);
- nfs_read_inode(inode);
- nfs_fill_inode(inode, fhandle, fattr);
- inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
- dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
- out:
- return inode;
- }
-#endif
return __nfs_fhget(sb, fhandle, fattr);
}
/*
* Look up the inode by super block and fattr->fileid.
- *
- * Note carefully the special handling of busy inodes (i_count > 1).
- * With the kernel 2.1.xx dcache all inodes except hard links must
- * have i_count == 1 after iget(). Otherwise, it indicates that the
- * server has reused a fileid (i_ino) and we have a stale inode.
*/
static struct inode *
__nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
{
+ struct nfs_find_desc desc = { fh, fattr };
struct inode *inode = NULL;
unsigned long ino;
ino = nfs_fattr_to_ino_t(fattr);
- while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
-
- /*
- * Check for busy inodes, and attempt to get rid of any
- * unused local references. If successful, we release the
- * inode and try again.
- *
- * Note that the busy test uses the values in the fattr,
- * as the inode may have become a different object.
- * (We can probably handle modes changes here, too.)
- */
- if (!nfs_inode_is_stale(inode, fh, fattr))
- break;
-
- dprintk("__nfs_fhget: inode (%x/%Ld) still busy, i_count=%d\n",
- inode->i_dev, (long long)NFS_FILEID(inode),
- atomic_read(&inode->i_count));
- nfs_zap_caches(inode);
- remove_inode_hash(inode);
- iput(inode);
- }
-
- if (!inode)
+ if (!(inode = iget4(sb, ino, nfs_find_actor, &desc)))
goto out_no_inode;
nfs_fill_inode(inode, fh, fattr);
lock_kernel();
result = NFS_PROTO(inode)->read(inode, cred, &fattr, flags,
offset, rsize, buffer, &eof);
- unlock_kernel();
nfs_refresh_inode(inode, &fattr);
+ unlock_kernel();
/*
* Even if we had a partial success we can't mark the page
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
-/* 4K page size but our output routines use some slack for overruns */
+/* buffer size is one page but our output routines use some slack for overruns */
#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024)
static ssize_t
found_data(sk);
return;
}
- job->cb.next = NULL;
+ INIT_LIST_HEAD(&job->cb.list);
job->cb.sync = 0;
job->cb.routine = smb_data_callback;
job->cb.data = job;
return pte;
}
-static inline void ptep_clear_wrprotect(pte_t *ptep)
+static inline void ptep_set_wrprotect(pte_t *ptep)
{
pte_t old_pte = *ptep;
set_pte(ptep, pte_wrprotect(old_pte));
#ifdef CONFIG_X86_LOCAL_APIC
#if APIC_DEBUG
-#define Dprintk(x...) printk(##x)
+#define Dprintk(x...) printk(x)
#else
#define Dprintk(x...)
#endif
: "=a" (low), "=d" (high) \
: "c" (counter))
+/* symbolic names for some interesting MSRs */
+#define MSR_IA32_PLATFORM_ID 0x17
+#define MSR_IA32_UCODE_WRITE 0x79
+#define MSR_IA32_UCODE_REV 0x8B
static inline int ptep_test_and_clear_dirty(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_DIRTY, ptep); }
static inline int ptep_test_and_clear_young(pte_t *ptep) { return test_and_clear_bit(_PAGE_BIT_ACCESSED, ptep); }
-static inline void ptep_clear_wrprotect(pte_t *ptep) { clear_bit(_PAGE_BIT_RW, ptep); }
+static inline void ptep_set_wrprotect(pte_t *ptep) { clear_bit(_PAGE_BIT_RW, ptep); }
static inline void ptep_mkdirty(pte_t *ptep) { set_bit(_PAGE_BIT_RW, ptep); }
/*
unsigned int bits[500];
};
-#define MICROCODE_IOCFREE _IO('6',0) /* because it is for P6 */
+/* '6' because it used to be for P6 only (but now covers Pentium 4 as well) */
+#define MICROCODE_IOCFREE _IO('6',0)
/* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
extern inline void rep_nop(void)
-/* $Id: hdreg.h,v 1.1 2000/01/21 04:56:27 zaitcev Exp $
+/* $Id: hdreg.h,v 1.2 2000/12/05 00:56:36 anton Exp $
* hdreg.h: SPARC PCI specific IDE glue.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
#ifndef __SPARC_HDREG_H
#define __SPARC_HDREG_H
-typedef unsigned int ide_ioreg_t;
+typedef unsigned long ide_ioreg_t;
#endif /* __SPARC_HDREG_H */
-/* $Id: processor.h,v 1.77 2000/01/21 11:39:17 jj Exp $
+/* $Id: processor.h,v 1.78 2000/11/30 08:37:31 anton Exp $
* include/asm-sparc/processor.h
*
* Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
#define SPARC_FLAG_KTHREAD 0x1 /* task is a kernel thread */
#define SPARC_FLAG_UNALIGNED 0x2 /* is allowed to do unaligned accesses */
+#define SPARC_FLAG_MMAPSHARED 0x4 /* task wants a shared mmap */
#define INIT_MMAP { &init_mm, (0), (0), \
NULL, __pgprot(0x0) , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
-/* $Id: pgalloc.h,v 1.13 2000/11/06 06:59:04 davem Exp $ */
+/* $Id: pgalloc.h,v 1.14 2000/12/09 04:15:24 anton Exp $ */
#ifndef _SPARC64_PGALLOC_H
#define _SPARC64_PGALLOC_H
#define flush_cache_page(vma, page) \
flush_cache_mm((vma)->vm_mm)
-/* These operations are unnecessary on the SpitFire since D-CACHE is write-through. */
-#define flush_icache_range(start, end) do { } while (0)
+/* This is unnecessary on the SpitFire since D-CACHE is write-through. */
#define flush_page_to_ram(page) do { } while (0)
+/*
+ * icache doesnt snoop local stores and we don't use block commit stores
+ * (which invalidate icache lines) during module load, so we need this.
+ */
+extern void flush_icache_range(unsigned long start, unsigned long end);
+
extern void __flush_dcache_page(void *addr, int flush_icache);
#define flush_dcache_page(page) \
do { if ((page)->mapping && !(page)->mapping->i_mmap && !(page)->mapping->i_mmap_shared) \
-/* $Id: processor.h,v 1.65 2000/08/09 00:00:17 davem Exp $
+/* $Id: processor.h,v 1.66 2000/11/29 05:56:12 anton Exp $
* include/asm-sparc64/processor.h
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
#define SPARC_FLAG_32BIT 0x04 /* task is older 32-bit binary */
#define SPARC_FLAG_NEWCHILD 0x08 /* task is just-spawned child process */
#define SPARC_FLAG_PERFCTR 0x10 /* task has performance counters active */
+#define SPARC_FLAG_MMAPSHARED 0x20 /* task wants a shared mmap */
#define FAULT_CODE_WRITE 0x01 /* Write access, implies D-TLB */
#define FAULT_CODE_DTLB 0x02 /* Miss happened in D-TLB */
#define ATM_ATMOPT_CLP 1 /* set CLP bit */
-typedef struct { unsigned int bits; } atm_vcc_flags_t;
+typedef struct { unsigned long bits; } atm_vcc_flags_t;
struct atm_vcc {
* Keith Owens <kaos@ocs.com.au> 28 Oct 2000.
*/
+#ifdef __KERNEL__
#define HAVE_INTER_MODULE
extern void inter_module_register(const char *, struct module *, const void *);
extern void inter_module_unregister(const char *);
};
extern int try_inc_mod_count(struct module *mod);
+#endif /* __KERNEL__ */
#if defined(MODULE) && !defined(__GENKSYMS__)
/* Linux driver for Disk-On-Chip 2000 */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2000.h,v 1.8 2000/07/10 15:46:29 dwmw2 Exp $ */
+/* $Id: doc2000.h,v 1.12 2000/11/03 12:43:43 dwmw2 Exp $ */
#ifndef __MTD_DOC2000_H__
#define __MTD_DOC2000_H__
* Others use readb/writeb
*/
#if defined(__arm__)
-#define ReadDOC(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+(DoC_##reg<<2))))
-#define WriteDOC(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+(DoC_##reg<<2)) = (__u32)d} while(0)
+#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+(reg<<2))))
+#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+(reg<<2)) = (__u32)d} while(0)
#elif defined(__ppc__)
-#define ReadDOC(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+(DoC_##reg<<1))))
-#define WriteDOC(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+(DoC_##reg<<1)) = (__u16)d} while(0)
+#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+(reg<<1))))
+#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+(reg<<1)) = (__u16)d} while(0)
#else
-#define ReadDOC(adr, reg) readb(((unsigned long)adr) + DoC_##reg)
-#define WriteDOC(d, adr, reg) writeb(d, ((unsigned long)adr) + DoC_##reg)
+#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + reg)
+#define WriteDOC_(d, adr, reg) writeb(d, ((unsigned long)adr) + reg)
#endif
+#if defined(__i386__)
+#define USE_MEMCPY
+#endif
+
+/* These are provided to directly use the DoC_xxx defines */
+#define ReadDOC(adr, reg) ReadDOC_(adr,DoC_##reg)
+#define WriteDOC(d, adr, reg) WriteDOC_(d,adr,DoC_##reg)
+
#define DOC_MODE_RESET 0
#define DOC_MODE_NORMAL 1
#define DOC_MODE_RESERVED1 2
#define DOC_TOGGLE_BIT 0x04
#define DOC_ECC_RESV 0x02
#define DOC_ECC_IGNORE 0x01
+
/* We have to also set the reserved bit 1 for enable */
#define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
-#define DOC_ECC_DIS (DOC_ECC_IGNORE | DOC_ECC_RESV)
+#define DOC_ECC_DIS (DOC_ECC_RESV)
struct Nand {
char floor, chip;
#define MAX_FLOORS_MIL 4
#define MAX_CHIPS_MIL 1
+#define ADDR_COLUMN 1
+#define ADDR_PAGE 2
+#define ADDR_COLUMN_PAGE 3
+
struct DiskOnChip {
unsigned long physadr;
unsigned long virtadr;
unsigned long totlen;
char ChipID; /* Type of DiskOnChip */
+ int ioreg;
unsigned long mfr; /* Flash IDs - only one type of flash per device */
unsigned long id;
int chipshift;
+ char page256;
+ char pageadrlen;
+ unsigned long erasesize;
int curfloor;
int curchip;
struct mtd_info *nextdoc;
};
+int doc_decode_ecc(unsigned char sector[512], unsigned char ecc1[6]);
#endif /* __MTD_DOC2000_H__ */
/* Overhauled routines for dealing with different mmap regions of flash */
-/* $Id: map.h,v 1.5 2000/06/26 16:18:58 dwmw2 Exp $ */
+/* $Id: map.h,v 1.10 2000/12/04 13:18:33 dwmw2 Exp $ */
#ifndef __LINUX_MTD_MAP_H__
#define __LINUX_MTD_MAP_H__
void (*write16)(struct map_info *, __u16, unsigned long);
void (*write32)(struct map_info *, __u32, unsigned long);
void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
+
+ void (*set_vpp)(int);
/* We put these two here rather than a single void *map_priv,
because we want mappers to be able to have quickly-accessible
cache for the 'currently-mapped page' without the _extra_
const char *im_name;
};
+#ifdef CONFIG_MODULES
/*
* Probe for the contents of a map device and make an MTD structure
* if anything is recognised. Doesn't register it because the calling
#define do_jedec_probe(x) do_map_probe(x, "jedec_probe", "jedec_probe")
#define do_ram_probe(x) do_map_probe(x, "map_ram_probe", "map_ram")
#define do_rom_probe(x) do_map_probe(x, "map_rom_probe", "map_rom")
+#else
+ /* without module support, call probe function directly */
+extern struct mtd_info *cfi_probe(struct map_info *);
+extern struct mtd_info *jedec_probe(struct map_info *);
+extern struct mtd_info *map_ram_probe(struct map_info *);
+extern struct mtd_info *map_rom_probe(struct map_info *);
+
+#define do_cfi_probe(x) cfi_probe(x)
+#define do_jedec_probe(x) jedec_probe(x)
+#define do_ram_probe(x) map_ram_probe(x)
+#define do_rom_probe(x) map_rom_probe(x)
+#endif
/*
* Destroy an MTD device which was created for a map device.
kfree(mtd);
}
+#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(1); } while(0)
+#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(0); } while(0)
#endif /* __LINUX_MTD_MAP_H__ */
-/* $Id: mtd.h,v 1.17 2000/07/04 07:24:49 jgg Exp $ */
+/* $Id: mtd.h,v 1.26 2000/10/30 17:18:04 sjhill Exp $ */
#ifndef __MTD_MTD_H__
#define __MTD_MTD_H__
#include <linux/types.h>
#include <linux/mtd/compatmac.h>
#include <linux/module.h>
+#include <linux/uio.h>
#endif /* __KERNEL__ */
// 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_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;
#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)
#ifndef __KERNEL__
// Kernel-only stuff starts here.
char *name;
+ int index;
u_long bank_size;
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-
+
+ /* iovec-based read/write methods. We need these especially for NAND flash,
+ with its limited number of write cycles per erase.
+ NB: The 'count' parameter is the number of _vectors_, each of
+ which contains an (ofs, len) tuple.
+ */
+ int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
+ int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+
+ /* Sync */
void (*sync) (struct mtd_info *mtd);
+ /* Chip-supported device locking */
+ int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+ int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+
/* Power Management functions */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)
#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args)
+#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args)
+#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args)
+#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args)
+#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args)
#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0)
#endif /* MTDC */
-/* Debugging macros */
-
-#ifdef DEBUGLVL
-#define DEBUG(n, args...) if (DEBUGLVL>(n)) printk(KERN_DEBUG args)
-#else
+/*
+ * Debugging macro and defines
+ */
+#define MTD_DEBUG_LEVEL0 (0) /* Quiet */
+#define MTD_DEBUG_LEVEL1 (1) /* Audible */
+#define MTD_DEBUG_LEVEL2 (2) /* Loud */
+#define MTD_DEBUG_LEVEL3 (3) /* Noisy */
+
+#ifdef CONFIG_MTD_DEBUG
+#define DEBUG(n, args...) \
+ if (n <= CONFIG_MTD_DEBUG_VERBOSE) { \
+ printk(KERN_INFO args); \
+ }
+#else /* CONFIG_MTD_DEBUG */
#define DEBUG(n, args...)
-#endif
+#endif /* CONFIG_MTD_DEBUG */
#endif /* __KERNEL__ */
-
#endif /* __MTD_MTD_H__ */
+/*
+ * linux/include/linux/mtd/nand.h
+ *
+ * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+ * Steven J. Hill <sjhill@cotw.com>
+ *
+ * $Id: nand.h,v 1.8 2000/10/30 17:16:17 sjhill Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Info:
+ * Contains standard defines and IDs for NAND flash devices
+ *
+ * Changelog:
+ * 01-31-2000 DMW Created
+ * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers
+ * so it can be used by other NAND flash device
+ * drivers. I also changed the copyright since none
+ * of the original contents of this file are specific
+ * to DoC devices. David can whack me with a baseball
+ * bat later if I did something naughty.
+ * 10-11-2000 SJH Added private NAND flash structure for driver
+ * 10-24-2000 SJH Added prototype for 'nand_scan' function
+ */
+#ifndef __LINUX_MTD_NAND_H
+#define __LINUX_MTD_NAND_H
-/* Defines for NAND flash devices */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: nand.h,v 1.2 1999/08/17 22:57:08 dwmw2 Exp $ */
+#include <linux/config.h>
+#include <linux/sched.h>
-#ifndef __MTD_NAND_H__
-#define __MTD_NAND_H__
-
-#define NAND_CMD_READ0 0
-#define NAND_CMD_READ1 1
-#define NAND_CMD_PAGEPROG 0x10
-#define NAND_CMD_READOOB 0x50
-#define NAND_CMD_ERASE1 0x60
-#define NAND_CMD_STATUS 0x70
-#define NAND_CMD_SEQIN 0x80
-#define NAND_CMD_READID 0x90
-#define NAND_CMD_ERASE2 0xd0
-#define NAND_CMD_RESET 0xff
-
-#define NAND_MFR_TOSHIBA 0x98
-#define NAND_MFR_SAMSUNG 0xec
-
-
-#endif /* __MTD_NAND_H__ */
+/*
+ * Searches for a NAND device
+ */
+extern int nand_scan (struct mtd_info *mtd);
+/*
+ * Standard NAND flash commands
+ */
+#define NAND_CMD_READ0 0
+#define NAND_CMD_READ1 1
+#define NAND_CMD_PAGEPROG 0x10
+#define NAND_CMD_READOOB 0x50
+#define NAND_CMD_ERASE1 0x60
+#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_SEQIN 0x80
+#define NAND_CMD_READID 0x90
+#define NAND_CMD_ERASE2 0xd0
+#define NAND_CMD_RESET 0xff
+/*
+ * Enumeration for NAND flash chip state
+ */
+typedef enum {
+ FL_READY,
+ FL_READING,
+ FL_WRITING,
+ FL_ERASING,
+ FL_SYNCING
+} nand_state_t;
+/*
+ * NAND Private Flash Chip Data
+ *
+ * Structure overview:
+ *
+ * IO_ADDR - address to access the 8 I/O lines to the flash device
+ *
+ * CTRL_ADDR - address where ALE, CLE and CE control bits are accessed
+ *
+ * CLE - location in control word for Command Latch Enable bit
+ *
+ * ALE - location in control word for Address Latch Enable bit
+ *
+ * NCE - location in control word for nChip Enable bit
+ *
+ * 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
+ *
+ * ecc_code_buf - used only for holding calculated or read ECCs for
+ * a page read or written when ECC is in use
+ *
+ * reserved - padding to make structure fall on word boundary if
+ * when ECC is in use
+ */
+struct nand_chip {
+ unsigned long IO_ADDR;
+ unsigned long CTRL_ADDR;
+ unsigned int CLE;
+ unsigned int ALE;
+ unsigned int NCE;
+ spinlock_t chip_lock;
+ wait_queue_head_t wq;
+ nand_state_t state;
+ int page_shift;
+ u_char *data_buf;
+#ifdef CONFIG_MTD_NAND_ECC
+ u_char ecc_code_buf[6];
+ u_char reserved[2];
+#endif
+};
+/*
+ * NAND Flash Manufacturer ID Codes
+ */
+#define NAND_MFR_TOSHIBA 0x98
+#define NAND_MFR_SAMSUNG 0xec
+/*
+ * NAND Flash Device ID Structure
+ *
+ * Structure overview:
+ *
+ * name - Complete name of device
+ *
+ * manufacture_id - manufacturer ID code of device.
+ *
+ * model_id - model ID code of device.
+ *
+ * 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 {
+ char * name;
+ int manufacture_id;
+ int model_id;
+ int chipshift;
+ char page256;
+ char pageadrlen;
+ unsigned long erasesize;
+};
+#endif /* __LINUX_MTD_NAND_H */
--- /dev/null
+/*
+ * linux/include/linux/mtd/nand_ids.h
+ *
+ * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+ * Steven J. Hill <sjhill@cotw.com>
+ *
+ * $Id: nand_ids.h,v 1.1 2000/10/13 16:16:26 mdeans Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Info:
+ * Contains standard defines and IDs for NAND flash devices
+ *
+ * Changelog:
+ * 01-31-2000 DMW Created
+ * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers
+ * so it can be used by other NAND flash device
+ * drivers. I also changed the copyright since none
+ * of the original contents of this file are specific
+ * to DoC devices. David can whack me with a baseball
+ * bat later if I did something naughty.
+ * 10-11-2000 SJH Added private NAND flash structure for driver
+ * 2000-10-13 BE Moved out of 'nand.h' - avoids duplication.
+ */
+
+#ifndef __LINUX_MTD_NAND_IDS_H
+#define __LINUX_MTD_NAND_IDS_H
+
+static struct nand_flash_dev nand_flash_ids[] = {
+ {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000},
+ {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000},
+ {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000},
+ {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000},
+ {"Toshiba TH58512FT", NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000},
+ {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000},
+ {"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000},
+ {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000},
+ {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000},
+ {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000},
+ {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000},
+ {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000},
+ {"Samsung unknown 64Mb", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000},
+ {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000},
+ {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000},
+ {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000},
+ {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000},
+ {NULL,}
+};
+
+#endif /* __LINUX_MTD_NAND_IDS_H */
/* Defines for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: nftl.h,v 1.6 2000/03/31 15:12:20 dwmw2 Exp $ */
+/* $Id: nftl.h,v 1.9 2000/11/07 05:48:49 ollie Exp $ */
#ifndef __MTD_NFTL_H__
#define __MTD_NFTL_H__
+#ifndef __BOOT__
#include <linux/mtd/mtd.h>
+#endif
/* Block Control Information */
struct nftl_bci {
unsigned char ECCSig[6];
- __u16 Status;
+ __u8 Status;
+ __u8 Status1;
}__attribute__((packed));
/* Unit Control Information */
} __attribute__((packed));
struct nftl_uci2 {
- __u32 WriteInh;
+ __u16 FoldMark;
+ __u16 FoldMark1;
__u32 unused;
} __attribute__((packed));
#define MAX_ERASE_ZONES (8192 - 512)
#define ERASE_MARK 0x3c69
-#define BLOCK_FREE 0xffff
-#define BLOCK_USED 0x5555
-#define BLOCK_IGNORE 0x1111
-#define BLOCK_DELETED 0x0000
+#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
#ifdef __KERNEL__
+/* these info are used in ReplUnitTable */
+#define BLOCK_NIL 0xffff /* last block of a chain */
+#define BLOCK_FREE 0xfffe /* free block */
+#define BLOCK_NOTEXPLORED 0xfffd /* non explored block, only used during mounting */
+#define BLOCK_RESERVED 0xfffc /* bios block or bad block */
struct NFTLrecord {
struct mtd_info *mtd;
unsigned char sectors;
unsigned short cylinders;
__u16 numvunits;
- __u16 lastEUN;
+ __u16 lastEUN; /* should be suppressed */
__u16 numfreeEUNs;
- __u16 LastFreeEUN; /* To speed up finding a free EUN */
+ __u16 LastFreeEUN; /* To speed up finding a free EUN */
__u32 long nr_sects;
int head,sect,cyl;
- __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */
- __u16 *VirtualUnitTable; /* [numEUNs]: VirtualUnitNumber for each */
- __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */
+ __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */
+ __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */
+ unsigned int nb_blocks; /* number of physical blocks */
+ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
+ struct erase_info instr;
};
+int NFTL_mount(struct NFTLrecord *s);
+int NFTL_formatblock(struct NFTLrecord *s, int block);
+
+#ifndef NFTL_MAJOR
#define NFTL_MAJOR 93
+#endif
+
#define MAX_NFTLS 16
+#define MAX_SECTORS_PER_UNIT 32
#endif /* __KERNEL__ */
--- /dev/null
+/*
+ * MTD partitioning layer definitions
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is GPL
+ *
+ * $Id: partitions.h,v 1.3 2000/11/10 23:35:12 nico Exp $
+ */
+
+#ifndef MTD_PARTITIONS_H
+#define MTD_PARTITIONS_H
+
+#include <linux/types.h>
+
+
+/*
+ * Partition definition structure:
+ *
+ * An array of struct partition is passed along with a MTD object to
+ * add_mtd_partitions() to create them.
+ *
+ * For each partition, these fields are available:
+ * name: string that will be used to label the partition's MTD device.
+ * size: the partition size; if 0, the partition will extend to the end of the
+ * master MTD device.
+ * offset: absolute starting position within the master MTD device; if 0,
+ * partition will start where the previous one ended.
+ * mask_flags: contains flags that have to be masked (removed) from the
+ * master MTD flag set for the corresponding MTD partition.
+ * For example, to force a read-only partition, simply adding
+ * MTD_WRITEABLE to the mask_flags will do the trick.
+ *
+ * Note: writeable partitions require their size and offset be
+ * erasesize aligned.
+ */
+
+struct mtd_partition {
+ char *name; /* identifier string */
+ u_long size; /* partition size */
+ u_long offset; /* offset within the master MTD space */
+ u_long mask_flags; /* master MTD flags to mask out for this partition */
+};
+
+
+int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
+int del_mtd_partitions(struct mtd_info *);
+
+#endif
+
nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
- return 0;
+ return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
mddev_t *mddev; /* RAID array if running */
unsigned long last_events; /* IO event timestamp */
- struct inode *inode; /* Lock inode */
- struct file filp; /* Lock file */
+ struct block_device *bdev; /* block device handle */
mdp_super_t *sb;
unsigned long sb_offset;
struct semaphore reconfig_sem;
struct semaphore recovery_sem;
struct semaphore resync_sem;
+ atomic_t active;
atomic_t recovery_active; /* blocks scheduled, but not written */
md_wait_queue_head_t recovery_wait;
int number;
int raid_disk;
kdev_t dev;
- int next;
int sect_limit;
int head_position;
struct sk_buff_head * list; /* List we are on */
struct sock *sk; /* Socket we are owned by */
struct timeval stamp; /* Time we arrived */
- struct net_device *dev; /* Device we arrived on/are leaving by */
- struct net_device *rx_dev;
+ struct net_device *dev; /* Device we arrived on/are leaving by */
/* Transport layer header */
union
unsigned int len; /* Length of actual data */
unsigned int csum; /* Checksum */
volatile char used; /* Data moved to user and not MSG_PEEK */
- unsigned char is_clone, /* We are a clone */
- cloned, /* head may be cloned (check refcnt to be sure). */
+ unsigned char cloned, /* head may be cloned (check refcnt to be sure). */
pkt_type, /* Packet class */
ip_summed; /* Driver fed us an IP checksum */
__u32 priority; /* Packet queueing priority */
#elif (DEBUG_SPINLOCKS < 2)
typedef struct {
- volatile unsigned int lock;
+ volatile unsigned long lock;
} spinlock_t;
#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
#else /* (DEBUG_SPINLOCKS >= 2) */
typedef struct {
- volatile unsigned int lock;
+ volatile unsigned long lock;
volatile unsigned int babble;
const char *module;
} spinlock_t;
}
#endif /* CONFIG_FILTER */
+ skb->dev = NULL;
skb_set_owner_r(skb, sk);
skb_queue_tail(&sk->receive_queue, skb);
if (!sk->dead)
OX_OBJS += pm.o
endif
+ifneq ($(CONFIG_IA64),y)
+# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+# needed for x86 only. Why this used to be enabled for all architectures is beyond
+# me. I suspect most platforms don't need this, but until we know that for sure
+# I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k
+# to get a correct value for the wait-channel (WCHAN in ps). --davidm
CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
+endif
include $(TOPDIR)/Rules.make
* arch/ia64/kernel/process.c.
*/
int do_fork(unsigned long clone_flags, unsigned long stack_start,
- struct pt_regs *regs, unsigned long stack_top)
+ struct pt_regs *regs, unsigned long stack_size)
{
int retval = -ENOMEM;
struct task_struct *p;
goto bad_fork_cleanup_fs;
if (copy_mm(clone_flags, p))
goto bad_fork_cleanup_sighand;
- retval = copy_thread(0, clone_flags, stack_start, stack_top, p, regs);
+ retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_sighand;
p->semundo = NULL;
/* If it's a COW mapping, write protect it both in the parent and the child */
if (cow) {
- ptep_clear_wrprotect(src_pte);
+ ptep_set_wrprotect(src_pte);
pte = *src_pte;
}
return -1;
}
-/*
- * This function zeroes out partial mmap'ed pages at truncation time..
- */
-static void partial_clear(struct vm_area_struct *vma, unsigned long address)
-{
- unsigned int offset;
- struct page *page;
- pgd_t *page_dir;
- pmd_t *page_middle;
- pte_t *page_table, pte;
-
- page_dir = pgd_offset(vma->vm_mm, address);
- if (pgd_none(*page_dir))
- return;
- if (pgd_bad(*page_dir)) {
- pgd_ERROR(*page_dir);
- pgd_clear(page_dir);
- return;
- }
- page_middle = pmd_offset(page_dir, address);
- if (pmd_none(*page_middle))
- return;
- if (pmd_bad(*page_middle)) {
- pmd_ERROR(*page_middle);
- pmd_clear(page_middle);
- return;
- }
- page_table = pte_offset(page_middle, address);
- pte = *page_table;
- if (!pte_present(pte))
- return;
- flush_cache_page(vma, address);
- page = pte_page(pte);
- if ((!VALID_PAGE(page)) || PageReserved(page))
- return;
- offset = address & ~PAGE_MASK;
- memclear_highpage_flush(page, offset, PAGE_SIZE - offset);
-}
-
static void vmtruncate_list(struct vm_area_struct *mpnt,
unsigned long pgoff, unsigned long partial)
{
/* Ok, partially affected.. */
start += diff << PAGE_SHIFT;
len = (len - diff) << PAGE_SHIFT;
- if (start & ~PAGE_MASK) {
- partial_clear(mpnt, start);
- start = (start + ~PAGE_MASK) & PAGE_MASK;
- }
flush_cache_range(mm, start, end);
zap_page_range(mm, start, len);
flush_tlb_range(mm, start, end);
lock_vma_mappings(vma);
spin_lock(&mm->page_table_lock);
__insert_vm_struct(mm, vma);
+ unlock_vma_mappings(vma);
if (correct_wcount)
atomic_inc(&file->f_dentry->d_inode->i_writecount);
merge_segments(mm, vma->vm_start, vma->vm_end);
spin_unlock(&mm->page_table_lock);
- unlock_vma_mappings(vma);
mm->total_vm += len >> PAGE_SHIFT;
if (flags & VM_LOCKED) {
lock_vma_mappings(vma);
spin_lock(&mm->page_table_lock);
__insert_vm_struct(mm, vma);
+ unlock_vma_mappings(vma);
merge_segments(mm, vma->vm_start, vma->vm_end);
spin_unlock(&mm->page_table_lock);
- unlock_vma_mappings(vma);
mm->total_vm += len >> PAGE_SHIFT;
if (flags & VM_LOCKED) {
avl_remove(mpnt, &mm->mmap_avl);
prev->vm_end = mpnt->vm_end;
prev->vm_next = mpnt->vm_next;
+ mm->map_count--;
if (mpnt->vm_ops && mpnt->vm_ops->close) {
mpnt->vm_pgoff += (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
mpnt->vm_start = mpnt->vm_end;
spin_unlock(&mm->page_table_lock);
- unlock_vma_mappings(mpnt);
mpnt->vm_ops->close(mpnt);
- lock_vma_mappings(mpnt);
- spin_lock(&mm->page_table_lock);
- }
- mm->map_count--;
+ } else
+ spin_unlock(&mm->page_table_lock);
+
+ lock_vma_mappings(mpnt);
__remove_shared_vm_struct(mpnt);
+ unlock_vma_mappings(mpnt);
if (mpnt->vm_file)
fput(mpnt->vm_file);
kmem_cache_free(vm_area_cachep, mpnt);
mpnt = prev;
+
+ spin_lock(&mm->page_table_lock);
}
}
lock_vma_mappings(vma);
spin_lock(¤t->mm->page_table_lock);
__insert_vm_struct(current->mm, new_vma);
+ unlock_vma_mappings(vma);
merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end);
spin_unlock(¤t->mm->page_table_lock);
- unlock_vma_mappings(vma);
do_munmap(current->mm, addr, old_len);
current->mm->total_vm += new_len >> PAGE_SHIFT;
if (new_vma->vm_flags & VM_LOCKED) {
default:
here += sprintf(here,"%3d",vcc->family);
}
- here += sprintf(here," %04x %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
+ here += sprintf(here," %04x %5ld %7d/%7d %7d/%7d\n",vcc->flags.bits,
vcc->reply,
atomic_read(&vcc->tx_inuse),vcc->sk->sndbuf,
atomic_read(&vcc->rx_inuse),vcc->sk->rcvbuf);
void dev_load(const char *name)
{
- if (!__dev_get_by_name(name) && capable(CAP_SYS_MODULE))
+ if (!dev_get(name) && capable(CAP_SYS_MODULE))
request_module(name);
}
skb2->h.raw = skb2->nh.raw;
skb2->pkt_type = PACKET_OUTGOING;
- skb2->rx_dev = skb->dev;
- dev_hold(skb2->rx_dev);
ptype->func(skb2, skb->dev, ptype);
}
}
goto drop;
enqueue:
- if (skb->rx_dev)
- dev_put(skb->rx_dev);
- skb->rx_dev = skb->dev;
- dev_hold(skb->rx_dev);
+ dev_hold(skb->dev);
__skb_queue_tail(&queue->input_pkt_queue,skb);
__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);
local_irq_restore(flags);
*/
static __inline__ void skb_bond(struct sk_buff *skb)
{
- struct net_device *dev = skb->rx_dev;
+ struct net_device *dev = skb->dev;
if (dev->master) {
dev_hold(dev->master);
- skb->dev = skb->rx_dev = dev->master;
+ skb->dev = dev->master;
dev_put(dev);
}
}
for (;;) {
struct sk_buff *skb;
+ struct net_device *rx_dev;
local_irq_disable();
skb = __skb_dequeue(&queue->input_pkt_queue);
skb_bond(skb);
+ rx_dev = skb->dev;
+
#ifdef CONFIG_NET_FASTROUTE
if (skb->pkt_type == PACKET_FASTROUTE) {
netdev_rx_stat[this_cpu].fastroute_deferred_out++;
dev_queue_xmit(skb);
+ dev_put(rx_dev);
continue;
}
#endif
if (skb->dev->br_port != NULL &&
br_handle_frame_hook != NULL) {
handle_bridge(skb, pt_prev);
+ dev_put(rx_dev);
continue;
}
#endif
kfree_skb(skb);
}
+ dev_put(rx_dev);
+
if (bugdet-- < 0 || jiffies - start_time > 1)
goto softnet_break;
#endif
if (dev_boot_phase) {
+#ifdef CONFIG_NET_DIVERT
+ ret = alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif /* CONFIG_NET_DIVERT */
+
/* This is NOT bug, but I am not sure, that all the
devices, initialized before netdev module is started
are sane.
dev_hold(dev);
write_unlock_bh(&dev_base_lock);
-#ifdef CONFIG_NET_DIVERT
- ret = alloc_divert_blk(dev);
- if (ret)
- return ret;
-#endif /* CONFIG_NET_DIVERT */
-
/*
* Default initial state at registry is that the
* device is present.
return 0;
}
+#ifdef CONFIG_NET_DIVERT
+ ret = alloc_divert_blk(dev);
+ if (ret)
+ return ret;
+#endif /* CONFIG_NET_DIVERT */
+
dev->iflink = -1;
/* Init, if this function is available */
dev->deadbeaf = 0;
write_unlock_bh(&dev_base_lock);
-#ifdef CONFIG_NET_DIVERT
- ret = alloc_divert_blk(dev);
- if (ret)
- return ret;
-#endif /* CONFIG_NET_DIVERT */
-
/* Notify protocols, that a new device appeared. */
notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
if (dev->divert == NULL) {
printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
dev->name);
- return -EFAULT;
+ return -ENOMEM;
} else {
memset(dev->divert, 0, sizeof(struct divert_blk));
}
* Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
* Florian La Roche <rzsfl@rz.uni-sb.de>
*
- * Version: $Id: skbuff.c,v 1.73 2000/05/22 07:29:44 davem Exp $
+ * Version: $Id: skbuff.c,v 1.75 2000/12/08 17:15:53 davem Exp $
*
* Fixes:
* Alan Cox : Fixed the worst of the load balancer bugs.
/* Set up other state */
skb->len = 0;
- skb->is_clone = 0;
skb->cloned = 0;
atomic_set(&skb->users, 1);
skb->ip_summed = 0;
skb->security = 0; /* By default packets are insecure */
skb->dst = NULL;
- skb->rx_dev = NULL;
#ifdef CONFIG_NETFILTER
skb->nfmark = skb->nfcache = 0;
skb->nfct = NULL;
#ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
#endif
-#ifdef CONFIG_NET
- if(skb->rx_dev)
- dev_put(skb->rx_dev);
-#endif
skb_headerinit(skb, NULL, 0); /* clean state */
kfree_skbmem(skb);
}
skb->cloned = 1;
dst_clone(n->dst);
- n->rx_dev = NULL;
n->cloned = 1;
n->next = n->prev = NULL;
n->list = NULL;
n->sk = NULL;
- n->is_clone = 1;
atomic_set(&n->users, 1);
n->destructor = NULL;
#ifdef CONFIG_NETFILTER
new->list=NULL;
new->sk=NULL;
new->dev=old->dev;
- new->rx_dev=NULL;
new->priority=old->priority;
new->protocol=old->protocol;
new->dst=dst_clone(old->dst);
new->mac.raw=old->mac.raw+offset;
memcpy(new->cb, old->cb, sizeof(old->cb));
new->used=old->used;
- new->is_clone=0;
atomic_set(&new->users, 1);
new->pkt_type=old->pkt_type;
new->stamp=old->stamp;
static void dn_log_martian(struct sk_buff *skb, const char *msg)
{
if (decnet_log_martians && net_ratelimit()) {
- char *devname = skb->rx_dev ? skb->rx_dev->name : "???";
+ char *devname = skb->dev ? skb->dev->name : "???";
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- printk(KERN_INFO "DECnet: Martian packet (%s) rx_dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port);
+ printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port);
}
}
int dn_nsp_rx(struct sk_buff *skb)
{
- return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->rx_dev, NULL, dn_nsp_rx_packet);
+ return NF_HOOK(PF_DECnet, NF_DN_LOCAL_IN, skb, skb->dev, NULL, dn_nsp_rx_packet);
}
/*
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct dst_entry *dst = skb->dst;
+ struct net_device *dev = skb->dev;
struct neighbour *neigh;
int err = -EINVAL;
else
cb->rt_flags &= ~DN_RT_F_IE;
- return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, skb->rx_dev, skb->dev, neigh->output);
+ return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output);
error:
}
skb->protocol = __constant_htons(ETH_P_DNA_RT);
skb->dev = dev;
- skb->rx_dev = dev;
cb->src = src;
cb->dst = dst;
local_bh_disable();
if (skb->dev)
dev_put(skb->dev);
skb->dev = NULL;
- skb->rx_dev = NULL;
if (err)
goto out_free;
skb->dst = &rt->u.dst;
/*
* NET3 IP device support routines.
*
- * Version: $Id: devinet.c,v 1.38 2000/08/19 23:22:56 davem Exp $
+ * Version: $Id: devinet.c,v 1.39 2000/12/10 22:24:11 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
*
* The IP fragmentation functionality.
*
- * Version: $Id: ip_fragment.c,v 1.52 2000/11/28 13:32:54 davem Exp $
+ * Version: $Id: ip_fragment.c,v 1.53 2000/12/08 17:15:53 davem Exp $
*
* Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
* Alan Cox <Alan.Cox@linux.org>
atomic_t refcnt;
struct timer_list timer; /* when will this queue expire? */
struct ipq **pprev;
- struct net_device *dev; /* Device - for icmp replies */
+ int iif; /* Device index - for icmp replies */
};
/* Hash table. */
IP_INC_STATS_BH(IpReasmFails);
if ((qp->last_in&FIRST_IN) && qp->fragments != NULL) {
+ struct sk_buff *head = qp->fragments;
+
/* Send an ICMP "Fragment Reassembly Timeout" message. */
- icmp_send(qp->fragments, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
+ if ((head->dev = dev_get_by_index(qp->iif)) != NULL) {
+ icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
+ dev_put(head->dev);
+ }
}
out:
spin_unlock(&qp->lock);
else
qp->fragments = skb;
- qp->dev = skb->dev;
+ qp->iif = skb->dev->ifindex;
+ skb->dev = NULL;
qp->meat += skb->len;
atomic_add(skb->truesize, &ip_frag_mem);
if (offset == 0)
* of bits on input. Until the new skb data handling is in I'm not going
* to touch this with a bargepole.
*/
-static struct sk_buff *ip_frag_reasm(struct ipq *qp)
+static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)
{
struct sk_buff *skb;
struct iphdr *iph;
skb->dst = dst_clone(head->dst);
skb->pkt_type = head->pkt_type;
skb->protocol = head->protocol;
- skb->dev = qp->dev;
+ skb->dev = dev;
/*
* Clearly bogus, because security markings of the individual
{
struct iphdr *iph = skb->nh.iph;
struct ipq *qp;
+ struct net_device *dev;
IP_INC_STATS_BH(IpReasmReqds);
if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
ip_evictor();
+ dev = skb->dev;
+
/* Lookup (or create) queue header */
if ((qp = ip_find(iph)) != NULL) {
struct sk_buff *ret = NULL;
if (qp->last_in == (FIRST_IN|LAST_IN) &&
qp->meat == qp->len)
- ret = ip_frag_reasm(qp);
+ ret = ip_frag_reasm(qp, dev);
spin_unlock(&qp->lock);
ipq_put(qp);
*
* The Internet Protocol (IP) module.
*
- * Version: $Id: ip_input.c,v 1.50 2000/10/24 22:54:26 davem Exp $
+ * Version: $Id: ip_input.c,v 1.51 2000/12/08 17:15:53 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
nf_debug_ip_local_deliver(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/
- /* Free rx_dev before enqueueing to sockets */
- if (skb->rx_dev) {
- dev_put(skb->rx_dev);
- skb->rx_dev = NULL;
- }
-
/* Point into the IP datagram, just past the header. */
skb->h.raw = skb->nh.raw + iph->ihl*4;
if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name);
else pm->outdev_name[0] = '\0';
pm->hw_protocol = e->skb->protocol;
- if (e->skb->rx_dev) {
- pm->hw_type = e->skb->rx_dev->type;
- if (e->skb->rx_dev->hard_header_parse)
- pm->hw_addrlen =
- e->skb->rx_dev->hard_header_parse(e->skb,
- pm->hw_addr);
- }
if (data_len)
memcpy(pm->payload, e->skb->data, data_len);
nlh->nlmsg_len = skb->tail - old_tail;
/* check if the interface we are leaving by is the same as the
one we arrived on */
- if (skb->rx_dev == rt->u.dst.dev) {
+ if (skb->dev == rt->u.dst.dev) {
/* Drop old route. */
dst_release(skb->dst);
skb->dst = &rt->u.dst;
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.221 2000/11/28 17:04:10 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.222 2000/12/08 17:15:53 davem Exp $
*
* IPv4 specific functions
*
if (sk->state == TCP_TIME_WAIT)
goto do_time_wait;
+ skb->dev = NULL;
+
bh_lock_sock(sk);
ret = 0;
if (!sk->lock.users) {
* Pedro Roque <roque@di.fc.ul.pt>
* Ian P. Morris <I.P.Morris@soton.ac.uk>
*
- * $Id: ip6_input.c,v 1.17 2000/02/27 19:42:53 davem Exp $
+ * $Id: ip6_input.c,v 1.18 2000/12/08 17:15:54 davem Exp $
*
* Based in linux/net/ipv4/ip_input.c
*
}
len = skb->tail - skb->h.raw;
- if (skb->rx_dev) {
- dev_put(skb->rx_dev);
- skb->rx_dev = NULL;
- }
-
raw_sk = raw_v6_htable[nexthdr&(MAX_INET_PROTOS-1)];
if (raw_sk)
raw_sk = ipv6_raw_deliver(skb, nexthdr, len);
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: reassembly.c,v 1.20 2000/11/28 13:48:03 davem Exp $
+ * $Id: reassembly.c,v 1.22 2000/12/08 17:41:54 davem Exp $
*
* Based on: net/ipv4/ip_fragment.c
*
struct sk_buff *fragments;
int len;
int meat;
- struct net_device *dev;
int iif;
__u8 last_in; /* has first/last segment arrived? */
#define COMPLETE 4
else
fq->fragments = skb;
- fq->dev = skb->dev;
fq->iif = skb->dev->ifindex;
+ skb->dev = NULL;
fq->meat += skb->len;
atomic_add(skb->truesize, &ip6_frag_mem);
* queue is eligible for reassembly i.e. it is not COMPLETE,
* the last and the first frames arrived and all the bits are here.
*/
-static u8* ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in)
+static u8 *ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
+ struct net_device *dev)
{
struct sk_buff *fp, *head = fq->fragments;
struct sk_buff *skb;
skb->mac.raw = skb->data;
skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
- skb->dev = fq->dev;
+ skb->dev = dev;
skb->protocol = __constant_htons(ETH_P_IPV6);
skb->pkt_type = head->pkt_type;
FRAG6_CB(skb)->h = FRAG6_CB(head)->h;
{
struct sk_buff *skb = *skbp;
struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
+ struct net_device *dev = skb->dev;
struct frag_queue *fq;
struct ipv6hdr *hdr;
if (fq->last_in == (FIRST_IN|LAST_IN) &&
fq->meat == fq->len)
- ret = ip6_frag_reasm(fq, skbp);
+ ret = ip6_frag_reasm(fq, skbp, dev);
spin_unlock(&fq->lock);
fq_put(fq);
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.127 2000/11/28 17:04:10 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.128 2000/12/08 17:15:54 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
+ skb->dev = NULL;
+
bh_lock_sock(sk);
ret = 0;
if (!sk->lock.users) {
*
* PACKET - implements raw packet sockets.
*
- * Version: $Id: af_packet.c,v 1.46 2000/10/24 21:26:19 davem Exp $
+ * Version: $Id: af_packet.c,v 1.47 2000/12/08 17:15:54 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
strncpy(spkt->spkt_device, dev->name, sizeof(spkt->spkt_device));
spkt->spkt_protocol = skb->protocol;
- if (skb->rx_dev) {
- dev_put(skb->rx_dev);
- skb->rx_dev = NULL;
- }
-
/*
* Charge the memory to the socket. This is done specifically
* to prevent sockets using all the memory up.
if (dev->hard_header_parse)
sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
- if (skb->rx_dev) {
- dev_put(skb->rx_dev);
- skb->rx_dev = NULL;
- }
-
#ifdef CONFIG_FILTER
if (skb->len > snaplen)
__skb_trim(skb, snaplen);
#endif
skb_set_owner_r(skb, sk);
+ skb->dev = NULL;
spin_lock(&sk->receive_queue.lock);
po->stats.tp_packets++;
__skb_queue_tail(&sk->receive_queue, skb);
sk->state = TCP_ESTABLISHED;
sk->sleep = osk->sleep;
sk->zapped = osk->zapped;
+ sk->backlog_rcv = osk->backlog_rcv;
x25->t21 = osk->protinfo.x25->t21;
x25->t22 = osk->protinfo.x25->t22;
return -EINVAL;
/* we currently don't support segmented records at the user interface */
- if (!(msg->msg_flags & MSG_EOR))
+ if (!(msg->msg_flags & (MSG_EOR|MSG_OOB)))
return -EINVAL;
if (sk->zapped)
## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
## Copyright (C) 2000 Tim Waugh <twaugh@redhat.com> ##
+## ##
+## #define enhancements by Armin Kuster <akuster@mvista.com> ##
+## Copyright (c) 2000 MontaVista Software, Inc. ##
## ##
## This software falls under the GNU General Public License. ##
## Please read the COPYING file for more information ##
+# w.o. 03-11-2000: added the '-filelist' option.
+
#
# This will read a 'c' file and scan for embedded comments in the
# style of gnome comments (+minor extensions - see below).
$blankline = $blankline_man;
$modulename = "API Documentation";
$function_only = 0;
+$filelist = '';
+
while ($ARGV[0] =~ m/^-(.*)/) {
$cmd = shift @ARGV;
if ($cmd eq "-html") {
$verbose = 1;
} elsif (($cmd eq "-h") || ($cmd eq "--help")) {
usage();
+ } elsif ($cmd eq '-filelist') {
+ $filelist = shift @ARGV;
}
}
$prototype =~ s/^extern+ //;
$prototype =~ s/^inline+ //;
$prototype =~ s/^__inline__+ //;
-
- if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
+ $prototype =~ s/^#define+ //; #ak added
+
+ if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
+ $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
+ $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
+ $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
+ $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ ||
+ $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
$prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
$prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
$prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ ||
$section_intro = "Introduction";
$section = $section_default;
-$lineno = 0;
+if( $filelist ne '' ) {
+ open(FLIST,"<$filelist") or die "Can't open file list $filelist";
+ while(<FLIST>) {
+ chop;
+ process_file($_);
+ }
+}
+
foreach $file (@ARGV) {
chomp($file);
+ process_file($file);
+}
+
+sub process_file($) {
+ my ($file) = @_;
+
if (!open(IN,"<$file")) {
print STDERR "Error: Cannot open file $file\n";
- next;
+ return;
}
+
+ $lineno = 0;
while (<IN>) {
$lineno++;
elsif (/([^\{]*)/) {
$prototype .= $1;
}
- if (/\{/) {
+ if (/\{/ || /\#/) { # added for #define AK
$prototype =~ s@/\*.*?\*/@@gos; # strip comments.
$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
$prototype =~ s@^ +@@gos; # strip leading spaces