diff options
259 files changed, 9001 insertions, 2269 deletions
diff --git a/.gitignore b/.gitignore index fdcce40226d7..3016ed30526d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ TAGS vmlinux* !vmlinux.lds.S System.map +Module.markers Module.symvers !.gitignore diff --git a/Documentation/ABI/stable/sysfs-class-ubi b/Documentation/ABI/stable/sysfs-class-ubi new file mode 100644 index 000000000000..18d471d9faea --- /dev/null +++ b/Documentation/ABI/stable/sysfs-class-ubi @@ -0,0 +1,212 @@ +What: /sys/class/ubi/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The ubi/ class sub-directory belongs to the UBI subsystem and + provides general UBI information, per-UBI device information + and per-UBI volume information. + +What: /sys/class/ubi/version +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + This file contains version of the latest supported UBI on-media + format. Currently it is 1, and there is no plan to change this. + However, if in the future UBI needs on-flash format changes + which cannot be done in a compatible manner, a new format + version will be added. So this is a mechanism for possible + future backward-compatible (but forward-incompatible) + improvements. + +What: /sys/class/ubiX/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The /sys/class/ubi0, /sys/class/ubi1, etc directories describe + UBI devices (UBI device 0, 1, etc). They contain general UBI + device information and per UBI volume information (each UBI + device may have many UBI volumes) + +What: /sys/class/ubi/ubiX/avail_eraseblocks +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Amount of available logical eraseblock. For example, one may + create a new UBI volume which has this amount of logical + eraseblocks. + +What: /sys/class/ubi/ubiX/bad_peb_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of bad physical eraseblocks on the underlying MTD device. + +What: /sys/class/ubi/ubiX/bgt_enabled +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the UBI background thread is disabled, + and ASCII "1\n" if it is enabled. + +What: /sys/class/ubi/ubiX/dev +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Major and minor numbers of the character device corresponding + to this UBI device (in <major>:<minor> format). + +What: /sys/class/ubi/ubiX/eraseblock_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum logical eraseblock size this UBI device may provide. UBI + volumes may have smaller logical eraseblock size because of their + alignment. + +What: /sys/class/ubi/ubiX/max_ec +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum physical eraseblock erase counter value. + +What: /sys/class/ubi/ubiX/max_vol_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Maximum number of volumes which this UBI device may have. + +What: /sys/class/ubi/ubiX/min_io_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Minimum input/output unit size. All the I/O may only be done + in fractions of the contained number. + +What: /sys/class/ubi/ubiX/mtd_num +Date: January 2008 +KernelVersion: 2.6.25 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Number of the underlying MTD device. + +What: /sys/class/ubi/ubiX/reserved_for_bad +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Number of physical eraseblocks reserved for bad block handling. + +What: /sys/class/ubi/ubiX/total_eraseblocks +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Total number of good (not marked as bad) physical eraseblocks on + the underlying MTD device. + +What: /sys/class/ubi/ubiX/volumes_count +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of volumes on this UBI device. + +What: /sys/class/ubi/ubiX/ubiX_Y/ +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The /sys/class/ubi/ubiX/ubiX_0/, /sys/class/ubi/ubiX/ubiX_1/, + etc directories describe UBI volumes on UBI device X (volumes + 0, 1, etc). + +What: /sys/class/ubi/ubiX/ubiX_Y/alignment +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume alignment - the value the logical eraseblock size of + this volume has to be aligned on. For example, 2048 means that + logical eraseblock size is multiple of 2048. In other words, + volume logical eraseblock size is UBI device logical eraseblock + size aligned to the alignment value. + +What: /sys/class/ubi/ubiX/ubiX_Y/corrupted +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the UBI volume is OK, and ASCII "1\n" + if it is corrupted (e.g., due to an interrupted volume update). + +What: /sys/class/ubi/ubiX/ubiX_Y/data_bytes +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + The amount of data this volume contains. This value makes sense + only for static volumes, and for dynamic volume it equivalent + to the total volume size in bytes. + +What: /sys/class/ubi/ubiX/ubiX_Y/dev +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Major and minor numbers of the character device corresponding + to this UBI volume (in <major>:<minor> format). + +What: /sys/class/ubi/ubiX/ubiX_Y/name +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume name. + +What: /sys/class/ubi/ubiX/ubiX_Y/reserved_ebs +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Count of physical eraseblock reserved for this volume. + Equivalent to the volume size in logical eraseblocks. + +What: /sys/class/ubi/ubiX/ubiX_Y/type +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Volume type. Contains ASCII "dynamic\n" for dynamic volumes and + "static\n" for static volumes. + +What: /sys/class/ubi/ubiX/ubiX_Y/upd_marker +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Contains ASCII "0\n" if the update marker is not set for this + volume, and "1\n" if it is set. The update marker is set when + volume update starts, and cleaned when it ends. So the presence + of the update marker indicates that the volume is being updated + at the moment of the update was interrupted. The later may be + checked using the "corrupted" sysfs file. + +What: /sys/class/ubi/ubiX/ubiX_Y/usable_eb_size +Date: July 2006 +KernelVersion: 2.6.22 +Contact: Artem Bityutskiy <dedekind@infradead.org> +Description: + Logical eraseblock size of this volume. Equivalent to logical + eraseblock size of the device aligned on the volume alignment + value. diff --git a/Documentation/HOWTO b/Documentation/HOWTO index 54835610b3d6..0291ade44c17 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -249,9 +249,11 @@ process is as follows: release a new -rc kernel every week. - Process continues until the kernel is considered "ready", the process should last around 6 weeks. - - A list of known regressions present in each -rc release is - tracked at the following URI: - http://kernelnewbies.org/known_regressions + - Known regressions in each release are periodically posted to the + linux-kernel mailing list. The goal is to reduce the length of + that list to zero before declaring the kernel to be "ready," but, in + the real world, a small number of regressions often remain at + release time. It is worth mentioning what Andrew Morton wrote on the linux-kernel mailing list about kernel releases: @@ -261,7 +263,7 @@ mailing list about kernel releases: 2.6.x.y -stable kernel tree --------------------------- -Kernels with 4 digit versions are -stable kernels. They contain +Kernels with 4-part versions are -stable kernels. They contain relatively small and critical fixes for security problems or significant regressions discovered in a given 2.6.x kernel. @@ -273,7 +275,10 @@ If no 2.6.x.y kernel is available, then the highest numbered 2.6.x kernel is the current stable kernel. 2.6.x.y are maintained by the "stable" team <stable@kernel.org>, and are -released almost every other week. +released as needs dictate. The normal release period is approximately +two weeks, but it can be longer if there are no pressing problems. A +security-related problem, instead, can cause a release to happen almost +instantly. The file Documentation/stable_kernel_rules.txt in the kernel tree documents what kinds of changes are acceptable for the -stable tree, and @@ -298,7 +303,9 @@ a while Andrew or the subsystem maintainer pushes it on to Linus for inclusion in mainline. It is heavily encouraged that all new patches get tested in the -mm tree -before they are sent to Linus for inclusion in the main kernel tree. +before they are sent to Linus for inclusion in the main kernel tree. Code +which does not make an appearance in -mm before the opening of the merge +window will prove hard to merge into the mainline. These kernels are not appropriate for use on systems that are supposed to be stable and they are more risky to run than any of the other @@ -354,11 +361,12 @@ Here is a list of some of the different kernel trees available: - SCSI, James Bottomley <James.Bottomley@SteelEye.com> git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git + - x86, Ingo Molnar <mingo@elte.hu> + git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git + quilt trees: - - USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman <gregkh@suse.de> + - USB, Driver Core, and I2C, Greg Kroah-Hartman <gregkh@suse.de> kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ - - x86-64, partly i386, Andi Kleen <ak@suse.de> - ftp.firstfloor.org:/pub/ak/x86_64/quilt/ Other kernel trees can be found listed at http://git.kernel.org/ and in the MAINTAINERS file. @@ -392,8 +400,8 @@ If you want to be advised of the future bug reports, you can subscribe to the bugme-new mailing list (only new bug reports are mailed here) or to the bugme-janitor mailing list (every change in the bugzilla is mailed here) - http://lists.osdl.org/mailman/listinfo/bugme-new - http://lists.osdl.org/mailman/listinfo/bugme-janitors + http://lists.linux-foundation.org/mailman/listinfo/bugme-new + http://lists.linux-foundation.org/mailman/listinfo/bugme-janitors diff --git a/Documentation/arm/Samsung-S3C24XX/NAND.txt b/Documentation/arm/Samsung-S3C24XX/NAND.txt new file mode 100644 index 000000000000..bc478a3409b8 --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/NAND.txt @@ -0,0 +1,30 @@ + S3C24XX NAND Support + ==================== + +Introduction +------------ + +Small Page NAND +--------------- + +The driver uses a 512 byte (1 page) ECC code for this setup. The +ECC code is not directly compatible with the default kernel ECC +code, so the driver enforces its own OOB layout and ECC parameters + +Large Page NAND +--------------- + +The driver is capable of handling NAND flash with a 2KiB page +size, with support for hardware ECC generation and correction. + +Unlike the 512byte page mode, the driver generates ECC data for +each 256 byte block in an 2KiB page. This means that more than +one error in a page can be rectified. It also means that the +OOB layout remains the default kernel layout for these flashes. + + +Document Author +--------------- + +Ben Dooks, Copyright 2007 Simtec Electronics + diff --git a/Documentation/arm/Samsung-S3C24XX/Overview.txt b/Documentation/arm/Samsung-S3C24XX/Overview.txt index c31b76fa66c4..d04e1e30c47f 100644 --- a/Documentation/arm/Samsung-S3C24XX/Overview.txt +++ b/Documentation/arm/Samsung-S3C24XX/Overview.txt @@ -156,6 +156,8 @@ NAND controller. If there are any problems the latest linux-mtd code can be found from http://www.linux-mtd.infradead.org/ + For more information see Documentation/arm/Samsung-S3C24XX/NAND.txt + Serial ------ diff --git a/Documentation/filesystems/nfs-rdma.txt b/Documentation/filesystems/nfs-rdma.txt index 1ae34879574b..d0ec45ae4e7d 100644 --- a/Documentation/filesystems/nfs-rdma.txt +++ b/Documentation/filesystems/nfs-rdma.txt @@ -5,7 +5,7 @@ ################################################################################ Author: NetApp and Open Grid Computing - Date: February 25, 2008 + Date: April 15, 2008 Table of Contents ~~~~~~~~~~~~~~~~~ @@ -197,12 +197,16 @@ NFS/RDMA Setup - On the server system, configure the /etc/exports file and start the NFS/RDMA server. - Exports entries with the following format have been tested: + Exports entries with the following formats have been tested: - /vol0 10.97.103.47(rw,async) 192.168.0.47(rw,async,insecure,no_root_squash) + /vol0 192.168.0.47(fsid=0,rw,async,insecure,no_root_squash) + /vol0 192.168.0.0/255.255.255.0(fsid=0,rw,async,insecure,no_root_squash) - Here the first IP address is the client's Ethernet address and the second - IP address is the clients IPoIB address. + The IP address(es) is(are) the client's IPoIB address for an InfiniBand HCA or the + cleint's iWARP address(es) for an RNIC. + + NOTE: The "insecure" option must be used because the NFS/RDMA client does not + use a reserved port. Each time a machine boots: diff --git a/Documentation/filesystems/seq_file.txt b/Documentation/filesystems/seq_file.txt index 7fb8e6dc62bf..b843743aa0b5 100644 --- a/Documentation/filesystems/seq_file.txt +++ b/Documentation/filesystems/seq_file.txt @@ -122,8 +122,7 @@ stop() is the place to free it. } Finally, the show() function should format the object currently pointed to -by the iterator for output. It should return zero, or an error code if -something goes wrong. The example module's show() function is: +by the iterator for output. The example module's show() function is: static int ct_seq_show(struct seq_file *s, void *v) { @@ -132,6 +131,12 @@ something goes wrong. The example module's show() function is: return 0; } +If all is well, the show() function should return zero. A negative error +code in the usual manner indicates that something went wrong; it will be +passed back to user space. This function can also return SEQ_SKIP, which +causes the current item to be skipped; if the show() function has already +generated output before returning SEQ_SKIP, that output will be dropped. + We will look at seq_printf() in a moment. But first, the definition of the seq_file iterator is finished by creating a seq_operations structure with the four functions we have just defined: @@ -182,12 +187,18 @@ The first two output a single character and a string, just like one would expect. seq_escape() is like seq_puts(), except that any character in s which is in the string esc will be represented in octal form in the output. -There is also a function for printing filenames: +There is also a pair of functions for printing filenames: int seq_path(struct seq_file *m, struct path *path, char *esc); + int seq_path_root(struct seq_file *m, struct path *path, + struct path *root, char *esc) Here, path indicates the file of interest, and esc is a set of characters -which should be escaped in the output. +which should be escaped in the output. A call to seq_path() will output +the path relative to the current process's filesystem root. If a different +root is desired, it can be used with seq_path_root(). Note that, if it +turns out that path cannot be reached from root, the value of root will be +changed in seq_file_root() to a root which *does* work. Making it all work diff --git a/Documentation/kbuild/modules.txt b/Documentation/kbuild/modules.txt index 1d247d59ad56..1821c077b435 100644 --- a/Documentation/kbuild/modules.txt +++ b/Documentation/kbuild/modules.txt @@ -486,7 +486,7 @@ Module.symvers contains a list of all exported symbols from a kernel build. Sometimes, an external module uses exported symbols from another external module. Kbuild needs to have full knowledge on all symbols to avoid spitting out warnings about undefined symbols. - Two solutions exist to let kbuild know all symbols of more than + Three solutions exist to let kbuild know all symbols of more than one external module. The method with a top-level kbuild file is recommended but may be impractical in certain situations. @@ -523,6 +523,13 @@ Module.symvers contains a list of all exported symbols from a kernel build. containing the sum of all symbols defined and not part of the kernel. + Use make variable KBUILD_EXTRA_SYMBOLS in the Makefile + If it is impractical to copy Module.symvers from another + module, you can assign a space separated list of files to + KBUILD_EXTRA_SYMBOLS in your Makfile. These files will be + loaded by modpost during the initialisation of its symbol + tables. + === 8. Tips & Tricks --- 8.1 Testing for CONFIG_FOO_BAR diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 0bc95eab1512..8df6a7b0e66c 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -1,7 +1,7 @@ ------- PHY Abstraction Layer -(Updated 2006-11-30) +(Updated 2008-04-08) Purpose @@ -291,3 +291,39 @@ Writing a PHY driver Feel free to look at the Marvell, Cicada, and Davicom drivers in drivers/net/phy/ for examples (the lxt and qsemi drivers have not been tested as of this writing) + +Board Fixups + + Sometimes the specific interaction between the platform and the PHY requires + special handling. For instance, to change where the PHY's clock input is, + or to add a delay to account for latency issues in the data path. In order + to support such contingencies, the PHY Layer allows platform code to register + fixups to be run when the PHY is brought up (or subsequently reset). + + When the PHY Layer brings up a PHY it checks to see if there are any fixups + registered for it, matching based on UID (contained in the PHY device's phy_id + field) and the bus identifier (contained in phydev->dev.bus_id). Both must + match, however two constants, PHY_ANY_ID and PHY_ANY_UID, are provided as + wildcards for the bus ID and UID, respectively. + + When a match is found, the PHY layer will invoke the run function associated + with the fixup. This function is passed a pointer to the phy_device of + interest. It should therefore only operate on that PHY. + + The platform code can either register the fixup using phy_register_fixup(): + + int phy_register_fixup(const char *phy_id, + u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); + + Or using one of the two stubs, phy_register_fixup_for_uid() and + phy_register_fixup_for_id(): + + int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); + int phy_register_fixup_for_id(const char *phy_id, + int (*run)(struct phy_device *)); + + The stubs set one of the two matching criteria, and set the other one to + match anything. + @@ -507,6 +507,10 @@ else KBUILD_CFLAGS += -O2 endif +ifneq (CONFIG_FRAME_WARN,0) +KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=${CONFIG_FRAME_WARN}) +endif + # Force gcc to behave correct even for buggy distributions # Arch Makefiles may override this setting KBUILD_CFLAGS += $(call cc-option, -fno-stack-protector) @@ -1396,7 +1400,7 @@ define xtags $(all-kconfigs) | xargs $1 -a \ --langdef=kconfig \ --language-force=kconfig \ - --regex-kconfig='/^[[:blank:]]*config[[:blank:]]+([[:alnum:]_]+)/\1/'; \ + --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/'; \ $(all-defconfigs) | xargs -r $1 -a \ --langdef=dotconfig \ --language-force=dotconfig \ @@ -1404,7 +1408,7 @@ define xtags elif $1 --version 2>&1 | grep -iq emacs; then \ $(all-sources) | xargs $1 -a; \ $(all-kconfigs) | xargs $1 -a \ - --regex='/^[ \t]*config[ \t]+\([a-zA-Z0-9_]+\)/\1/'; \ + --regex='/^[ \t]*(menu|)config[ \t]+\([a-zA-Z0-9_]+\)/\2/'; \ $(all-defconfigs) | xargs -r $1 -a \ --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'; \ else \ @@ -1539,7 +1543,6 @@ quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files)) cmd_rmfiles = rm -f $(rm-files) # Run depmod only if we have System.map and depmod is executable -# and we build for the host arch quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) cmd_depmod = \ if [ -r System.map -a -x $(DEPMOD) ]; then \ diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 524b88920947..409dd71f2738 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -866,14 +866,21 @@ void smp_call_function_client(int irq, struct pt_regs *regs) void *info = call_data->info; clear_softint(1 << irq); + + irq_enter(); + + if (!call_data->wait) { + /* let initiator proceed after getting data */ + atomic_inc(&call_data->finished); + } + + func(info); + + irq_exit(); + if (call_data->wait) { /* let initiator proceed only after completion */ - func(info); atomic_inc(&call_data->finished); - } else { - /* let initiator proceed after getting data */ - atomic_inc(&call_data->finished); - func(info); } } @@ -1032,7 +1039,9 @@ void smp_receive_signal(int cpu) void smp_receive_signal_client(int irq, struct pt_regs *regs) { + irq_enter(); clear_softint(1 << irq); + irq_exit(); } void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) @@ -1040,6 +1049,8 @@ void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) struct mm_struct *mm; unsigned long flags; + irq_enter(); + clear_softint(1 << irq); /* See if we need to allocate a new TLB context because @@ -1059,6 +1070,8 @@ void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) load_secondary_context(mm); __flush_tlb_mm(CTX_HWBITS(mm->context), SECONDARY_CONTEXT); + + irq_exit(); } void smp_new_mmu_context_version(void) @@ -1217,6 +1230,8 @@ void smp_penguin_jailcell(int irq, struct pt_regs *regs) { clear_softint(1 << irq); + irq_enter(); + preempt_disable(); __asm__ __volatile__("flushw"); @@ -1229,6 +1244,8 @@ void smp_penguin_jailcell(int irq, struct pt_regs *regs) prom_world(0); preempt_enable(); + + irq_exit(); } /* /proc/profile writes can call this, don't __init it please. */ diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 73ed01ba40dc..8d4761f15fa9 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -454,8 +454,8 @@ asmlinkage long sys_ipc(unsigned int call, int first, unsigned long second, err = sys_semget(first, (int)second, (int)third); goto out; case SEMCTL: { - err = sys_semctl(first, third, - (int)second | IPC_64, + err = sys_semctl(first, second, + (int)third | IPC_64, (union semun) ptr); goto out; } diff --git a/arch/x86/boot/.gitignore b/arch/x86/boot/.gitignore index b1bdc4c6f9f2..172cf8a98bdd 100644 --- a/arch/x86/boot/.gitignore +++ b/arch/x86/boot/.gitignore @@ -1,7 +1,8 @@ bootsect bzImage +cpustr.h +mkcpustr +offsets.h setup setup.bin setup.elf -cpustr.h -mkcpustr diff --git a/arch/x86/kernel/acpi/realmode/.gitignore b/arch/x86/kernel/acpi/realmode/.gitignore new file mode 100644 index 000000000000..58f1f48a58f8 --- /dev/null +++ b/arch/x86/kernel/acpi/realmode/.gitignore @@ -0,0 +1,3 @@ +wakeup.bin +wakeup.elf +wakeup.lds diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index df4099dc1c68..65c7857a90dd 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -511,31 +511,30 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len) unsigned long flags; char *vaddr; int nr_pages = 2; + struct page *pages[2]; + int i; - BUG_ON(len > sizeof(long)); - BUG_ON((((long)addr + len - 1) & ~(sizeof(long) - 1)) - - ((long)addr & ~(sizeof(long) - 1))); - if (kernel_text_address((unsigned long)addr)) { - struct page *pages[2] = { virt_to_page(addr), - virt_to_page(addr + PAGE_SIZE) }; - if (!pages[1]) - nr_pages = 1; - vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL); - BUG_ON(!vaddr); - local_irq_save(flags); - memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len); - local_irq_restore(flags); - vunmap(vaddr); + if (!core_kernel_text((unsigned long)addr)) { + pages[0] = vmalloc_to_page(addr); + pages[1] = vmalloc_to_page(addr + PAGE_SIZE); } else { - /* - * modules are in vmalloc'ed memory, always writable. - */ - local_irq_save(flags); - memcpy(addr, opcode, len); - local_irq_restore(flags); + pages[0] = virt_to_page(addr); + WARN_ON(!PageReserved(pages[0])); + pages[1] = virt_to_page(addr + PAGE_SIZE); } + BUG_ON(!pages[0]); + if (!pages[1]) + nr_pages = 1; + vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL); + BUG_ON(!vaddr); + local_irq_save(flags); + memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len); + local_irq_restore(flags); + vunmap(vaddr); sync_core(); /* Could also do a CLFLUSH here to speed up CPU recovery; but that causes hangs on some VIA CPUs. */ + for (i = 0; i < len; i++) + BUG_ON(((char *)addr)[i] != ((char *)opcode)[i]); return addr; } diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index f0f8934fc303..2a609dc3271c 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -409,7 +409,7 @@ restore_nocheck_notrace: irq_return: INTERRUPT_RETURN .section .fixup,"ax" -iret_exc: +ENTRY(iret_exc) pushl $0 # no error code pushl $do_iret_error jmp error_code @@ -1017,6 +1017,13 @@ ENTRY(kernel_thread_helper) ENDPROC(kernel_thread_helper) #ifdef CONFIG_XEN +/* Xen doesn't set %esp to be precisely what the normal sysenter + entrypoint expects, so fix it up before using the normal path. */ +ENTRY(xen_sysenter_target) + RING0_INT_FRAME + addl $5*4, %esp /* remove xen-provided frame */ + jmp sysenter_past_esp + ENTRY(xen_hypervisor_callback) CFI_STARTPROC pushl $0 @@ -1035,8 +1042,9 @@ ENTRY(xen_hypervisor_callback) cmpl $xen_iret_end_crit,%eax jae 1f - call xen_iret_crit_fixup + jmp xen_iret_crit_fixup +ENTRY(xen_do_upcall) 1: mov %esp, %eax call xen_evtchn_do_upcall jmp ret_from_intr diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 3733412d1357..74f0c5ea2a03 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -366,11 +366,13 @@ struct pv_mmu_ops pv_mmu_ops = { .flush_tlb_single = native_flush_tlb_single, .flush_tlb_others = native_flush_tlb_others, - .alloc_pt = paravirt_nop, - .alloc_pd = paravirt_nop, - .alloc_pd_clone = paravirt_nop, - .release_pt = paravirt_nop, - .release_pd = paravirt_nop, + .alloc_pte = paravirt_nop, + .alloc_pmd = paravirt_nop, + .alloc_pmd_clone = paravirt_nop, + .alloc_pud = paravirt_nop, + .release_pte = paravirt_nop, + .release_pmd = paravirt_nop, + .release_pud = paravirt_nop, .set_pte = native_set_pte, .set_pte_at = native_set_pte_at, diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 19c9386ac118..1791a751a772 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -8,6 +8,7 @@ #include <asm/apic.h> #include <asm/desc.h> #include <asm/hpet.h> +#include <asm/pgtable.h> #include <asm/reboot_fixups.h> #include <asm/reboot.h> @@ -15,7 +16,6 @@ # include <linux/dmi.h> # include <linux/ctype.h> # include <linux/mc146818rtc.h> -# include <asm/pgtable.h> #else # include <asm/iommu.h> #endif @@ -275,7 +275,7 @@ void machine_real_restart(unsigned char *code, int length) /* Remap the kernel at virtual address zero, as well as offset zero from the kernel segment. This assumes the kernel segment starts at virtual address PAGE_OFFSET. */ - memcpy(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, + memcpy(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY, sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS); /* diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index ade371f9663a..eef79e84145f 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1039,8 +1039,8 @@ int __cpuinit native_cpu_up(unsigned int cpu) #ifdef CONFIG_X86_32 /* init low mem mapping */ - clone_pgd_range(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, - min_t(unsigned long, KERNEL_PGD_PTRS, USER_PGD_PTRS)); + clone_pgd_range(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY, + min_t(unsigned long, KERNEL_PGD_PTRS, KERNEL_PGD_BOUNDARY)); flush_tlb_all(); #endif diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c index 12affe1f9bce..956f38927aa7 100644 --- a/arch/x86/kernel/vmi_32.c +++ b/arch/x86/kernel/vmi_32.c @@ -320,7 +320,7 @@ static void check_zeroed_page(u32 pfn, int type, struct page *page) * pdes need to be zeroed. */ if (type & VMI_PAGE_CLONE) - limit = USER_PTRS_PER_PGD; + limit = KERNEL_PGD_BOUNDARY; for (i = 0; i < limit; i++) BUG_ON(ptr[i]); } @@ -392,13 +392,13 @@ static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type) } #endif -static void vmi_allocate_pt(struct mm_struct *mm, u32 pfn) +static void vmi_allocate_pte(struct mm_struct *mm, u32 pfn) { vmi_set_page_type(pfn, VMI_PAGE_L1); vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0); } -static void vmi_allocate_pd(struct mm_struct *mm, u32 pfn) +static void vmi_allocate_pmd(struct mm_struct *mm, u32 pfn) { /* * This call comes in very early, before mem_map is setup. @@ -409,20 +409,20 @@ static void vmi_allocate_pd(struct mm_struct *mm, u32 pfn) vmi_ops.allocate_page(pfn, VMI_PAGE_L2, 0, 0, 0); } -static void vmi_allocate_pd_clone(u32 pfn, u32 clonepfn, u32 start, u32 count) +static void vmi_allocate_pmd_clone(u32 pfn, u32 clonepfn, u32 start, u32 count) { vmi_set_page_type(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE); vmi_check_page_type(clonepfn, VMI_PAGE_L2); vmi_ops.allocate_page(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE, clonepfn, start, count); } -static void vmi_release_pt(u32 pfn) +static void vmi_release_pte(u32 pfn) { vmi_ops.release_page(pfn, VMI_PAGE_L1); vmi_set_page_type(pfn, VMI_PAGE_NORMAL); } -static void vmi_release_pd(u32 pfn) +static void vmi_release_pmd(u32 pfn) { vmi_ops.release_page(pfn, VMI_PAGE_L2); vmi_set_page_type(pfn, VMI_PAGE_NORMAL); @@ -871,15 +871,15 @@ static inline int __init activate_vmi(void) vmi_ops.allocate_page = vmi_get_function(VMI_CALL_AllocatePage); if (vmi_ops.allocate_page) { - pv_mmu_ops.alloc_pt = vmi_allocate_pt; - pv_mmu_ops.alloc_pd = vmi_allocate_pd; - pv_mmu_ops.alloc_pd_clone = vmi_allocate_pd_clone; + pv_mmu_ops.alloc_pte = vmi_allocate_pte; + pv_mmu_ops.alloc_pmd = vmi_allocate_pmd; + pv_mmu_ops.alloc_pmd_clone = vmi_allocate_pmd_clone; } vmi_ops.release_page = vmi_get_function(VMI_CALL_ReleasePage); if (vmi_ops.release_page) { - pv_mmu_ops.release_pt = vmi_release_pt; - pv_mmu_ops.release_pd = vmi_release_pd; + pv_mmu_ops.release_pte = vmi_release_pte; + pv_mmu_ops.release_pmd = vmi_release_pmd; } /* Set linear is needed in all cases */ diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index d05722121d24..6e2c4efce0ef 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -543,8 +543,8 @@ static void __init do_boot_cpu(__u8 cpu) hijack_source.idt.Offset, stack_start.sp)); /* init lowmem identity mapping */ - clone_pgd_range(swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, - min_t(unsigned long, KERNEL_PGD_PTRS, USER_PGD_PTRS)); + clone_pgd_range(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY, + min_t(unsigned long, KERNEL_PGD_PTRS, KERNEL_PGD_BOUNDARY)); flush_tlb_all(); if (quad_boot) { diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 20941d2954e2..b7b3e4c7cfc9 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -1,5 +1,5 @@ obj-y := init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \ - pat.o + pat.o pgtable.o obj-$(CONFIG_X86_32) += pgtable_32.o diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 9ec62da85fd7..08aa1878fad4 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -71,7 +71,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); - paravirt_alloc_pd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); + paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); pud = pud_offset(pgd, 0); BUG_ON(pmd_table != pmd_offset(pud, 0)); @@ -100,7 +100,7 @@ static pte_t * __init one_page_table_init(pmd_t *pmd) (pte_t *)alloc_bootmem_low_pages(PAGE_SIZE); } - paravirt_alloc_pt(&init_mm, __pa(page_table) >> PAGE_SHIFT); + paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT); set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); BUG_ON(page_table != pte_offset_kernel(pmd, 0)); } @@ -365,7 +365,7 @@ void __init native_pagetable_setup_start(pgd_t *base) pte_clear(NULL, va, pte); } - paravirt_alloc_pd(&init_mm, __pa(base) >> PAGE_SHIFT); + paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT); } void __init native_pagetable_setup_done(pgd_t *base) @@ -457,7 +457,7 @@ void zap_low_mappings(void) * Note that "pgd_clear()" doesn't do it for * us, because pgd_clear() is a no-op on i386. */ - for (i = 0; i < USER_PTRS_PER_PGD; i++) { + for (i = 0; i < KERNEL_PGD_BOUNDARY; i++) { #ifdef CONFIG_X86_PAE set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page))); #else diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 1ff7906a9a4d..b798e7b92b17 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -135,7 +135,7 @@ static __init void *spp_getpage(void) return ptr; } -static __init void +static void set_pte_phys(unsigned long vaddr, unsigned long phys, pgprot_t prot) { pgd_t *pgd; @@ -173,7 +173,7 @@ set_pte_phys(unsigned long vaddr, unsigned long phys, pgprot_t prot) new_pte = pfn_pte(phys >> PAGE_SHIFT, prot); pte = pte_offset_kernel(pmd, vaddr); - if (!pte_none(*pte) && + if (!pte_none(*pte) && pte_val(new_pte) && pte_val(*pte) != (pte_val(new_pte) & __supported_pte_mask)) pte_ERROR(*pte); set_pte(pte, new_pte); @@ -214,8 +214,7 @@ void __init cleanup_highmap(void) } /* NOTE: this is meant to be run only at boot */ -void __init -__set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) +void __set_fixmap(enum fixed_addresses idx, unsigned long phys, pgprot_t prot) { unsigned long address = __fix_to_virt(idx); diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 3a4baf95e24d..36a3f7ded626 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -407,7 +407,7 @@ void __init early_ioremap_clear(void) pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)); pmd_clear(pmd); - paravirt_release_pt(__pa(bm_pte) >> PAGE_SHIFT); + paravirt_release_pte(__pa(bm_pte) >> PAGE_SHIFT); __flush_tlb_all(); } diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index c29ebd037254..bd5e05c654dc 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -483,9 +483,7 @@ static int split_large_page(pte_t *kpte, unsigned long address) goto out_unlock; pbase = (pte_t *)page_address(base); -#ifdef CONFIG_X86_32 - paravirt_alloc_pt(&init_mm, page_to_pfn(base)); -#endif + paravirt_alloc_pte(&init_mm, page_to_pfn(base)); ref_prot = pte_pgprot(pte_clrhuge(*kpte)); #ifdef CONFIG_X86_64 diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c new file mode 100644 index 000000000000..50159764f694 --- /dev/null +++ b/arch/x86/mm/pgtable.c @@ -0,0 +1,276 @@ +#include <linux/mm.h> +#include <asm/pgalloc.h> +#include <asm/pgtable.h> +#include <asm/tlb.h> + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + return (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); +} + +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *pte; + +#ifdef CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT|__GFP_ZERO, 0); +#else + pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0); +#endif + if (pte) + pgtable_page_ctor(pte); + return pte; +} + +void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte) +{ + pgtable_page_dtor(pte); + paravirt_release_pte(page_to_pfn(pte)); + tlb_remove_page(tlb, pte); +} + +#if PAGETABLE_LEVELS > 2 +void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd) +{ + paravirt_release_pmd(__pa(pmd) >> PAGE_SHIFT); + tlb_remove_page(tlb, virt_to_page(pmd)); +} + +#if PAGETABLE_LEVELS > 3 +void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pud) +{ + paravirt_release_pud(__pa(pud) >> PAGE_SHIFT); + tlb_remove_page(tlb, virt_to_page(pud)); +} +#endif /* PAGETABLE_LEVELS > 3 */ +#endif /* PAGETABLE_LEVELS > 2 */ + +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + + list_add(&page->lru, &pgd_list); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + + list_del(&page->lru); +} + +#define UNSHARED_PTRS_PER_PGD \ + (SHARED_KERNEL_PMD ? KERNEL_PGD_BOUNDARY : PTRS_PER_PGD) + +static void pgd_ctor(void *p) +{ + pgd_t *pgd = p; + unsigned long flags; + + /* Clear usermode parts of PGD */ + memset(pgd, 0, KERNEL_PGD_BOUNDARY*sizeof(pgd_t)); + + spin_lock_irqsave(&pgd_lock, flags); + + /* If the pgd points to a shared pagetable level (either the + ptes in non-PAE, or shared PMD in PAE), then just copy the + references from swapper_pg_dir. */ + if (PAGETABLE_LEVELS == 2 || + (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD) || + PAGETABLE_LEVELS == 4) { + clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY, + swapper_pg_dir + KERNEL_PGD_BOUNDARY, + KERNEL_PGD_PTRS); + paravirt_alloc_pmd_clone(__pa(pgd) >> PAGE_SHIFT, + __pa(swapper_pg_dir) >> PAGE_SHIFT, + KERNEL_PGD_BOUNDARY, + KERNEL_PGD_PTRS); + } + + /* list required to sync kernel mapping updates */ + if (!SHARED_KERNEL_PMD) + pgd_list_add(pgd); + + spin_unlock_irqrestore(&pgd_lock, flags); +} + +static void pgd_dtor(void *pgd) +{ + unsigned long flags; /* can be called from interrupt context */ + + if (SHARED_KERNEL_PMD) + return; + + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); +} + +/* + * List of all pgd's needed for non-PAE so it can invalidate entries + * in both cached and uncached pgd's; not needed for PAE since the + * kernel pmd is shared. If PAE were not to share the pmd a similar + * tactic would be needed. This is essentially codepath-based locking + * against pageattr.c; it is the unique case in which a valid change + * of kernel pagetables can't be lazily synchronized by vmalloc faults. + * vmalloc faults work because attached pagetables are never freed. + * -- wli + */ + +#ifdef CONFIG_X86_PAE +/* + * Mop up any pmd pages which may still be attached to the pgd. + * Normally they will be freed by munmap/exit_mmap, but any pmd we + * preallocate which never got a corresponding vma will need to be + * freed manually. + */ +static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp) +{ + int i; + + for(i = 0; i < UNSHARED_PTRS_PER_PGD; i++) { + pgd_t pgd = pgdp[i]; + + if (pgd_val(pgd) != 0) { + pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd); + + pgdp[i] = native_make_pgd(0); + + paravirt_release_pmd(pgd_val(pgd) >> PAGE_SHIFT); + pmd_free(mm, pmd); + } + } +} + +/* + * In PAE mode, we need to do a cr3 reload (=tlb flush) when + * updating the top-level pagetable entries to guarantee the + * processor notices the update. Since this is expensive, and + * all 4 top-level entries are used almost immediately in a + * new process's life, we just pre-populate them here. + * + * Also, if we're in a paravirt environment where the kernel pmd is + * not shared between pagetables (!SHARED_KERNEL_PMDS), we allocate + * and initialize the kernel pmds here. + */ +static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd) +{ + pud_t *pud; + unsigned long addr; + int i; + + pud = pud_offset(pgd, 0); + for (addr = i = 0; i < UNSHARED_PTRS_PER_PGD; + i++, pud++, addr += PUD_SIZE) { + pmd_t *pmd = pmd_alloc_one(mm, addr); + + if (!pmd) { + pgd_mop_up_pmds(mm, pgd); + return 0; + } + + if (i >= KERNEL_PGD_BOUNDARY) + memcpy(pmd, (pmd_t *)pgd_page_vaddr(swapper_pg_dir[i]), + sizeof(pmd_t) * PTRS_PER_PMD); + + pud_populate(mm, pud, pmd); + } + + return 1; +} + +void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd) +{ + paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT); + + /* Note: almost everything apart from _PAGE_PRESENT is + reserved at the pmd (PDPT) level. */ + set_pud(pudp, __pud(__pa(pmd) | _PAGE_PRESENT)); + + /* + * According to Intel App note "TLBs, Paging-Structure Caches, + * and Their Invalidation", April 2007, document 317080-001, + * section 8.1: in PAE mode we explicitly have to flush the + * TLB via cr3 if the top-level pgd is changed... + */ + if (mm == current->active_mm) + write_cr3(read_cr3()); +} +#else /* !CONFIG_X86_PAE */ +/* No need to prepopulate any pagetable entries in non-PAE modes. */ +static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd) +{ + return 1; +} + +static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgd) +{ +} +#endif /* CONFIG_X86_PAE */ + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + + /* so that alloc_pmd can use it */ + mm->pgd = pgd; + if (pgd) + pgd_ctor(pgd); + + if (pgd && !pgd_prepopulate_pmd(mm, pgd)) { + pgd_dtor(pgd); + free_page((unsigned long)pgd); + pgd = NULL; + } + + return pgd; +} + +void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + pgd_mop_up_pmds(mm, pgd); + pgd_dtor(pgd); + free_page((unsigned long)pgd); +} + +int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty) +{ + int changed = !pte_same(*ptep, entry); + + if (changed && dirty) { + *ptep = entry; + pte_update_defer(vma->vm_mm, address, ptep); + flush_tlb_page(vma, address); + } + + return changed; +} + +int ptep_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + int ret = 0; + + if (pte_young(*ptep)) + ret = test_and_clear_bit(_PAGE_BIT_ACCESSED, + &ptep->pte); + + if (ret) + pte_update(vma->vm_mm, addr, ptep); + + return ret; +} + +int ptep_clear_flush_young(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep) +{ + int young; + + young = ptep_test_and_clear_young(vma, address, ptep); + if (young) + flush_tlb_page(vma, address); + + return young; +} diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c index 6fb9e7c6893f..9ee007be9142 100644 --- a/arch/x86/mm/pgtable_32.c +++ b/arch/x86/mm/pgtable_32.c @@ -173,210 +173,6 @@ void reserve_top_address(unsigned long reserve) __VMALLOC_RESERVE += reserve; } -pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) -{ - return (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); -} - -pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) -{ - struct page *pte; - -#ifdef CONFIG_HIGHPTE - pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT|__GFP_ZERO, 0); -#else - pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0); -#endif - if (pte) - pgtable_page_ctor(pte); - return pte; -} - -/* - * List of all pgd's needed for non-PAE so it can invalidate entries - * in both cached and uncached pgd's; not needed for PAE since the - * kernel pmd is shared. If PAE were not to share the pmd a similar - * tactic would be needed. This is essentially codepath-based locking - * against pageattr.c; it is the unique case in which a valid change - * of kernel pagetables can't be lazily synchronized by vmalloc faults. - * vmalloc faults work because attached pagetables are never freed. - * -- wli - */ -static inline void pgd_list_add(pgd_t *pgd) -{ - struct page *page = virt_to_page(pgd); - - list_add(&page->lru, &pgd_list); -} - -static inline void pgd_list_del(pgd_t *pgd) -{ - struct page *page = virt_to_page(pgd); - - list_del(&page->lru); -} - -#define UNSHARED_PTRS_PER_PGD \ - (SHARED_KERNEL_PMD ? USER_PTRS_PER_PGD : PTRS_PER_PGD) - -static void pgd_ctor(void *p) -{ - pgd_t *pgd = p; - unsigned long flags; - - /* Clear usermode parts of PGD */ - memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t)); - - spin_lock_irqsave(&pgd_lock, flags); - - /* If the pgd points to a shared pagetable level (either the - ptes in non-PAE, or shared PMD in PAE), then just copy the - references from swapper_pg_dir. */ - if (PAGETABLE_LEVELS == 2 || - (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD)) { - clone_pgd_range(pgd + USER_PTRS_PER_PGD, - swapper_pg_dir + USER_PTRS_PER_PGD, - KERNEL_PGD_PTRS); - paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT, - __pa(swapper_pg_dir) >> PAGE_SHIFT, - USER_PTRS_PER_PGD, - KERNEL_PGD_PTRS); - } - - /* list required to sync kernel mapping updates */ - if (!SHARED_KERNEL_PMD) - pgd_list_add(pgd); - - spin_unlock_irqrestore(&pgd_lock, flags); -} - -static void pgd_dtor(void *pgd) -{ - unsigned long flags; /* can be called from interrupt context */ - - if (SHARED_KERNEL_PMD) - return; - - spin_lock_irqsave(&pgd_lock, flags); - pgd_list_del(pgd); - spin_unlock_irqrestore(&pgd_lock, flags); -} - -#ifdef CONFIG_X86_PAE -/* - * Mop up any pmd pages which may still be attached to the pgd. - * Normally they will be freed by munmap/exit_mmap, but any pmd we - * preallocate which never got a corresponding vma will need to be - * freed manually. - */ -static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp) -{ - int i; - - for(i = 0; i < UNSHARED_PTRS_PER_PGD; i++) { - pgd_t pgd = pgdp[i]; - - if (pgd_val(pgd) != 0) { - pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd); - - pgdp[i] = native_make_pgd(0); - - paravirt_release_pd(pgd_val(pgd) >> PAGE_SHIFT); - pmd_free(mm, pmd); - } - } -} - -/* - * In PAE mode, we need to do a cr3 reload (=tlb flush) when - * updating the top-level pagetable entries to guarantee the - * processor notices the update. Since this is expensive, and - * all 4 top-level entries are used almost immediately in a - * new process's life, we just pre-populate them here. - * - * Also, if we're in a paravirt environment where the kernel pmd is - * not shared between pagetables (!SHARED_KERNEL_PMDS), we allocate - * and initialize the kernel pmds here. - */ -static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd) -{ - pud_t *pud; - unsigned long addr; - int i; - - pud = pud_offset(pgd, 0); - for (addr = i = 0; i < UNSHARED_PTRS_PER_PGD; - i++, pud++, addr += PUD_SIZE) { - pmd_t *pmd = pmd_alloc_one(mm, addr); - - if (!pmd) { - pgd_mop_up_pmds(mm, pgd); - return 0; - } - - if (i >= USER_PTRS_PER_PGD) - memcpy(pmd, (pmd_t *)pgd_page_vaddr(swapper_pg_dir[i]), - sizeof(pmd_t) * PTRS_PER_PMD); - - pud_populate(mm, pud, pmd); - } - - return 1; -} -#else /* !CONFIG_X86_PAE */ -/* No need to prepopulate any pagetable entries in non-PAE modes. */ -static int pgd_prepopulate_pmd(struct mm_struct *mm, pgd_t *pgd) -{ - return 1; -} - -static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp) -{ -} -#endif /* CONFIG_X86_PAE */ - -pgd_t *pgd_alloc(struct mm_struct *mm) -{ - pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); - - /* so that alloc_pd can use it */ - mm->pgd = pgd; - if (pgd) - pgd_ctor(pgd); - - if (pgd && !pgd_prepopulate_pmd(mm, pgd)) { - pgd_dtor(pgd); - free_page((unsigned long)pgd); - pgd = NULL; - } - - return pgd; -} - -void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - pgd_mop_up_pmds(mm, pgd); - pgd_dtor(pgd); - free_page((unsigned long)pgd); -} - -void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte) -{ - pgtable_page_dtor(pte); - paravirt_release_pt(page_to_pfn(pte)); - tlb_remove_page(tlb, pte); -} - -#ifdef CONFIG_X86_PAE - -void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd) -{ - paravirt_release_pd(__pa(pmd) >> PAGE_SHIFT); - tlb_remove_page(tlb, virt_to_page(pmd)); -} - -#endif - int pmd_bad(pmd_t pmd) { WARN_ON_ONCE(pmd_bad_v1(pmd) != pmd_bad_v2(pmd)); diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index 4d5f2649bee4..2e641be2737e 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -6,7 +6,7 @@ config XEN bool "Xen guest support" select PARAVIRT depends on X86_32 - depends on X86_CMPXCHG && X86_TSC && !NEED_MULTIPLE_NODES && !(X86_VISWS || X86_VOYAGER) + depends on X86_CMPXCHG && X86_TSC && !(X86_VISWS || X86_VOYAGER) help This is the Linux Xen port. Enabling this will allow the kernel to boot in a paravirtualized environment under the diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 343df246bd3e..3d8df981d5fd 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -1,4 +1,4 @@ -obj-y := enlighten.o setup.o features.o multicalls.o mmu.o \ - events.o time.o manage.o xen-asm.o +obj-y := enlighten.o setup.o multicalls.o mmu.o \ + time.o manage.o xen-asm.o grant-table.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index c0388220cf97..c8a56e457d61 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -155,7 +155,8 @@ static void xen_cpuid(unsigned int *ax, unsigned int *bx, if (*ax == 1) maskedx = ~((1 << X86_FEATURE_APIC) | /* disable APIC */ (1 << X86_FEATURE_ACPI) | /* disable ACPI */ - (1 << X86_FEATURE_SEP) | /* disable SEP */ + (1 << X86_FEATURE_MCE) | /* disable MCE */ + (1 << X86_FEATURE_MCA) | /* disable MCA */ (1 << X86_FEATURE_ACC)); /* thermal monitoring */ asm(XEN_EMULATE_PREFIX "cpuid" @@ -531,26 +532,37 @@ static void xen_apic_write(unsigned long reg, u32 val) static void xen_flush_tlb(void) { struct mmuext_op *op; - struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + struct multicall_space mcs; + + preempt_disable(); + + mcs = xen_mc_entry(sizeof(*op)); op = mcs.args; op->cmd = MMUEXT_TLB_FLUSH_LOCAL; MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); } static void xen_flush_tlb_single(unsigned long addr) { struct mmuext_op *op; - struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + struct multicall_space mcs; + + preempt_disable(); + mcs = xen_mc_entry(sizeof(*op)); op = mcs.args; op->cmd = MMUEXT_INVLPG_LOCAL; op->arg1.linear_addr = addr & PAGE_MASK; MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); } static void xen_flush_tlb_others(const cpumask_t *cpus, struct mm_struct *mm, @@ -655,15 +667,17 @@ static void xen_write_cr3(unsigned long cr3) /* Early in boot, while setting up the initial pagetable, assume everything is pinned. */ -static __init void xen_alloc_pt_init(struct mm_struct *mm, u32 pfn) +static __init void xen_alloc_pte_init(struct mm_struct *mm, u32 pfn) { +#ifdef CONFIG_FLATMEM BUG_ON(mem_map); /* should only be used early */ +#endif make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); } -/* Early release_pt assumes that all pts are pinned, since there's +/* Early release_pte assumes that all pts are pinned, since there's only init_mm and anything attached to that is pinned. */ -static void xen_release_pt_init(u32 pfn) +static void xen_release_pte_init(u32 pfn) { make_lowmem_page_readwrite(__va(PFN_PHYS(pfn))); } @@ -697,12 +711,12 @@ static void xen_alloc_ptpage(struct mm_struct *mm, u32 pfn, unsigned level) } } -static void xen_alloc_pt(struct mm_struct *mm, u32 pfn) +static void xen_alloc_pte(struct mm_struct *mm, u32 pfn) { xen_alloc_ptpage(mm, pfn, PT_PTE); } -static void xen_alloc_pd(struct mm_struct *mm, u32 pfn) +static void xen_alloc_pmd(struct mm_struct *mm, u32 pfn) { xen_alloc_ptpage(mm, pfn, PT_PMD); } @@ -722,12 +736,12 @@ static void xen_release_ptpage(u32 pfn, unsigned level) } } -static void xen_release_pt(u32 pfn) +static void xen_release_pte(u32 pfn) { xen_release_ptpage(pfn, PT_PTE); } -static void xen_release_pd(u32 pfn) +static void xen_release_pmd(u32 pfn) { xen_release_ptpage(pfn, PT_PMD); } @@ -849,10 +863,10 @@ static __init void xen_pagetable_setup_done(pgd_t *base) { /* This will work as long as patching hasn't happened yet (which it hasn't) */ - pv_mmu_ops.alloc_pt = xen_alloc_pt; - pv_mmu_ops.alloc_pd = xen_alloc_pd; - pv_mmu_ops.release_pt = xen_release_pt; - pv_mmu_ops.release_pd = xen_release_pd; + pv_mmu_ops.alloc_pte = xen_alloc_pte; + pv_mmu_ops.alloc_pmd = xen_alloc_pmd; + pv_mmu_ops.release_pte = xen_release_pte; + pv_mmu_ops.release_pmd = xen_release_pmd; pv_mmu_ops.set_pte = xen_set_pte; setup_shared_info(); @@ -994,7 +1008,7 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { .read_pmc = native_read_pmc, .iret = xen_iret, - .irq_enable_syscall_ret = NULL, /* never called */ + .irq_enable_syscall_ret = xen_sysexit, .load_tr_desc = paravirt_nop, .set_ldt = xen_set_ldt, @@ -1059,11 +1073,11 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = { .pte_update = paravirt_nop, .pte_update_defer = paravirt_nop, - .alloc_pt = xen_alloc_pt_init, - .release_pt = xen_release_pt_init, - .alloc_pd = xen_alloc_pt_init, - .alloc_pd_clone = paravirt_nop, - .release_pd = xen_release_pt_init, + .alloc_pte = xen_alloc_pte_init, + .release_pte = xen_release_pte_init, + .alloc_pmd = xen_alloc_pte_init, + .alloc_pmd_clone = paravirt_nop, + .release_pmd = xen_release_pte_init, #ifdef CONFIG_HIGHPTE .kmap_atomic_pte = xen_kmap_atomic_pte, diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c new file mode 100644 index 000000000000..49ba9b5224d1 --- /dev/null +++ b/arch/x86/xen/grant-table.c @@ -0,0 +1,91 @@ +/****************************************************************************** + * grant_table.c + * x86 specific part + * + * Granting foreign access to our memory reservation. + * + * Copyright (c) 2005-2006, Christopher Clark + * Copyright (c) 2004-2005, K A Fraser + * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp> + * VA Linux Systems Japan. Split out x86 specific part. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> + +#include <xen/interface/xen.h> +#include <xen/page.h> +#include <xen/grant_table.h> + +#include <asm/pgtable.h> + +static int map_pte_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +{ + unsigned long **frames = (unsigned long **)data; + + set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); + (*frames)++; + return 0; +} + +static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +{ + + set_pte_at(&init_mm, addr, pte, __pte(0)); + return 0; +} + +int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + struct grant_entry **__shared) +{ + int rc; + struct grant_entry *shared = *__shared; + + if (shared == NULL) { + struct vm_struct *area = + xen_alloc_vm_area(PAGE_SIZE * max_nr_gframes); + BUG_ON(area == NULL); + shared = area->addr; + *__shared = shared; + } + + rc = apply_to_page_range(&init_mm, (unsigned long)shared, + PAGE_SIZE * nr_gframes, + map_pte_fn, &frames); + return rc; +} + +void arch_gnttab_unmap_shared(struct grant_entry *shared, + unsigned long nr_gframes) +{ + apply_to_page_range(&init_mm, (unsigned long)shared, + PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); +} diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 2a054ef2a3da..6cbcf65609ad 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -156,6 +156,10 @@ void set_pte_mfn(unsigned long vaddr, unsigned long mfn, pgprot_t flags) void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval) { + /* updates to init_mm may be done without lock */ + if (mm == &init_mm) + preempt_disable(); + if (mm == current->mm || mm == &init_mm) { if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) { struct multicall_space mcs; @@ -163,14 +167,61 @@ void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, MULTI_update_va_mapping(mcs.mc, addr, pteval, 0); xen_mc_issue(PARAVIRT_LAZY_MMU); - return; + goto out; } else if (HYPERVISOR_update_va_mapping(addr, pteval, 0) == 0) - return; + goto out; } xen_set_pte(ptep, pteval); + +out: + if (mm == &init_mm) + preempt_enable(); +} + +pteval_t xen_pte_val(pte_t pte) +{ + pteval_t ret = pte.pte; + + if (ret & _PAGE_PRESENT) + ret = machine_to_phys(XMADDR(ret)).paddr | _PAGE_PRESENT; + + return ret; +} + +pgdval_t xen_pgd_val(pgd_t pgd) +{ + pgdval_t ret = pgd.pgd; + if (ret & _PAGE_PRESENT) + ret = machine_to_phys(XMADDR(ret)).paddr | _PAGE_PRESENT; + return ret; +} + +pte_t xen_make_pte(pteval_t pte) +{ + if (pte & _PAGE_PRESENT) { + pte = phys_to_machine(XPADDR(pte)).maddr; + pte &= ~(_PAGE_PCD | _PAGE_PWT); + } + + return (pte_t){ .pte = pte }; } +pgd_t xen_make_pgd(pgdval_t pgd) +{ + if (pgd & _PAGE_PRESENT) + pgd = phys_to_machine(XPADDR(pgd)).maddr; + + return (pgd_t){ pgd }; +} + +pmdval_t xen_pmd_val(pmd_t pmd) +{ + pmdval_t ret = native_pmd_val(pmd); + if (ret & _PAGE_PRESENT) + ret = machine_to_phys(XMADDR(ret)).paddr | _PAGE_PRESENT; + return ret; +} #ifdef CONFIG_X86_PAE void xen_set_pud(pud_t *ptr, pud_t val) { @@ -214,100 +265,18 @@ void xen_pmd_clear(pmd_t *pmdp) xen_set_pmd(pmdp, __pmd(0)); } -unsigned long long xen_pte_val(pte_t pte) +pmd_t xen_make_pmd(pmdval_t pmd) { - unsigned long long ret = 0; - - if (pte.pte_low) { - ret = ((unsigned long long)pte.pte_high << 32) | pte.pte_low; - ret = machine_to_phys(XMADDR(ret)).paddr | 1; - } - - return ret; -} - -unsigned long long xen_pmd_val(pmd_t pmd) -{ - unsigned long long ret = pmd.pmd; - if (ret) - ret = machine_to_phys(XMADDR(ret)).paddr | 1; - return ret; -} - -unsigned long long xen_pgd_val(pgd_t pgd) -{ - unsigned long long ret = pgd.pgd; - if (ret) - ret = machine_to_phys(XMADDR(ret)).paddr | 1; - return ret; -} - -pte_t xen_make_pte(unsigned long long pte) -{ - if (pte & _PAGE_PRESENT) { - pte = phys_to_machine(XPADDR(pte)).maddr; - pte &= ~(_PAGE_PCD | _PAGE_PWT); - } - - return (pte_t){ .pte = pte }; -} - -pmd_t xen_make_pmd(unsigned long long pmd) -{ - if (pmd & 1) + if (pmd & _PAGE_PRESENT) pmd = phys_to_machine(XPADDR(pmd)).maddr; - return (pmd_t){ pmd }; -} - -pgd_t xen_make_pgd(unsigned long long pgd) -{ - if (pgd & _PAGE_PRESENT) - pgd = phys_to_machine(XPADDR(pgd)).maddr; - - return (pgd_t){ pgd }; + return native_make_pmd(pmd); } #else /* !PAE */ void xen_set_pte(pte_t *ptep, pte_t pte) { *ptep = pte; } - -unsigned long xen_pte_val(pte_t pte) -{ - unsigned long ret = pte.pte_low; - - if (ret & _PAGE_PRESENT) - ret = machine_to_phys(XMADDR(ret)).paddr; - - return ret; -} - -unsigned long xen_pgd_val(pgd_t pgd) -{ - unsigned long ret = pgd.pgd; - if (ret) - ret = machine_to_phys(XMADDR(ret)).paddr | 1; - return ret; -} - -pte_t xen_make_pte(unsigned long pte) -{ - if (pte & _PAGE_PRESENT) { - pte = phys_to_machine(XPADDR(pte)).maddr; - pte &= ~(_PAGE_PCD | _PAGE_PWT); - } - - return (pte_t){ pte }; -} - -pgd_t xen_make_pgd(unsigned long pgd) -{ - if (pgd & _PAGE_PRESENT) - pgd = phys_to_machine(XPADDR(pgd)).maddr; - - return (pgd_t){ pgd }; -} #endif /* CONFIG_X86_PAE */ /* diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 2341492bf7a0..82517e4a752a 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -16,6 +16,7 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> +#include <xen/interface/callback.h> #include <xen/interface/physdev.h> #include <xen/features.h> @@ -68,6 +69,24 @@ static void __init fiddle_vdso(void) *mask |= 1 << VDSO_NOTE_NONEGSEG_BIT; } +void xen_enable_sysenter(void) +{ + int cpu = smp_processor_id(); + extern void xen_sysenter_target(void); + /* Mask events on entry, even though they get enabled immediately */ + static struct callback_register sysenter = { + .type = CALLBACKTYPE_sysenter, + .address = { __KERNEL_CS, (unsigned long)xen_sysenter_target }, + .flags = CALLBACKF_mask_events, + }; + + if (!boot_cpu_has(X86_FEATURE_SEP) || + HYPERVISOR_callback_op(CALLBACKOP_register, &sysenter) != 0) { + clear_cpu_cap(&cpu_data(cpu), X86_FEATURE_SEP); + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_SEP); + } +} + void __init xen_arch_setup(void) { struct physdev_set_iopl set_iopl; @@ -82,6 +101,8 @@ void __init xen_arch_setup(void) HYPERVISOR_set_callbacks(__KERNEL_CS, (unsigned long)xen_hypervisor_callback, __KERNEL_CS, (unsigned long)xen_failsafe_callback); + xen_enable_sysenter(); + set_iopl.iopl = 1; rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); if (rc != 0) diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index e340ff92f6b6..92dd3dbf3ffb 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -36,8 +36,9 @@ #include "mmu.h" static cpumask_t xen_cpu_initialized_map; -static DEFINE_PER_CPU(int, resched_irq); -static DEFINE_PER_CPU(int, callfunc_irq); +static DEFINE_PER_CPU(int, resched_irq) = -1; +static DEFINE_PER_CPU(int, callfunc_irq) = -1; +static DEFINE_PER_CPU(int, debug_irq) = -1; /* * Structure and data for smp_call_function(). This is designed to minimise @@ -72,6 +73,7 @@ static __cpuinit void cpu_bringup_and_idle(void) int cpu = smp_processor_id(); cpu_init(); + xen_enable_sysenter(); preempt_disable(); per_cpu(cpu_state, cpu) = CPU_ONLINE; @@ -88,9 +90,7 @@ static __cpuinit void cpu_bringup_and_idle(void) static int xen_smp_intr_init(unsigned int cpu) { int rc; - const char *resched_name, *callfunc_name; - - per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = -1; + const char *resched_name, *callfunc_name, *debug_name; resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu); rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR, @@ -114,6 +114,14 @@ static int xen_smp_intr_init(unsigned int cpu) goto fail; per_cpu(callfunc_irq, cpu) = rc; + debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu); + rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt, + IRQF_DISABLED | IRQF_PERCPU | IRQF_NOBALANCING, + debug_name, NULL); + if (rc < 0) + goto fail; + per_cpu(debug_irq, cpu) = rc; + return 0; fail: @@ -121,6 +129,8 @@ static int xen_smp_intr_init(unsigned int cpu) unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); if (per_cpu(callfunc_irq, cpu) >= 0) unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + if (per_cpu(debug_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); return rc; } diff --git a/arch/x86/xen/xen-asm.S b/arch/x86/xen/xen-asm.S index fe161ed4b01e..2497a30f41de 100644 --- a/arch/x86/xen/xen-asm.S +++ b/arch/x86/xen/xen-asm.S @@ -108,6 +108,20 @@ ENDPATCH(xen_restore_fl_direct) RELOC(xen_restore_fl_direct, 2b+1) /* + We can't use sysexit directly, because we're not running in ring0. + But we can easily fake it up using iret. Assuming xen_sysexit + is jumped to with a standard stack frame, we can just strip it + back to a standard iret frame and use iret. + */ +ENTRY(xen_sysexit) + movl PT_EAX(%esp), %eax /* Shouldn't be necessary? */ + orl $X86_EFLAGS_IF, PT_EFLAGS(%esp) + lea PT_EIP(%esp), %esp + + jmp xen_iret +ENDPROC(xen_sysexit) + +/* This is run where a normal iret would be run, with the same stack setup: 8: eflags 4: cs @@ -184,8 +198,12 @@ iret_restore_end: region is OK. */ je xen_hypervisor_callback - iret +1: iret xen_iret_end_crit: +.section __ex_table,"a" + .align 4 + .long 1b,iret_exc +.previous hyper_iret: /* put this out of line since its very rarely used */ @@ -219,9 +237,7 @@ hyper_iret: ds } SAVE_ALL state eax } : : - ebx } - ---------------- - return addr <- esp + ebx }<- esp ---------------- In order to deliver the nested exception properly, we need to shift @@ -236,10 +252,8 @@ hyper_iret: it's usermode state which we eventually need to restore. */ ENTRY(xen_iret_crit_fixup) - /* offsets +4 for return address */ - /* - Paranoia: Make sure we're really coming from userspace. + Paranoia: Make sure we're really coming from kernel space. One could imagine a case where userspace jumps into the critical range address, but just before the CPU delivers a GP, it decides to deliver an interrupt instead. Unlikely? @@ -248,32 +262,32 @@ ENTRY(xen_iret_crit_fixup) jump instruction itself, not the destination, but some virtual environments get this wrong. */ - movl PT_CS+4(%esp), %ecx + movl PT_CS(%esp), %ecx andl $SEGMENT_RPL_MASK, %ecx cmpl $USER_RPL, %ecx je 2f - lea PT_ORIG_EAX+4(%esp), %esi - lea PT_EFLAGS+4(%esp), %edi + lea PT_ORIG_EAX(%esp), %esi + lea PT_EFLAGS(%esp), %edi /* If eip is before iret_restore_end then stack hasn't been restored yet. */ cmp $iret_restore_end, %eax jae 1f - movl 0+4(%edi),%eax /* copy EAX */ - movl %eax, PT_EAX+4(%esp) + movl 0+4(%edi),%eax /* copy EAX (just above top of frame) */ + movl %eax, PT_EAX(%esp) lea ESP_OFFSET(%edi),%edi /* move dest up over saved regs */ /* set up the copy */ 1: std - mov $(PT_EIP+4) / 4, %ecx /* copy ret+saved regs up to orig_eax */ + mov $PT_EIP / 4, %ecx /* saved regs up to orig_eax */ rep movsl cld lea 4(%edi),%esp /* point esp to new frame */ -2: ret +2: jmp xen_do_upcall /* diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 956a491ea998..f1063ae08037 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -2,6 +2,8 @@ #define XEN_OPS_H #include <linux/init.h> +#include <linux/irqreturn.h> +#include <xen/xen-ops.h> /* These are code, but not functions. Defined in entry.S */ extern const char xen_hypervisor_callback[]; @@ -9,7 +11,6 @@ extern const char xen_failsafe_callback[]; void xen_copy_trap_info(struct trap_info *traps); -DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); DECLARE_PER_CPU(unsigned long, xen_cr3); DECLARE_PER_CPU(unsigned long, xen_current_cr3); @@ -19,6 +20,7 @@ extern struct shared_info *HYPERVISOR_shared_info; char * __init xen_memory_setup(void); void __init xen_arch_setup(void); void __init xen_init_IRQ(void); +void xen_enable_sysenter(void); void xen_setup_timer(int cpu); void xen_setup_cpu_clockevents(void); @@ -28,6 +30,8 @@ unsigned long xen_get_wallclock(void); int xen_set_wallclock(unsigned long time); unsigned long long xen_sched_clock(void); +irqreturn_t xen_debug_interrupt(int irq, void *dev_id); + bool xen_vcpu_stolen(int vcpu); void xen_mark_init_mm_pinned(void); @@ -64,4 +68,6 @@ DECL_ASM(unsigned long, xen_save_fl_direct, void); DECL_ASM(void, xen_restore_fl_direct, unsigned long); void xen_iret(void); +void xen_sysexit(void); + #endif /* XEN_OPS_H */ diff --git a/drivers/Kconfig b/drivers/Kconfig index 3a0e3549739f..80f0ec91e2cf 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -97,4 +97,6 @@ source "drivers/dca/Kconfig" source "drivers/auxdisplay/Kconfig" source "drivers/uio/Kconfig" + +source "drivers/xen/Kconfig" endmenu diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 9c6f3f99208d..d771da816d95 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -47,6 +47,7 @@ #include <xen/interface/grant_table.h> #include <xen/interface/io/blkif.h> +#include <xen/interface/io/protocols.h> #include <asm/xen/hypervisor.h> @@ -74,7 +75,6 @@ static struct block_device_operations xlvbd_block_fops; struct blkfront_info { struct xenbus_device *xbdev; - dev_t dev; struct gendisk *gd; int vdevice; blkif_vdev_t handle; @@ -88,6 +88,7 @@ struct blkfront_info struct blk_shadow shadow[BLK_RING_SIZE]; unsigned long shadow_free; int feature_barrier; + int is_ready; /** * The number of people holding this device open. We won't allow a @@ -614,6 +615,12 @@ again: message = "writing event-channel"; goto abort_transaction; } + err = xenbus_printf(xbt, dev->nodename, "protocol", "%s", + XEN_IO_PROTO_ABI_NATIVE); + if (err) { + message = "writing protocol"; + goto abort_transaction; + } err = xenbus_transaction_end(xbt, 0); if (err) { @@ -833,6 +840,8 @@ static void blkfront_connect(struct blkfront_info *info) spin_unlock_irq(&blkif_io_lock); add_disk(info->gd); + + info->is_ready = 1; } /** @@ -896,7 +905,7 @@ static void backend_changed(struct xenbus_device *dev, break; case XenbusStateClosing: - bd = bdget(info->dev); + bd = bdget_disk(info->gd, 0); if (bd == NULL) xenbus_dev_fatal(dev, -ENODEV, "bdget failed"); @@ -925,6 +934,13 @@ static int blkfront_remove(struct xenbus_device *dev) return 0; } +static int blkfront_is_ready(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->dev.driver_data; + + return info->is_ready; +} + static int blkif_open(struct inode *inode, struct file *filep) { struct blkfront_info *info = inode->i_bdev->bd_disk->private_data; @@ -971,6 +987,7 @@ static struct xenbus_driver blkfront = { .remove = blkfront_remove, .resume = blkfront_resume, .otherend_changed = backend_changed, + .is_ready = blkfront_is_ready, }; static int __init xlblk_init(void) @@ -998,3 +1015,5 @@ module_exit(xlblk_exit); MODULE_DESCRIPTION("Xen virtual block device frontend"); MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(XENVBD_MAJOR); +MODULE_ALIAS("xen:vbd"); +MODULE_ALIAS("xenblk"); diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index 6228fadacd38..9d19aec5820a 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2167,6 +2167,7 @@ static const struct file_operations dv1394_fops= /* * Export information about protocols/devices supported by this driver. */ +#ifdef MODULE static struct ieee1394_device_id dv1394_id_table[] = { { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, @@ -2177,6 +2178,7 @@ static struct ieee1394_device_id dv1394_id_table[] = { }; MODULE_DEVICE_TABLE(ieee1394, dv1394_id_table); +#endif /* MODULE */ static struct hpsb_protocol_driver dv1394_driver = { .name = "dv1394", diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index b94e55e6eaa5..b5de5f21ef78 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -123,6 +123,8 @@ struct hpsb_iso { /* how many times the buffer has overflowed or underflowed */ atomic_t overflows; + /* how many cycles were skipped for a given context */ + atomic_t skips; /* Current number of bytes lost in discarded packets */ int bytes_discarded; diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index 0690469fcecf..e509e13cb7a7 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -1723,6 +1723,8 @@ struct ohci_iso_xmit { struct dma_prog_region prog; struct ohci1394_iso_tasklet task; int task_active; + int last_cycle; + atomic_t skips; u32 ContextControlSet; u32 ContextControlClear; @@ -1759,6 +1761,8 @@ static int ohci_iso_xmit_init(struct hpsb_iso *iso) iso->hostdata = xmit; xmit->ohci = iso->host->hostdata; xmit->task_active = 0; + xmit->last_cycle = -1; + atomic_set(&iso->skips, 0); dma_prog_region_init(&xmit->prog); @@ -1856,6 +1860,26 @@ static void ohci_iso_xmit_task(unsigned long data) /* parse cycle */ cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF; + if (xmit->last_cycle > -1) { + int cycle_diff = cycle - xmit->last_cycle; + int skip; + + /* unwrap */ + if (cycle_diff < 0) { + cycle_diff += 8000; + if (cycle_diff < 0) + PRINT(KERN_ERR, "bogus cycle diff %d\n", + cycle_diff); + } + + skip = cycle_diff - 1; + if (skip > 0) { + DBGMSG("skipped %d cycles without packet loss", skip); + atomic_add(skip, &iso->skips); + } + } + xmit->last_cycle = cycle; + /* tell the subsystem the packet has gone out */ hpsb_iso_packet_sent(iso, cycle, event != 0x11); @@ -1943,6 +1967,16 @@ static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info prev->output_last.branchAddress = cpu_to_le32( dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3); + /* + * Link the skip address to this descriptor itself. This causes a + * context to skip a cycle whenever lost cycles or FIFO overruns occur, + * without dropping the data at that point the application should then + * decide whether this is an error condition or not. Some protocols + * can deal with this by dropping some rate-matching padding packets. + */ + next->output_more_immediate.branchAddress = + prev->output_last.branchAddress; + /* disable interrupt, unless required by the IRQ interval */ if (prev_i % iso->irq_interval) { prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 04e96ba56e09..ec2a0adbedb2 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -2356,13 +2356,16 @@ static void rawiso_activity_cb(struct hpsb_iso *iso) static void raw1394_iso_fill_status(struct hpsb_iso *iso, struct raw1394_iso_status *stat) { + int overflows = atomic_read(&iso->overflows); + int skips = atomic_read(&iso->skips); + stat->config.data_buf_size = iso->buf_size; stat->config.buf_packets = iso->buf_packets; stat->config.channel = iso->channel; stat->config.speed = iso->speed; stat->config.irq_interval = iso->irq_interval; stat->n_packets = hpsb_iso_n_ready(iso); - stat->overflows = atomic_read(&iso->overflows); + stat->overflows = ((skips & 0xFFFF) << 16) | ((overflows & 0xFFFF)); stat->xmit_cycle = iso->xmit_cycle; } @@ -2437,6 +2440,8 @@ static int raw1394_iso_get_status(struct file_info *fi, void __user * uaddr) /* reset overflow counter */ atomic_set(&iso->overflows, 0); + /* reset skip counter */ + atomic_set(&iso->skips, 0); return 0; } @@ -2935,6 +2940,7 @@ static int raw1394_release(struct inode *inode, struct file *file) /* * Export information about protocols/devices supported by this driver. */ +#ifdef MODULE static struct ieee1394_device_id raw1394_id_table[] = { { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, @@ -2956,6 +2962,7 @@ static struct ieee1394_device_id raw1394_id_table[] = { }; MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); +#endif /* MODULE */ static struct hpsb_protocol_driver raw1394_driver = { .name = "raw1394", diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index e03024eeeac1..e24772d336e1 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -1293,6 +1293,7 @@ static const struct file_operations video1394_fops= /* * Export information about protocols/devices supported by this driver. */ +#ifdef MODULE static struct ieee1394_device_id video1394_id_table[] = { { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, @@ -1313,6 +1314,7 @@ static struct ieee1394_device_id video1394_id_table[] = { }; MODULE_DEVICE_TABLE(ieee1394, video1394_id_table); +#endif /* MODULE */ static struct hpsb_protocol_driver video1394_driver = { .name = VIDEO1394_DRIVER_NAME, diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 9dea14db724c..5f9d860925a1 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -149,6 +149,15 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. +config XEN_KBDDEV_FRONTEND + tristate "Xen virtual keyboard and mouse support" + depends on XEN_FBDEV_FRONTEND + default y + help + This driver implements the front-end of the Xen virtual + keyboard and mouse device driver. It communicates with a back-end + in another domain. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 2ae87b19caa8..98c4f9a77876 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -23,3 +23,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o + +obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c new file mode 100644 index 000000000000..0f47f4697cdf --- /dev/null +++ b/drivers/input/xen-kbdfront.c @@ -0,0 +1,340 @@ +/* + * Xen para-virtual input device + * + * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> + * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> + * + * Based on linux/drivers/input/mouse/sermouse.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +/* + * TODO: + * + * Switch to grant tables together with xen-fbfront.c. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/input.h> +#include <asm/xen/hypervisor.h> +#include <xen/events.h> +#include <xen/page.h> +#include <xen/interface/io/fbif.h> +#include <xen/interface/io/kbdif.h> +#include <xen/xenbus.h> + +struct xenkbd_info { + struct input_dev *kbd; + struct input_dev *ptr; + struct xenkbd_page *page; + int irq; + struct xenbus_device *xbdev; + char phys[32]; +}; + +static int xenkbd_remove(struct xenbus_device *); +static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); +static void xenkbd_disconnect_backend(struct xenkbd_info *); + +/* + * Note: if you need to send out events, see xenfb_do_update() for how + * to do that. + */ + +static irqreturn_t input_handler(int rq, void *dev_id) +{ + struct xenkbd_info *info = dev_id; + struct xenkbd_page *page = info->page; + __u32 cons, prod; + + prod = page->in_prod; + if (prod == page->in_cons) + return IRQ_HANDLED; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->in_cons; cons != prod; cons++) { + union xenkbd_in_event *event; + struct input_dev *dev; + event = &XENKBD_IN_RING_REF(page, cons); + + dev = info->ptr; + switch (event->type) { + case XENKBD_TYPE_MOTION: + input_report_rel(dev, REL_X, event->motion.rel_x); + input_report_rel(dev, REL_Y, event->motion.rel_y); + break; + case XENKBD_TYPE_KEY: + dev = NULL; + if (test_bit(event->key.keycode, info->kbd->keybit)) + dev = info->kbd; + if (test_bit(event->key.keycode, info->ptr->keybit)) + dev = info->ptr; + if (dev) + input_report_key(dev, event->key.keycode, + event->key.pressed); + else + printk(KERN_WARNING + "xenkbd: unhandled keycode 0x%x\n", + event->key.keycode); + break; + case XENKBD_TYPE_POS: + input_report_abs(dev, ABS_X, event->pos.abs_x); + input_report_abs(dev, ABS_Y, event->pos.abs_y); + break; + } + if (dev) + input_sync(dev); + } + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_irq(info->irq); + + return IRQ_HANDLED; +} + +static int __devinit xenkbd_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int ret, i; + struct xenkbd_info *info; + struct input_dev *kbd, *ptr; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + dev->dev.driver_data = info; + info->xbdev = dev; + info->irq = -1; + snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); + + info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!info->page) + goto error_nomem; + + /* keyboard */ + kbd = input_allocate_device(); + if (!kbd) + goto error_nomem; + kbd->name = "Xen Virtual Keyboard"; + kbd->phys = info->phys; + kbd->id.bustype = BUS_PCI; + kbd->id.vendor = 0x5853; + kbd->id.product = 0xffff; + kbd->evbit[0] = BIT(EV_KEY); + for (i = KEY_ESC; i < KEY_UNKNOWN; i++) + set_bit(i, kbd->keybit); + for (i = KEY_OK; i < KEY_MAX; i++) + set_bit(i, kbd->keybit); + + ret = input_register_device(kbd); + if (ret) { + input_free_device(kbd); + xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); + goto error; + } + info->kbd = kbd; + + /* pointing device */ + ptr = input_allocate_device(); + if (!ptr) + goto error_nomem; + ptr->name = "Xen Virtual Pointer"; + ptr->phys = info->phys; + ptr->id.bustype = BUS_PCI; + ptr->id.vendor = 0x5853; + ptr->id.product = 0xfffe; + ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + for (i = BTN_LEFT; i <= BTN_TASK; i++) + set_bit(i, ptr->keybit); + ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y); + input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + + ret = input_register_device(ptr); + if (ret) { + input_free_device(ptr); + xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); + goto error; + } + info->ptr = ptr; + + ret = xenkbd_connect_backend(dev, info); + if (ret < 0) + goto error; + + return 0; + + error_nomem: + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + error: + xenkbd_remove(dev); + return ret; +} + +static int xenkbd_resume(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev->dev.driver_data; + + xenkbd_disconnect_backend(info); + memset(info->page, 0, PAGE_SIZE); + return xenkbd_connect_backend(dev, info); +} + +static int xenkbd_remove(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev->dev.driver_data; + + xenkbd_disconnect_backend(info); + if (info->kbd) + input_unregister_device(info->kbd); + if (info->ptr) + input_unregister_device(info->ptr); + free_page((unsigned long)info->page); + kfree(info); + return 0; +} + +static int xenkbd_connect_backend(struct xenbus_device *dev, + struct xenkbd_info *info) +{ + int ret, evtchn; + struct xenbus_transaction xbt; + + ret = xenbus_alloc_evtchn(dev, &evtchn); + if (ret) + return ret; + ret = bind_evtchn_to_irqhandler(evtchn, input_handler, + 0, dev->devicetype, info); + if (ret < 0) { + xenbus_free_evtchn(dev, evtchn); + xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); + return ret; + } + info->irq = ret; + + again: + ret = xenbus_transaction_start(&xbt); + if (ret) { + xenbus_dev_fatal(dev, ret, "starting transaction"); + return ret; + } + ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + virt_to_mfn(info->page)); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + evtchn); + if (ret) + goto error_xenbus; + ret = xenbus_transaction_end(xbt, 0); + if (ret) { + if (ret == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, ret, "completing transaction"); + return ret; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + return 0; + + error_xenbus: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, ret, "writing xenstore"); + return ret; +} + +static void xenkbd_disconnect_backend(struct xenkbd_info *info) +{ + if (info->irq >= 0) + unbind_from_irqhandler(info->irq, info); + info->irq = -1; +} + +static void xenkbd_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct xenkbd_info *info = dev->dev.driver_data; + int ret, val; + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: +InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + printk(KERN_WARNING + "xenkbd: can't request abs-pointer"); + } + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateConnected: + /* + * Work around xenbus race condition: If backend goes + * through InitWait to Connected fast enough, we can + * get Connected twice here. + */ + if (dev->state != XenbusStateConnected) + goto InitWait; /* no InitWait seen yet, fudge it */ + break; + + case XenbusStateClosing: + xenbus_frontend_closed(dev); + break; + } +} + +static struct xenbus_device_id xenkbd_ids[] = { + { "vkbd" }, + { "" } +}; + +static struct xenbus_driver xenkbd = { + .name = "vkbd", + .owner = THIS_MODULE, + .ids = xenkbd_ids, + .probe = xenkbd_probe, + .remove = xenkbd_remove, + .resume = xenkbd_resume, + .otherend_changed = xenkbd_backend_changed, +}; + +static int __init xenkbd_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + /* Nothing to do if running in dom0. */ + if (is_initial_xendomain()) + return -ENODEV; + + return xenbus_register_frontend(&xenkbd); +} + +static void __exit xenkbd_cleanup(void) +{ + xenbus_unregister_driver(&xenkbd); +} + +module_init(xenkbd_init); +module_exit(xenkbd_cleanup); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index e8503341e3b1..eed06d068fd1 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -158,6 +158,12 @@ config MTD_OF_PARTS the partition map from the children of the flash node, as described in Documentation/powerpc/booting-without-of.txt. +config MTD_AR7_PARTS + tristate "TI AR7 partitioning support" + depends on MTD_PARTITIONS + ---help--- + TI AR7 partitioning support + comment "User Modules And Translation Layers" config MTD_CHAR diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 538e33d11d46..4b77335715f0 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o +obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o # 'Users' - code which presents functionality to userspace. diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c new file mode 100644 index 000000000000..ecf170b55c32 --- /dev/null +++ b/drivers/mtd/ar7part.c @@ -0,0 +1,151 @@ +/* + * Copyright © 2007 Eugene Konev <ejka@openwrt.org> + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * TI AR7 flash partition table. + * Based on ar7 map by Felix Fietkau <nbd@openwrt.org> + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/bootmem.h> +#include <linux/magic.h> + +#define AR7_PARTS 4 +#define ROOT_OFFSET 0xe0000 + +#define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42) +#define LOADER_MAGIC2 le32_to_cpu(0xfeed1281) + +#ifndef SQUASHFS_MAGIC +#define SQUASHFS_MAGIC 0x73717368 +#endif + +struct ar7_bin_rec { + unsigned int checksum; + unsigned int length; + unsigned int address; +}; + +static struct mtd_partition ar7_parts[AR7_PARTS]; + +static int create_mtd_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long origin) +{ + struct ar7_bin_rec header; + unsigned int offset; + size_t len; + unsigned int pre_size = master->erasesize, post_size = 0; + unsigned int root_offset = ROOT_OFFSET; + + int retries = 10; + + ar7_parts[0].name = "loader"; + ar7_parts[0].offset = 0; + ar7_parts[0].size = master->erasesize; + ar7_parts[0].mask_flags = MTD_WRITEABLE; + + ar7_parts[1].name = "config"; + ar7_parts[1].offset = 0; + ar7_parts[1].size = master->erasesize; + ar7_parts[1].mask_flags = 0; + + do { /* Try 10 blocks starting from master->erasesize */ + offset = pre_size; + master->read(master, offset, + sizeof(header), &len, (uint8_t *)&header); + if (!strncmp((char *)&header, "TIENV0.8", 8)) + ar7_parts[1].offset = pre_size; + if (header.checksum == LOADER_MAGIC1) + break; + if (header.checksum == LOADER_MAGIC2) + break; + pre_size += master->erasesize; + } while (retries--); + + pre_size = offset; + + if (!ar7_parts[1].offset) { + ar7_parts[1].offset = master->size - master->erasesize; + post_size = master->erasesize; + } + + switch (header.checksum) { + case LOADER_MAGIC1: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (uint8_t *)&header); + } + root_offset = offset + sizeof(header) + 4; + break; + case LOADER_MAGIC2: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (uint8_t *)&header); + } + root_offset = offset + sizeof(header) + 4 + 0xff; + root_offset &= ~(uint32_t)0xff; + break; + default: + printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum); + break; + } + + master->read(master, root_offset, + sizeof(header), &len, (u8 *)&header); + if (header.checksum != SQUASHFS_MAGIC) { + root_offset += master->erasesize - 1; + root_offset &= ~(master->erasesize - 1); + } + + ar7_parts[2].name = "linux"; + ar7_parts[2].offset = pre_size; + ar7_parts[2].size = master->size - pre_size - post_size; + ar7_parts[2].mask_flags = 0; + + ar7_parts[3].name = "rootfs"; + ar7_parts[3].offset = root_offset; + ar7_parts[3].size = master->size - root_offset - post_size; + ar7_parts[3].mask_flags = 0; + + *pparts = ar7_parts; + return AR7_PARTS; +} + +static struct mtd_part_parser ar7_parser = { + .owner = THIS_MODULE, + .parse_fn = create_mtd_partitions, + .name = "ar7part", +}; + +static int __init ar7_parser_init(void) +{ + return register_mtd_parser(&ar7_parser); +} + +module_init(ar7_parser_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " + "Eugene Konev <ejka@openwrt.org>"); +MODULE_DESCRIPTION("MTD partitioning for TI AR7"); diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 0080452531d6..e812df607a5c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -384,7 +384,7 @@ read_pri_intelext(struct map_info *map, __u16 adr) if (extp_size > 4096) { printk(KERN_ERR "%s: cfi_pri_intelext is too fat\n", - __FUNCTION__); + __func__); return NULL; } goto again; @@ -619,6 +619,9 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, sizeof(struct cfi_intelext_blockinfo); } + if (!numparts) + numparts = 1; + /* Programming Region info */ if (extp->MinorVersion >= '4') { struct cfi_intelext_programming_regioninfo *prinfo; @@ -641,7 +644,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, if ((1 << partshift) < mtd->erasesize) { printk( KERN_ERR "%s: bad number of hw partitions (%d)\n", - __FUNCTION__, numparts); + __func__, numparts); return -EINVAL; } @@ -1071,10 +1074,10 @@ static int __xipram xip_wait_for_operation( chip->state = newstate; map_write(map, CMD(0xff), adr); (void) map_read(map, adr); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); spin_unlock(chip->mutex); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); cond_resched(); /* @@ -2013,7 +2016,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", - __FUNCTION__, ofs, len); + __func__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2023,7 +2026,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", - __FUNCTION__, ret); + __func__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2037,7 +2040,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", - __FUNCTION__, ofs, len); + __func__, ofs, len); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif @@ -2047,7 +2050,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", - __FUNCTION__, ret); + __func__, ret); cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, NULL); #endif diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 458d477614d6..f7fcc6389533 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -220,6 +220,28 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) mtd->flags |= MTD_POWERUP_LOCK; } +static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { + cfi->cfiq->EraseRegionInfo[0] |= 0x0040; + pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); + } +} + +static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { + cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; + pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); + } +} + static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG @@ -231,6 +253,10 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, + { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif @@ -723,10 +749,10 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, chip->erase_suspended = 1; map_write(map, CMD(0xf0), adr); (void) map_read(map, adr); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); spin_unlock(chip->mutex); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); cond_resched(); /* diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 492e2ab27420..1b720cc571f3 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -445,7 +445,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, retry: #ifdef DEBUG_CFI_FEATURES - printk("%s: chip->state[%d]\n", __FUNCTION__, chip->state); + printk("%s: chip->state[%d]\n", __func__, chip->state); #endif spin_lock_bh(chip->mutex); @@ -463,7 +463,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; #ifdef DEBUG_CFI_FEATURES - printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); + printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); #endif case FL_STATUS: @@ -591,7 +591,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ if (map_word_bitsset(map, status, CMD(0x3a))) { #ifdef DEBUG_CFI_FEATURES - printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); + printk("%s: 2 status[%lx]\n", __func__, status.x[0]); #endif /* clear status */ map_write(map, CMD(0x50), cmd_adr); @@ -625,9 +625,9 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, ofs = to - (chipnum << cfi->chipshift); #ifdef DEBUG_CFI_FEATURES - printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); - printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); - printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); + printk("%s: map_bankwidth(map)[%x]\n", __func__, map_bankwidth(map)); + printk("%s: chipnum[%x] wbufsize[%x]\n", __func__, chipnum, wbufsize); + printk("%s: ofs[%x] len[%x]\n", __func__, ofs, len); #endif /* Write buffer is worth it only if more than one word to write... */ @@ -893,7 +893,8 @@ retry: return ret; } -int cfi_staa_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +static int cfi_staa_erase_varsize(struct mtd_info *mtd, + struct erase_info *instr) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr, len; diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index f651b6ef1c5d..a4463a91ce31 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -39,7 +39,7 @@ struct mtd_info *cfi_probe(struct map_info *map); #define xip_allowed(base, map) \ do { \ (void) map_read(map, base); \ - asm volatile (".rep 8; nop; .endr"); \ + xip_iprefetch(); \ local_irq_enable(); \ } while (0) @@ -232,6 +232,11 @@ static int __xipram cfi_chip_setup(struct map_info *map, cfi->mfr = cfi_read_query16(map, base); cfi->id = cfi_read_query16(map, base + ofs_factor); + /* Get AMD/Spansion extended JEDEC ID */ + if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) + cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | + cfi_read_query(map, base + 0xf * ofs_factor); + /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); /* ... even if it's an Intel chip */ diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 2e51496c248e..72e0022a47bf 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -65,7 +65,7 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n #ifdef CONFIG_MTD_XIP (void) map_read(map, base); - asm volatile (".rep 8; nop; .endr"); + xip_iprefetch(); local_irq_enable(); #endif diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 4be51a86a85c..aa07575eb288 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -132,6 +132,8 @@ #define M29F800AB 0x0058 #define M29W800DT 0x00D7 #define M29W800DB 0x005B +#define M29W400DT 0x00EE +#define M29W400DB 0x00EF #define M29W160DT 0x22C4 #define M29W160DB 0x2249 #define M29W040B 0x00E3 @@ -160,6 +162,7 @@ #define SST49LF030A 0x001C #define SST49LF040A 0x0051 #define SST49LF080A 0x005B +#define SST36VF3203 0x7354 /* Toshiba */ #define TC58FVT160 0x00C2 @@ -1113,7 +1116,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", @@ -1125,7 +1128,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,32), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", @@ -1140,7 +1143,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,2), ERASEINFO(0x04000,1), } - }, { + }, { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", @@ -1218,7 +1221,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x40000,16), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF512, .name = "SST 39LF512", @@ -1230,7 +1233,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,16), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF010, .name = "SST 39LF010", @@ -1242,7 +1245,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,32), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST29EE020, .name = "SST 29EE020", @@ -1276,7 +1279,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,64), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF040, .name = "SST 39LF040", @@ -1288,7 +1291,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,128), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", @@ -1300,7 +1303,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,32), } - }, { + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", @@ -1412,6 +1415,18 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST36VF3203, + .name = "SST 36VF3203", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { + ERASEINFO(0x10000,64), + } + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", @@ -1426,7 +1441,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,15), } - }, { + }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", @@ -1456,6 +1471,36 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,15) } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29W400DT, + .name = "ST M29W400DT", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,7), + ERASEINFO(0x02000,1), + ERASEINFO(0x08000,2), + ERASEINFO(0x10000,1) + } + }, { + .mfr_id = MANUFACTURER_ST, + .dev_id = M29W400DB, + .name = "ST M29W400DB", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7) + } }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, @@ -1486,7 +1531,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,31) } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29W040B, .name = "ST M29W040B", @@ -1498,7 +1543,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW040, .name = "ST M50FW040", @@ -1510,7 +1555,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,8), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW080, .name = "ST M50FW080", @@ -1522,7 +1567,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x10000,16), } - }, { + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW016, .name = "ST M50FW016", diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index b44292abd9f7..e472a0e9de9d 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -119,7 +119,8 @@ static struct mtd_partition * newpart(char *s, char *p; name = ++s; - if ((p = strchr(name, delim)) == 0) + p = strchr(name, delim); + if (!p) { printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); return NULL; @@ -159,9 +160,10 @@ static struct mtd_partition * newpart(char *s, return NULL; } /* more partitions follow, parse them */ - if ((parts = newpart(s + 1, &s, num_parts, - this_part + 1, &extra_mem, extra_mem_size)) == 0) - return NULL; + parts = newpart(s + 1, &s, num_parts, this_part + 1, + &extra_mem, extra_mem_size); + if (!parts) + return NULL; } else { /* this is the last partition: allocate space for all */ @@ -308,9 +310,6 @@ static int parse_cmdline_partitions(struct mtd_info *master, struct cmdline_mtd_partition *part; char *mtd_id = master->name; - if(!cmdline) - return -EINVAL; - /* parse command line */ if (!cmdline_parsed) mtdpart_setup_real(cmdline); @@ -341,7 +340,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, return part->num_parts; } } - return -EINVAL; + return 0; } diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 811d56fd890f..35ed1103dbb2 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -77,6 +77,13 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. +config M25PXX_USE_FAST_READ + bool "Use FAST_READ OPCode allowing SPI CLK <= 50MHz" + depends on MTD_M25P80 + default y + help + This option enables FAST_READ access supported by ST M25Pxx. + config MTD_SLRAM tristate "Uncached system RAM" help diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index ad1880c67518..519d942e7940 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -305,7 +305,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) } list_add(&dev->list, &blkmtd_device_list); INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: "), + dev->mtd.name + strlen("block2mtd: "), dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; @@ -366,9 +366,9 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR("block2mtd: " fmt "\n", ## args); \ - return 0; \ +#define parse_err(fmt, args...) do { \ + ERROR(fmt, ## args); \ + return 0; \ } while (0) #ifndef MODULE @@ -473,7 +473,7 @@ static void __devexit block2mtd_exit(void) block2mtd_sync(&dev->mtd); del_mtd_device(&dev->mtd); INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: ")); + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 99fd210feaec..1d324e5c412d 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -275,7 +275,7 @@ static __u8 read8 (__u32 offset) { volatile __u8 *data = (__u8 *) (FLASH_OFFSET + offset); #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.2x\n", __func__, offset, *data); #endif return (*data); } @@ -284,7 +284,7 @@ static __u32 read32 (__u32 offset) { volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x -> 0x%.8x\n", __func__, offset, *data); #endif return (*data); } @@ -294,7 +294,7 @@ static void write32 (__u32 x,__u32 offset) volatile __u32 *data = (__u32 *) (FLASH_OFFSET + offset); *data = x; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,*data); + printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, *data); #endif } @@ -337,7 +337,7 @@ static inline int erase_block (__u32 offset) __u32 status; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x\n",__FUNCTION__,offset); + printk (KERN_DEBUG "%s(): 0x%.8x\n", __func__, offset); #endif /* erase and confirm */ @@ -371,7 +371,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) int i,first; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n",__FUNCTION__,instr->addr,instr->len); + printk (KERN_DEBUG "%s(addr = 0x%.8x, len = %d)\n", __func__, instr->addr, instr->len); #endif /* sanity checks */ @@ -442,7 +442,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char *buf) { #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) from,len); + printk (KERN_DEBUG "%s(from = 0x%.8x, len = %d)\n", __func__, (__u32)from, len); #endif /* sanity checks */ @@ -488,7 +488,7 @@ static inline int write_dword (__u32 offset,__u32 x) __u32 status; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n",__FUNCTION__,offset,x); + printk (KERN_DEBUG "%s(): 0x%.8x <- 0x%.8x\n", __func__, offset, x); #endif /* setup writing */ @@ -524,7 +524,7 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen int i,n; #ifdef LART_DEBUG - printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n",__FUNCTION__,(__u32) to,len); + printk (KERN_DEBUG "%s(to = 0x%.8x, len = %d)\n", __func__, (__u32)to, len); #endif *retlen = 0; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 98df5bcc02f3..25efd331ef28 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -33,7 +33,7 @@ /* Flash opcodes. */ #define OPCODE_WREN 0x06 /* Write enable */ #define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ @@ -52,7 +52,15 @@ /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_COUNT 100000 +#define CMD_SIZE 4 +#ifdef CONFIG_M25PXX_USE_FAST_READ +#define OPCODE_READ OPCODE_FAST_READ +#define FAST_READ_DUMMY_BYTE 1 +#else +#define OPCODE_READ OPCODE_NORM_READ +#define FAST_READ_DUMMY_BYTE 0 +#endif #ifdef CONFIG_MTD_PARTITIONS #define mtd_has_partitions() (1) @@ -68,7 +76,7 @@ struct m25p { struct mtd_info mtd; unsigned partitioned:1; u8 erase_opcode; - u8 command[4]; + u8 command[CMD_SIZE + FAST_READ_DUMMY_BYTE]; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -151,7 +159,7 @@ static int wait_till_ready(struct m25p *flash) static int erase_sector(struct m25p *flash, u32 offset) { DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", - flash->spi->dev.bus_id, __FUNCTION__, + flash->spi->dev.bus_id, __func__, flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ @@ -167,7 +175,7 @@ static int erase_sector(struct m25p *flash, u32 offset) flash->command[2] = offset >> 8; flash->command[3] = offset; - spi_write(flash->spi, flash->command, sizeof(flash->command)); + spi_write(flash->spi, flash->command, CMD_SIZE); return 0; } @@ -188,7 +196,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) u32 addr,len; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", - flash->spi->dev.bus_id, __FUNCTION__, "at", + flash->spi->dev.bus_id, __func__, "at", (u32)instr->addr, instr->len); /* sanity checks */ @@ -240,7 +248,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __FUNCTION__, "from", + flash->spi->dev.bus_id, __func__, "from", (u32)from, len); /* sanity checks */ @@ -253,8 +261,12 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_init(&m); memset(t, 0, (sizeof t)); + /* NOTE: + * OPCODE_FAST_READ (if available) is faster. + * Should add 1 byte DUMMY_BYTE. + */ t[0].tx_buf = flash->command; - t[0].len = sizeof(flash->command); + t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -287,7 +299,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE; mutex_unlock(&flash->lock); @@ -308,7 +320,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_message m; DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", - flash->spi->dev.bus_id, __FUNCTION__, "to", + flash->spi->dev.bus_id, __func__, "to", (u32)to, len); if (retlen) @@ -325,7 +337,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, memset(t, 0, (sizeof t)); t[0].tx_buf = flash->command; - t[0].len = sizeof(flash->command); + t[0].len = CMD_SIZE; spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; @@ -354,7 +366,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE; } else { u32 i; @@ -364,7 +376,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, t[1].len = page_size; spi_sync(flash->spi, &m); - *retlen = m.actual_length - sizeof(flash->command); + *retlen = m.actual_length - CMD_SIZE; /* write everything in PAGESIZE chunks */ for (i = page_size; i < len; i += page_size) { @@ -387,8 +399,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); if (retlen) - *retlen += m.actual_length - - sizeof(flash->command); + *retlen += m.actual_length - CMD_SIZE; } } @@ -435,6 +446,7 @@ static struct flash_info __devinitdata m25p_data [] = { { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, + { "at25df641", 0x1f4800, 64 * 1024, 128, SECT_4K, }, { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index e427c82d5f4c..bf485ff49457 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/mtdram.h> static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 180298b92a7a..5f960182da95 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -282,7 +282,7 @@ static int phram_setup(const char *val, struct kernel_param *kp) } module_param_call(phram, phram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(phram,"Memory region to map. \"map=<name>,<start>,<length>\""); +MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); static int __init init_phram(void) diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index c815d0f38577..4a79b187b568 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -136,8 +136,6 @@ typedef struct partition_t { #endif } partition_t; -void ftl_freepart(partition_t *part); - /* Partition state flags */ #define FTL_FORMATTED 0x01 @@ -1014,7 +1012,7 @@ static int ftl_writesect(struct mtd_blktrans_dev *dev, /*====================================================================*/ -void ftl_freepart(partition_t *part) +static void ftl_freepart(partition_t *part) { vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; @@ -1069,7 +1067,7 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(dev); } -struct mtd_blktrans_ops ftl_tr = { +static struct mtd_blktrans_ops ftl_tr = { .name = "ftl", .major = FTL_MAJOR, .part_bits = PART_BITS, diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index b8917beeb650..c551d2f0779c 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -41,11 +41,6 @@ char inftlmountrev[]="$Revision: 1.18 $"; -extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); -extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); - /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 12c253664eb2..1bd69aa9e22a 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -21,6 +21,9 @@ config MTD_PHYSMAP particular board as well as the bus width, either statically with config options or at run-time. + To compile this driver as a module, choose M here: the + module will be called physmap. + config MTD_PHYSMAP_START hex "Physical start address of flash mapping" depends on MTD_PHYSMAP diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index fc3b2672d1e2..1f492062f8ca 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -137,7 +137,7 @@ static int bast_flash_probe(struct platform_device *pdev) if (info->map.size > AREA_MAXSIZE) info->map.size = AREA_MAXSIZE; - pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__, + pr_debug("%s: area %08lx, size %ld\n", __func__, info->map.phys, info->map.size); info->area = request_mem_region(res->start, info->map.size, @@ -149,7 +149,7 @@ static int bast_flash_probe(struct platform_device *pdev) } info->map.virt = ioremap(res->start, info->map.size); - pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt); + pr_debug("%s: virt at %08x\n", __func__, (int)info->map.virt); if (info->map.virt == 0) { printk(KERN_ERR PFX "failed to ioremap() region\n"); @@ -223,3 +223,4 @@ module_exit(bast_flash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("BAST MTD Map driver"); +MODULE_ALIAS("platform:bast-nor"); diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 688ef495888a..59d8fb49270a 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -28,6 +28,9 @@ #define ROM_PROBE_STEP_SIZE (64*1024) +#define DEV_CK804 1 +#define DEV_MCP55 2 + struct ck804xrom_window { void __iomem *virt; unsigned long phys; @@ -45,8 +48,9 @@ struct ck804xrom_map_info { char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; - -/* The 2 bits controlling the window size are often set to allow reading +/* + * The following applies to ck804 only: + * The 2 bits controlling the window size are often set to allow reading * the BIOS, but too small to allow writing, since the lock registers are * 4MiB lower in the address space than the data. * @@ -58,10 +62,17 @@ struct ck804xrom_map_info { * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a * 64KiB window. * + * The following applies to mcp55 only: + * The 15 bits controlling the window size are distributed as follows: + * byte @0x88: bit 0..7 + * byte @0x8c: bit 8..15 + * word @0x90: bit 16..30 + * If all bits are enabled, we have a 16? MiB window + * Please set win_size_bits to 0x7fffffff if you actually want to do something */ static uint win_size_bits = 0; module_param(win_size_bits, uint, 0); -MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x88 byte, normally set by BIOS."); +MODULE_PARM_DESC(win_size_bits, "ROM window size bits override, normally set by BIOS."); static struct ck804xrom_window ck804xrom_window = { .maps = LIST_HEAD_INIT(ck804xrom_window.maps), @@ -102,10 +113,11 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window) static int __devinit ck804xrom_init_one (struct pci_dev *pdev, - const struct pci_device_id *ent) + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; + u16 word; struct ck804xrom_window *window = &ck804xrom_window; struct ck804xrom_map_info *map = NULL; unsigned long map_top; @@ -113,26 +125,42 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, /* Remember the pci dev I find the window in */ window->pdev = pci_dev_get(pdev); - /* Enable the selected rom window. This is often incorrectly - * set up by the BIOS, and the 4MiB offset for the lock registers - * requires the full 5MiB of window space. - * - * This 'write, then read' approach leaves the bits for - * other uses of the hardware info. - */ - pci_read_config_byte(pdev, 0x88, &byte); - pci_write_config_byte(pdev, 0x88, byte | win_size_bits ); - - - /* Assume the rom window is properly setup, and find it's size */ - pci_read_config_byte(pdev, 0x88, &byte); - - if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) - window->phys = 0xffb00000; /* 5MiB */ - else if ((byte & (1<<7)) == (1<<7)) - window->phys = 0xffc00000; /* 4MiB */ - else - window->phys = 0xffff0000; /* 64KiB */ + switch (ent->driver_data) { + case DEV_CK804: + /* Enable the selected rom window. This is often incorrectly + * set up by the BIOS, and the 4MiB offset for the lock registers + * requires the full 5MiB of window space. + * + * This 'write, then read' approach leaves the bits for + * other uses of the hardware info. + */ + pci_read_config_byte(pdev, 0x88, &byte); + pci_write_config_byte(pdev, 0x88, byte | win_size_bits ); + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x88, &byte); + + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) + window->phys = 0xffb00000; /* 5MiB */ + else if ((byte & (1<<7)) == (1<<7)) + window->phys = 0xffc00000; /* 4MiB */ + else + window->phys = 0xffff0000; /* 64KiB */ + break; + + case DEV_MCP55: + pci_read_config_byte(pdev, 0x88, &byte); + pci_write_config_byte(pdev, 0x88, byte | (win_size_bits & 0xff)); + + pci_read_config_byte(pdev, 0x8c, &byte); + pci_write_config_byte(pdev, 0x8c, byte | ((win_size_bits & 0xff00) >> 8)); + + pci_read_config_word(pdev, 0x90, &word); + pci_write_config_word(pdev, 0x90, word | ((win_size_bits & 0x7fff0000) >> 16)); + + window->phys = 0xff000000; /* 16MiB, hardcoded for now */ + break; + } window->size = 0xffffffffUL - window->phys + 1UL; @@ -303,8 +331,15 @@ static void __devexit ck804xrom_remove_one (struct pci_dev *pdev) } static struct pci_device_id ck804xrom_pci_tbl[] = { - { PCI_VENDOR_ID_NVIDIA, 0x0051, - PCI_ANY_ID, PCI_ANY_ID, }, /* nvidia ck804 */ + { PCI_VENDOR_ID_NVIDIA, 0x0051, PCI_ANY_ID, PCI_ANY_ID, DEV_CK804 }, + { PCI_VENDOR_ID_NVIDIA, 0x0360, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0361, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0362, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0363, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0364, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0365, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0366, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, + { PCI_VENDOR_ID_NVIDIA, 0x0367, PCI_ANY_ID, PCI_ANY_ID, DEV_MCP55 }, { 0, } }; @@ -332,7 +367,7 @@ static int __init init_ck804xrom(void) break; } if (pdev) { - retVal = ck804xrom_init_one(pdev, &ck804xrom_pci_tbl[0]); + retVal = ck804xrom_init_one(pdev, id); pci_dev_put(pdev); return retVal; } diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 6946d802e6f6..325c8880c437 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -190,6 +190,7 @@ static struct platform_driver armflash_driver = { .remove = armflash_remove, .driver = { .name = "armflash", + .owner = THIS_MODULE, }, }; @@ -209,3 +210,4 @@ module_exit(armflash_exit); MODULE_AUTHOR("ARM Ltd"); MODULE_DESCRIPTION("ARM Integrator CFI map driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:armflash"); diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c26488a1793a..c8396b8574c4 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -253,6 +253,7 @@ static struct platform_driver ixp2000_flash_driver = { .remove = ixp2000_flash_remove, .driver = { .name = "IXP2000-Flash", + .owner = THIS_MODULE, }, }; @@ -270,4 +271,4 @@ module_init(ixp2000_flash_init); module_exit(ixp2000_flash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); - +MODULE_ALIAS("platform:IXP2000-Flash"); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7a828e3e6446..01f19a4714b5 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -275,6 +275,7 @@ static struct platform_driver ixp4xx_flash_driver = { .remove = ixp4xx_flash_remove, .driver = { .name = "IXP4XX-Flash", + .owner = THIS_MODULE, }, }; @@ -295,3 +296,4 @@ module_exit(ixp4xx_flash_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems"); MODULE_AUTHOR("Deepak Saxena"); +MODULE_ALIAS("platform:IXP4XX-Flash"); diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index e8d9ae535673..240b0e2d095d 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -70,7 +70,7 @@ static void omap_set_vpp(struct map_info *map, int enable) } } -static int __devinit omapflash_probe(struct platform_device *pdev) +static int __init omapflash_probe(struct platform_device *pdev) { int err; struct omapflash_info *info; @@ -130,7 +130,7 @@ out_free_info: return err; } -static int __devexit omapflash_remove(struct platform_device *pdev) +static int __exit omapflash_remove(struct platform_device *pdev) { struct omapflash_info *info = platform_get_drvdata(pdev); @@ -152,16 +152,16 @@ static int __devexit omapflash_remove(struct platform_device *pdev) } static struct platform_driver omapflash_driver = { - .probe = omapflash_probe, - .remove = __devexit_p(omapflash_remove), + .remove = __exit_p(omapflash_remove), .driver = { .name = "omapflash", + .owner = THIS_MODULE, }, }; static int __init omapflash_init(void) { - return platform_driver_register(&omapflash_driver); + return platform_driver_probe(&omapflash_driver, omapflash_probe); } static void __exit omapflash_exit(void) @@ -174,4 +174,4 @@ module_exit(omapflash_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MTD NOR map driver for TI OMAP boards"); - +MODULE_ALIAS("platform:omapflash"); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index eaeb56a4070a..1912d968718b 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); #undef DEBUG #define DEBUG(n, format, arg...) \ if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ } #else diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index bc4649a17b9d..183255fcfdcb 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -242,6 +242,7 @@ static struct platform_driver physmap_flash_driver = { .shutdown = physmap_flash_shutdown, .driver = { .name = "physmap-flash", + .owner = THIS_MODULE, }, }; @@ -319,3 +320,10 @@ module_exit(physmap_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_DESCRIPTION("Generic configurable MTD map driver"); + +/* legacy platform drivers can't hotplug or coldplg */ +#ifndef PHYSMAP_COMPAT +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:physmap-flash"); +#endif + diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 894c0b271289..f0b10ca05029 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -47,6 +47,7 @@ struct platram_info { struct mtd_info *mtd; struct map_info map; struct mtd_partition *partitions; + bool free_partitions; struct resource *area; struct platdata_mtd_ram *pdata; }; @@ -98,7 +99,8 @@ static int platram_remove(struct platform_device *pdev) #ifdef CONFIG_MTD_PARTITIONS if (info->partitions) { del_mtd_partitions(info->mtd); - kfree(info->partitions); + if (info->free_partitions) + kfree(info->partitions); } #endif del_mtd_device(info->mtd); @@ -176,7 +178,8 @@ static int platram_probe(struct platform_device *pdev) info->map.phys = res->start; info->map.size = (res->end - res->start) + 1; - info->map.name = pdata->mapname != NULL ? pdata->mapname : (char *)pdev->name; + info->map.name = pdata->mapname != NULL ? + (char *)pdata->mapname : (char *)pdev->name; info->map.bankwidth = pdata->bankwidth; /* register our usage of the memory area */ @@ -203,9 +206,19 @@ static int platram_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "initialised map, probing for mtd\n"); - /* probe for the right mtd map driver */ + /* probe for the right mtd map driver + * supplied by the platform_data struct */ + + if (pdata->map_probes != 0) { + const char **map_probes = pdata->map_probes; + + for ( ; !info->mtd && *map_probes; map_probes++) + info->mtd = do_map_probe(*map_probes , &info->map); + } + /* fallback to map_ram */ + else + info->mtd = do_map_probe("map_ram", &info->map); - info->mtd = do_map_probe("map_ram" , &info->map); if (info->mtd == NULL) { dev_err(&pdev->dev, "failed to probe for map_ram\n"); err = -ENOMEM; @@ -220,19 +233,21 @@ static int platram_probe(struct platform_device *pdev) * to add this device whole */ #ifdef CONFIG_MTD_PARTITIONS - if (pdata->nr_partitions > 0) { - const char **probes = { NULL }; - - if (pdata->probes) - probes = (const char **)pdata->probes; - - err = parse_mtd_partitions(info->mtd, probes, + if (!pdata->nr_partitions) { + /* try to probe using the supplied probe type */ + if (pdata->probes) { + err = parse_mtd_partitions(info->mtd, pdata->probes, &info->partitions, 0); - if (err > 0) { - err = add_mtd_partitions(info->mtd, info->partitions, - err); + info->free_partitions = 1; + if (err > 0) + err = add_mtd_partitions(info->mtd, + info->partitions, err); } } + /* use the static mapping */ + else + err = add_mtd_partitions(info->mtd, pdata->partitions, + pdata->nr_partitions); #endif /* CONFIG_MTD_PARTITIONS */ if (add_mtd_device(info->mtd)) { @@ -240,7 +255,9 @@ static int platram_probe(struct platform_device *pdev) err = -ENOMEM; } - dev_info(&pdev->dev, "registered mtd device\n"); + if (!err) + dev_info(&pdev->dev, "registered mtd device\n"); + return err; exit_free: @@ -251,6 +268,9 @@ static int platram_probe(struct platform_device *pdev) /* device driver info */ +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:mtd-ram"); + static struct platform_driver platram_driver = { .probe = platram_probe, .remove = platram_remove, diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 02bde8c982ec..f43ba2815cbb 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -46,7 +46,7 @@ static struct mtd_partition **msp_parts; static struct map_info *msp_maps; static int fcnt; -#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__) +#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__) int __init init_msp_flash(void) { diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index f904e6bd02e0..c7d5a52a2d55 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -456,6 +456,7 @@ static struct platform_driver sa1100_mtd_driver = { .shutdown = sa1100_mtd_shutdown, .driver = { .name = "flash", + .owner = THIS_MODULE, }, }; @@ -475,3 +476,4 @@ module_exit(sa1100_mtd_exit); MODULE_AUTHOR("Nicolas Pitre"); MODULE_DESCRIPTION("SA1100 CFI map driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:flash"); diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 12fe53c0d2fc..917dc778f24e 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -92,7 +92,7 @@ int __init init_sharpsl(void) parts = sharpsl_partitions; nb_parts = ARRAY_SIZE(sharpsl_partitions); - printk(KERN_NOTICE "Using %s partision definition\n", part_type); + printk(KERN_NOTICE "Using %s partition definition\n", part_type); add_mtd_partitions(mymtd, parts, nb_parts); return 0; diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 37e4ded9b600..521734057314 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -124,7 +124,7 @@ int __init init_tqm_mtd(void) //request maximum flash size address space start_scan_addr = ioremap(flash_addr, flash_size); if (!start_scan_addr) { - printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __func__, flash_addr); return -EIO; } @@ -132,7 +132,7 @@ int __init init_tqm_mtd(void) if(mtd_size >= flash_size) break; - printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); + printk(KERN_INFO "%s: chip probing count %d\n", __func__, idx); map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); if(map_banks[idx] == NULL) { @@ -178,7 +178,7 @@ int __init init_tqm_mtd(void) mtd_size += mtd_banks[idx]->size; num_banks++; - printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __func__, num_banks, mtd_banks[idx]->name, mtd_banks[idx]->size); } } diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index d3cf05012b46..5a680e1e61f1 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -35,7 +35,7 @@ #define OOPS_PAGE_SIZE 4096 -struct mtdoops_context { +static struct mtdoops_context { int mtd_index; struct work_struct work_erase; struct work_struct work_write; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 959fb86cda01..5076faf9ca66 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -278,6 +278,54 @@ config MTD_NAND_AT91 help Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 processors. +choice + prompt "ECC management for NAND Flash / SmartMedia on AT91" + depends on MTD_NAND_AT91 + +config MTD_NAND_AT91_ECC_HW + bool "Hardware ECC" + depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 + help + Uses hardware ECC provided by the at91sam9260/at91sam9263 chip + instead of software ECC. + The hardware ECC controller is capable of single bit error + correction and 2-bit random detection per page. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition. + + If unsure, say Y + +config MTD_NAND_AT91_ECC_SOFT + bool "Software ECC" + help + Uses software ECC. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition. + +config MTD_NAND_AT91_ECC_NONE + bool "No ECC (testing only, DANGEROUS)" + depends on DEBUG_KERNEL + help + No ECC will be used. + It's not a good idea and it should be reserved for testing + purpose only. + + If unsure, say N + + endchoice + +endchoice + +config MTD_NAND_PXA3xx + bool "Support for NAND flash devices on PXA3xx" + depends on MTD_NAND && PXA3xx + help + This enables the driver for the NAND flash device found on + PXA3xx processors config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" @@ -330,4 +378,12 @@ config MTD_NAND_FSL_ELBC Enabling this option will enable you to use this to control external NAND devices. +config MTD_NAND_FSL_UPM + tristate "Support for NAND on Freescale UPM" + depends on MTD_NAND && OF_GPIO && (PPC_83xx || PPC_85xx) + select FSL_LBC + help + Enables support for NAND Flash chips wired onto Freescale PowerPC + processor localbus with User-Programmable Machine support. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 80d575eeee96..a6e74a46992a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -27,10 +27,12 @@ obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o +obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index c9fb2acf4056..414ceaecdb3a 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -9,6 +9,15 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * * 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. @@ -29,11 +38,59 @@ #include <asm/arch/board.h> #include <asm/arch/gpio.h> +#ifdef CONFIG_MTD_NAND_AT91_ECC_HW +#define hard_ecc 1 +#else +#define hard_ecc 0 +#endif + +#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE +#define no_ecc 1 +#else +#define no_ecc 0 +#endif + +/* Register access macros */ +#define ecc_readl(add, reg) \ + __raw_readl(add + AT91_ECC_##reg) +#define ecc_writel(add, reg, value) \ + __raw_writel((value), add + AT91_ECC_##reg) + +#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */ + +/* oob layout for large page size + * bad block info is on bytes 0 and 1 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout at91_oobinfo_large = { + .eccbytes = 4, + .eccpos = {60, 61, 62, 63}, + .oobfree = { + {2, 58} + }, +}; + +/* oob layout for small page size + * bad block info is on bytes 4 and 5 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout at91_oobinfo_small = { + .eccbytes = 4, + .eccpos = {0, 1, 2, 3}, + .oobfree = { + {6, 10} + }, +}; + struct at91_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; void __iomem *io_base; struct at91_nand_data *board; + struct device *dev; + void __iomem *ecc; }; /* @@ -44,6 +101,12 @@ static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *nand_chip = mtd->priv; struct at91_nand_host *host = nand_chip->priv; + if (host->board->enable_pin && (ctrl & NAND_CTRL_CHANGE)) { + if (ctrl & NAND_NCE) + at91_set_gpio_value(host->board->enable_pin, 0); + else + at91_set_gpio_value(host->board->enable_pin, 1); + } if (cmd == NAND_CMD_NONE) return; @@ -82,8 +145,217 @@ static void at91_nand_disable(struct at91_nand_host *host) at91_set_gpio_value(host->board->enable_pin, 1); } +/* + * write oob for small pages + */ +static int at91_nand_write_oob_512(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int len, pos, status = 0; + const uint8_t *bufpoi = chip->oob_poi; + + pos = eccsize + chunk; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; + +} + +/* + * read oob for small pages + */ +static int at91_nand_read_oob_512(struct mtd_info *mtd, + struct nand_chip *chip, int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/* + * Calculate HW ECC + * + * function called after a write + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int at91_nand_calculate(struct mtd_info *mtd, + const u_char *dat, unsigned char *ecc_code) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + uint32_t *eccpos = nand_chip->ecc.layout->eccpos; + unsigned int ecc_value; + + /* get the first 2 ECC bytes */ + ecc_value = ecc_readl(host->ecc, PR); + + ecc_code[eccpos[0]] = ecc_value & 0xFF; + ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; + + /* get the last 2 ECC bytes */ + ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; + + ecc_code[eccpos[2]] = ecc_value & 0xFF; + ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; + + return 0; +} + +/* + * HW ECC read page function + * + * mtd: mtd info structure + * chip: nand chip info structure + * buf: buffer to store read data + */ +static int at91_nand_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + uint8_t *ecc_pos; + int stat; + + /* read the page */ + chip->read_buf(mtd, p, eccsize); + + /* move to ECC position if needed */ + if (eccpos[0] != 0) { + /* This only works on large pages + * because the ECC controller waits for + * NAND_CMD_RNDOUTSTART after the + * NAND_CMD_RNDOUT. + * anyway, for small pages, the eccpos[0] == 0 + */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + eccpos[0], -1); + } + + /* the ECC controller needs to read the ECC just after the data */ + ecc_pos = oob + eccpos[0]; + chip->read_buf(mtd, ecc_pos, eccbytes); + + /* check if there's an error */ + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + /* get back to oob start (end of page) */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + + /* read the oob */ + chip->read_buf(mtd, oob, mtd->oobsize); + + return 0; +} + +/* + * HW ECC Correction + * + * function called after a read + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Detect and correct a 1 bit error for a page + */ +static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at91_nand_host *host = nand_chip->priv; + unsigned int ecc_status; + unsigned int ecc_word, ecc_bit; + + /* get the status from the Status Register */ + ecc_status = ecc_readl(host->ecc, SR); + + /* if there's no error */ + if (likely(!(ecc_status & AT91_ECC_RECERR))) + return 0; + + /* get error bit offset (4 bits) */ + ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; + /* get word address (12 bits) */ + ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; + ecc_word >>= 4; + + /* if there are multiple errors */ + if (ecc_status & AT91_ECC_MULERR) { + /* check if it is a freshly erased block + * (filled with 0xff) */ + if ((ecc_bit == AT91_ECC_BITADDR) + && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { + /* the block has just been erased, return OK */ + return 0; + } + /* it doesn't seems to be a freshly + * erased block. + * We can't correct so many errors */ + dev_dbg(host->dev, "at91_nand : multiple errors detected." + " Unable to correct.\n"); + return -EIO; + } + + /* if there's a single bit error : we can correct it */ + if (ecc_status & AT91_ECC_ECCERR) { + /* there's nothing much to do here. + * the bit error is on the ECC itself. + */ + dev_dbg(host->dev, "at91_nand : one bit error on ECC code." + " Nothing to correct\n"); + return 0; + } + + dev_dbg(host->dev, "at91_nand : one bit error on data." + " (word offset in the page :" + " 0x%x bit offset : 0x%x)\n", + ecc_word, ecc_bit); + /* correct the error */ + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* 16 bits words */ + ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); + } else { + /* 8 bits words */ + dat[ecc_word] ^= (1 << ecc_bit); + } + dev_dbg(host->dev, "at91_nand : error corrected\n"); + return 1; +} + +/* + * Enable HW ECC : unsused + */ +static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } + #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif /* @@ -94,6 +366,8 @@ static int __init at91_nand_probe(struct platform_device *pdev) struct at91_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; + struct resource *regs; + struct resource *mem; int res; #ifdef CONFIG_MTD_PARTITIONS @@ -108,8 +382,13 @@ static int __init at91_nand_probe(struct platform_device *pdev) return -ENOMEM; } - host->io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); + return -ENXIO; + } + + host->io_base = ioremap(mem->start, mem->end - mem->start + 1); if (host->io_base == NULL) { printk(KERN_ERR "at91_nand: ioremap failed\n"); kfree(host); @@ -119,6 +398,7 @@ static int __init at91_nand_probe(struct platform_device *pdev) mtd = &host->mtd; nand_chip = &host->nand_chip; host->board = pdev->dev.platform_data; + host->dev = &pdev->dev; nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -132,7 +412,32 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (host->board->rdy_pin) nand_chip->dev_ready = at91_nand_device_ready; + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!regs && hard_ecc) { + printk(KERN_ERR "at91_nand: can't get I/O resource " + "regs\nFalling back on software ECC\n"); + } + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + if (no_ecc) + nand_chip->ecc.mode = NAND_ECC_NONE; + if (hard_ecc && regs) { + host->ecc = ioremap(regs->start, regs->end - regs->start + 1); + if (host->ecc == NULL) { + printk(KERN_ERR "at91_nand: ioremap failed\n"); + res = -EIO; + goto err_ecc_ioremap; + } + nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; + nand_chip->ecc.calculate = at91_nand_calculate; + nand_chip->ecc.correct = at91_nand_correct; + nand_chip->ecc.hwctl = at91_nand_hwctl; + nand_chip->ecc.read_page = at91_nand_read_page; + nand_chip->ecc.bytes = 4; + nand_chip->ecc.prepad = 0; + nand_chip->ecc.postpad = 0; + } + nand_chip->chip_delay = 20; /* 20us command delay time */ if (host->board->bus_width_16) /* 16-bit bus width */ @@ -149,8 +454,53 @@ static int __init at91_nand_probe(struct platform_device *pdev) } } - /* Scan to find existance of the device */ - if (nand_scan(mtd, 1)) { + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1)) { + res = -ENXIO; + goto out; + } + + if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { + /* ECC is calculated for the whole page (1 step) */ + nand_chip->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand_chip->ecc.layout = &at91_oobinfo_small; + nand_chip->ecc.read_oob = at91_nand_read_oob_512; + nand_chip->ecc.write_oob = at91_nand_write_oob_512; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); + break; + case 1024: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); + break; + case 2048: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); + break; + case 4096: + nand_chip->ecc.layout = &at91_oobinfo_large; + ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.calculate = NULL; + nand_chip->ecc.correct = NULL; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = NULL; + nand_chip->ecc.postpad = 0; + nand_chip->ecc.prepad = 0; + nand_chip->ecc.bytes = 0; + break; + } + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { res = -ENXIO; goto out; } @@ -179,9 +529,15 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (!res) return res; +#ifdef CONFIG_MTD_PARTITIONS release: +#endif nand_release(mtd); + out: + iounmap(host->ecc); + +err_ecc_ioremap: at91_nand_disable(host); platform_set_drvdata(pdev, NULL); iounmap(host->io_base); @@ -202,6 +558,7 @@ static int __devexit at91_nand_remove(struct platform_device *pdev) at91_nand_disable(host); iounmap(host->io_base); + iounmap(host->ecc); kfree(host); return 0; @@ -233,4 +590,5 @@ module_exit(at91_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9"); +MODULE_ALIAS("platform:at91_nand"); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 747042ab094a..e87a57297328 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -1,6 +1,6 @@ /* linux/drivers/mtd/nand/bf5xx_nand.c * - * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2008 Analog Devices Inc. * http://blackfin.uclinux.org/ * Bryan Wu <bryan.wu@analog.com> * @@ -74,7 +74,7 @@ static int hardware_ecc = 1; static int hardware_ecc; #endif -static unsigned short bfin_nfc_pin_req[] = +static const unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, P_NAND_D0, @@ -581,12 +581,6 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) bfin_write_NFC_IRQSTAT(val); SSYNC(); - if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); - return -EFAULT; - } - /* DMA initialization */ if (bf5xx_nand_dma_init(info)) err = -ENXIO; @@ -654,6 +648,12 @@ static int bf5xx_nand_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "(%p)\n", pdev); + if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { + printk(KERN_ERR DRV_NAME + ": Requesting Peripherals failed\n"); + return -EFAULT; + } + if (!plat) { dev_err(&pdev->dev, "no platform specific information\n"); goto exit_error; @@ -803,3 +803,4 @@ module_exit(bf5xx_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 8dab69657b19..3370a800fd36 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -279,7 +279,7 @@ static int is_geode(void) #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 378b7aa63812..4b69aacdf5ca 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -184,11 +184,11 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) in_be32(&lbc->fbar), in_be32(&lbc->fpar), in_be32(&lbc->fbcr), priv->bank); + ctrl->irq_status = 0; /* execute special operation */ out_be32(&lbc->lsor, priv->bank); /* wait for FCM complete flag or timeout */ - ctrl->irq_status = 0; wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, FCM_TIMEOUT_MSECS * HZ/1000); ctrl->status = ctrl->irq_status; @@ -346,19 +346,20 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, ctrl->column = column; ctrl->oob = 0; - fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); - if (priv->page_size) { + fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_WB << FIR_OP3_SHIFT) | (FIR_OP_CW1 << FIR_OP4_SHIFT)); - - fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; } else { + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_CM2 << FIR_OP1_SHIFT) | @@ -480,7 +481,7 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) struct fsl_elbc_ctrl *ctrl = priv->ctrl; unsigned int bufsize = mtd->writesize + mtd->oobsize; - if (len < 0) { + if (len <= 0) { dev_err(ctrl->dev, "write_buf of %d bytes", len); ctrl->status = 0; return; @@ -495,6 +496,15 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) } memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&ctrl->addr[ctrl->index] + len - 1); + ctrl->index += len; } @@ -666,7 +676,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) /* adjust Option Register and ECC to match Flash page size */ if (mtd->writesize == 512) { priv->page_size = 0; - clrbits32(&lbc->bank[priv->bank].or, ~OR_FCM_PGS); + clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); } else if (mtd->writesize == 2048) { priv->page_size = 1; setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); @@ -687,11 +697,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return -1; } - /* The default u-boot configuration on MPC8313ERDB causes errors; - * more delay is needed. This should be safe for other boards - * as well. - */ - setbits32(&lbc->bank[priv->bank].or, 0x70); return 0; } @@ -779,6 +784,8 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) nand_release(&priv->mtd); + kfree(priv->mtd.name); + if (priv->vbase) iounmap(priv->vbase); @@ -839,6 +846,12 @@ static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, goto err; } + priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", res.start); + if (!priv->mtd.name) { + ret = -ENOMEM; + goto err; + } + ret = fsl_elbc_chip_init(priv); if (ret) goto err; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c new file mode 100644 index 000000000000..1ebfd87f00b4 --- /dev/null +++ b/drivers/mtd/nand/fsl_upm.c @@ -0,0 +1,291 @@ +/* + * Freescale UPM NAND driver. + * + * Copyright © 2007-2008 MontaVista Software, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/mtd.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <asm/fsl_lbc.h> + +struct fsl_upm_nand { + struct device *dev; + struct mtd_info mtd; + struct nand_chip chip; + int last_ctrl; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + + struct fsl_upm upm; + uint8_t upm_addr_offset; + uint8_t upm_cmd_offset; + void __iomem *io_base; + int rnb_gpio; + const uint32_t *wait_pattern; + const uint32_t *wait_write; + int chip_delay; +}; + +#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) + +static int fun_chip_ready(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (gpio_get_value(fun->rnb_gpio)) + return 1; + + dev_vdbg(fun->dev, "busy\n"); + return 0; +} + +static void fun_wait_rnb(struct fsl_upm_nand *fun) +{ + int cnt = 1000000; + + if (fun->rnb_gpio >= 0) { + while (--cnt && !fun_chip_ready(&fun->mtd)) + cpu_relax(); + } + + if (!cnt) + dev_err(fun->dev, "tired waiting for RNB\n"); +} + +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + if (!(ctrl & fun->last_ctrl)) { + fsl_upm_end_pattern(&fun->upm); + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); + } + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } + + fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd); + + if (fun->wait_pattern) + fun_wait_rnb(fun); +} + +static uint8_t fun_read_byte(struct mtd_info *mtd) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + + return in_8(fun->chip.IO_ADDR_R); +} + +static void fun_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) + buf[i] = in_8(fun->chip.IO_ADDR_R); +} + +static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + int i; + + for (i = 0; i < len; i++) { + out_8(fun->chip.IO_ADDR_W, buf[i]); + if (fun->wait_write) + fun_wait_rnb(fun); + } +} + +static int __devinit fun_chip_init(struct fsl_upm_nand *fun) +{ + int ret; +#ifdef CONFIG_MTD_PARTITIONS + static const char *part_types[] = { "cmdlinepart", NULL, }; +#endif + + fun->chip.IO_ADDR_R = fun->io_base; + fun->chip.IO_ADDR_W = fun->io_base; + fun->chip.cmd_ctrl = fun_cmd_ctrl; + fun->chip.chip_delay = fun->chip_delay; + fun->chip.read_byte = fun_read_byte; + fun->chip.read_buf = fun_read_buf; + fun->chip.write_buf = fun_write_buf; + fun->chip.ecc.mode = NAND_ECC_SOFT; + + if (fun->rnb_gpio >= 0) + fun->chip.dev_ready = fun_chip_ready; + + fun->mtd.priv = &fun->chip; + fun->mtd.owner = THIS_MODULE; + + ret = nand_scan(&fun->mtd, 1); + if (ret) + return ret; + + fun->mtd.name = fun->dev->bus_id; + +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0); + if (ret > 0) + return add_mtd_partitions(&fun->mtd, fun->parts, ret); +#endif + return add_mtd_device(&fun->mtd); +} + +static int __devinit fun_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct fsl_upm_nand *fun; + struct resource io_res; + const uint32_t *prop; + int ret; + int size; + + fun = kzalloc(sizeof(*fun), GFP_KERNEL); + if (!fun) + return -ENOMEM; + + ret = of_address_to_resource(ofdev->node, 0, &io_res); + if (ret) { + dev_err(&ofdev->dev, "can't get IO base\n"); + goto err1; + } + + ret = fsl_upm_find(io_res.start, &fun->upm); + if (ret) { + dev_err(&ofdev->dev, "can't find UPM\n"); + goto err1; + } + + prop = of_get_property(ofdev->node, "fsl,upm-addr-offset", &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM address offset\n"); + ret = -EINVAL; + goto err2; + } + fun->upm_addr_offset = *prop; + + prop = of_get_property(ofdev->node, "fsl,upm-cmd-offset", &size); + if (!prop || size != sizeof(uint32_t)) { + dev_err(&ofdev->dev, "can't get UPM command offset\n"); + ret = -EINVAL; + goto err2; + } + fun->upm_cmd_offset = *prop; + + fun->rnb_gpio = of_get_gpio(ofdev->node, 0); + if (fun->rnb_gpio >= 0) { + ret = gpio_request(fun->rnb_gpio, ofdev->dev.bus_id); + if (ret) { + dev_err(&ofdev->dev, "can't request RNB gpio\n"); + goto err2; + } + gpio_direction_input(fun->rnb_gpio); + } else if (fun->rnb_gpio == -EINVAL) { + dev_err(&ofdev->dev, "specified RNB gpio is invalid\n"); + goto err2; + } + + fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, + io_res.end - io_res.start + 1); + if (!fun->io_base) { + ret = -ENOMEM; + goto err2; + } + + fun->dev = &ofdev->dev; + fun->last_ctrl = NAND_CLE; + fun->wait_pattern = of_get_property(ofdev->node, "fsl,wait-pattern", + NULL); + fun->wait_write = of_get_property(ofdev->node, "fsl,wait-write", NULL); + + prop = of_get_property(ofdev->node, "chip-delay", NULL); + if (prop) + fun->chip_delay = *prop; + else + fun->chip_delay = 50; + + ret = fun_chip_init(fun); + if (ret) + goto err2; + + dev_set_drvdata(&ofdev->dev, fun); + + return 0; +err2: + if (fun->rnb_gpio >= 0) + gpio_free(fun->rnb_gpio); +err1: + kfree(fun); + + return ret; +} + +static int __devexit fun_remove(struct of_device *ofdev) +{ + struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); + + nand_release(&fun->mtd); + + if (fun->rnb_gpio >= 0) + gpio_free(fun->rnb_gpio); + + kfree(fun); + + return 0; +} + +static struct of_device_id of_fun_match[] = { + { .compatible = "fsl,upm-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fun_match); + +static struct of_platform_driver of_fun_driver = { + .name = "fsl,upm-nand", + .match_table = of_fun_match, + .probe = fun_probe, + .remove = __devexit_p(fun_remove), +}; + +static int __init fun_module_init(void) +{ + return of_register_platform_driver(&of_fun_driver); +} +module_init(fun_module_init); + +static void __exit fun_module_exit(void) +{ + of_unregister_platform_driver(&of_fun_driver); +} +module_exit(fun_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_DESCRIPTION("Driver for NAND chips working through Freescale " + "LocalBus User-Programmable Machine"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7acb1a0e7409..ba1bdf787323 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2229,6 +2229,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, { struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; + int tmp_id, tmp_manf; /* Select the device */ chip->select_chip(mtd, 0); @@ -2240,6 +2241,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); + /* Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + + tmp_manf = chip->read_byte(mtd); + tmp_id = chip->read_byte(mtd); + + if (tmp_manf != *maf_id || tmp_id != dev_id) { + printk(KERN_INFO "%s: second ID read did not match " + "%02x,%02x against %02x,%02x\n", __func__, + *maf_id, dev_id, tmp_manf, tmp_id); + return ERR_PTR(-ENODEV); + } + /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (dev_id == nand_flash_ids[i].id) { diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 1c0e89f00e8d..955959eb02d4 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -317,3 +317,5 @@ module_exit(ndfc_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); MODULE_DESCRIPTION("Platform driver for NDFC"); +MODULE_ALIAS("platform:ndfc-chip"); +MODULE_ALIAS("platform:ndfc-nand"); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index ec5ad28b237e..59e05a1c50cf 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -169,3 +169,4 @@ module_exit(orion_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tzachi Perelstein"); MODULE_DESCRIPTION("NAND glue for Orion platforms"); +MODULE_ALIAS("platform:orion_nand"); diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index f6d5c2adc4fd..f674c5427b17 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -54,6 +54,7 @@ static int __init plat_nand_probe(struct platform_device *pdev) data->chip.priv = &data; data->mtd.priv = &data->chip; data->mtd.owner = THIS_MODULE; + data->mtd.name = pdev->dev.bus_id; data->chip.IO_ADDR_R = data->io_base; data->chip.IO_ADDR_W = data->io_base; @@ -150,3 +151,4 @@ module_exit(plat_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vitaly Wool"); MODULE_DESCRIPTION("Simple generic NAND driver"); +MODULE_ALIAS("platform:gen_nand"); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c new file mode 100644 index 000000000000..fceb468ccdec --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,1249 @@ +/* + * drivers/mtd/nand/pxa3xx_nand.c + * + * Copyright © 2005 Intel Corporation + * Copyright © 2006 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <asm/dma.h> + +#include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa3xx_nand.h> + +#define CHIP_DELAY_TIMEOUT (2 * HZ/10) + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBDR0 (0x1C) /* Bad Block Register 0 */ +#define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ + +#define NDCR_SPARE_EN (0x1 << 31) +#define NDCR_ECC_EN (0x1 << 30) +#define NDCR_DMA_EN (0x1 << 29) +#define NDCR_ND_RUN (0x1 << 28) +#define NDCR_DWIDTH_C (0x1 << 27) +#define NDCR_DWIDTH_M (0x1 << 26) +#define NDCR_PAGE_SZ (0x1 << 24) +#define NDCR_NCSX (0x1 << 23) +#define NDCR_ND_MODE (0x3 << 21) +#define NDCR_NAND_MODE (0x0) +#define NDCR_CLR_PG_CNT (0x1 << 20) +#define NDCR_CLR_ECC (0x1 << 19) +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDCR_RA_START (0x1 << 15) +#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_ND_ARB_EN (0x1 << 12) + +#define NDSR_MASK (0xfff) +#define NDSR_RDY (0x1 << 11) +#define NDSR_CS0_PAGED (0x1 << 10) +#define NDSR_CS1_PAGED (0x1 << 9) +#define NDSR_CS0_CMDD (0x1 << 8) +#define NDSR_CS1_CMDD (0x1 << 7) +#define NDSR_CS0_BBD (0x1 << 6) +#define NDSR_CS1_BBD (0x1 << 5) +#define NDSR_DBERR (0x1 << 4) +#define NDSR_SBERR (0x1 << 3) +#define NDSR_WRDREQ (0x1 << 2) +#define NDSR_RDDREQ (0x1 << 1) +#define NDSR_WRCMDREQ (0x1) + +#define NDCB0_AUTO_RS (0x1 << 25) +#define NDCB0_CSEL (0x1 << 24) +#define NDCB0_CMD_TYPE_MASK (0x7 << 21) +#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) +#define NDCB0_NC (0x1 << 20) +#define NDCB0_DBC (0x1 << 19) +#define NDCB0_ADDR_CYC_MASK (0x7 << 16) +#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) +#define NDCB0_CMD2_MASK (0xff << 8) +#define NDCB0_CMD1_MASK (0xff) +#define NDCB0_ADDR_CYC_SHIFT (16) + +/* dma-able I/O address for the NAND data and commands */ +#define NDCB0_DMA_ADDR (0x43100048) +#define NDDB_DMA_ADDR (0x43100040) + +/* macros for registers read/write */ +#define nand_writel(info, off, val) \ + __raw_writel((val), (info)->mmio_base + (off)) + +#define nand_readl(info, off) \ + __raw_readl((info)->mmio_base + (off)) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_DMABUSERR = -1, + ERR_SENDCMD = -2, + ERR_DBERR = -3, + ERR_BBERR = -4, +}; + +enum { + STATE_READY = 0, + STATE_CMD_HANDLE, + STATE_DMA_READING, + STATE_DMA_WRITING, + STATE_DMA_DONE, + STATE_PIO_READING, + STATE_PIO_WRITING, +}; + +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_cmdset { + uint16_t read1; + uint16_t read2; + uint16_t program; + uint16_t read_status; + uint16_t read_id; + uint16_t erase; + uint16_t reset; + uint16_t lock; + uint16_t unlock; + uint16_t lock_status; +}; + +struct pxa3xx_nand_flash { + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ + struct pxa3xx_nand_cmdset *cmdset; + + uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ + uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ + uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ + uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ + uint32_t num_blocks; /* Number of physical blocks in Flash */ + uint32_t chip_id; + + /* NOTE: these are automatically calculated, do not define */ + size_t oob_size; + size_t read_id_bytes; + + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; +}; + +struct pxa3xx_nand_info { + struct nand_chip nand_chip; + + struct platform_device *pdev; + struct pxa3xx_nand_flash *flash_info; + + struct clk *clk; + void __iomem *mmio_base; + + unsigned int buf_start; + unsigned int buf_count; + + /* DMA information */ + int drcmr_dat; + int drcmr_cmd; + + unsigned char *data_buff; + dma_addr_t data_buff_phys; + size_t data_buff_size; + int data_dma_ch; + struct pxa_dma_desc *data_desc; + dma_addr_t data_desc_addr; + + uint32_t reg_ndcr; + + /* saved column/page_addr during CMD_SEQIN */ + int seqin_column; + int seqin_page_addr; + + /* relate to the command */ + unsigned int state; + + int use_ecc; /* use HW ECC ? */ + int use_dma; /* use DMA ? */ + + size_t data_size; /* data size in FIFO */ + int retcode; + struct completion cmd_complete; + + /* generated NDCBx register values */ + uint32_t ndcb0; + uint32_t ndcb1; + uint32_t ndcb2; +}; + +static int use_dma = 1; +module_param(use_dma, bool, 0444); +MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); + +static struct pxa3xx_nand_cmdset smallpage_cmdset = { + .read1 = 0x0000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_cmdset largepage_cmdset = { + .read1 = 0x3000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_timing samsung512MbX16_timing = { + .tCH = 10, + .tCS = 0, + .tWH = 20, + .tWP = 40, + .tRH = 30, + .tRP = 40, + .tR = 11123, + .tWHR = 110, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash samsung512MbX16 = { + .timing = &samsung512MbX16_timing, + .cmdset = &smallpage_cmdset, + .page_per_block = 32, + .page_size = 512, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 4096, + .chip_id = 0x46ec, +}; + +static struct pxa3xx_nand_timing micron_timing = { + .tCH = 10, + .tCS = 25, + .tWH = 15, + .tWP = 25, + .tRH = 15, + .tRP = 25, + .tR = 25000, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash micron1GbX8 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 1024, + .chip_id = 0xa12c, +}; + +static struct pxa3xx_nand_flash micron1GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 1024, + .chip_id = 0xb12c, +}; + +static struct pxa3xx_nand_flash *builtin_flash_types[] = { + &samsung512MbX16, + µn1GbX8, + µn1GbX16, +}; + +#define NDTR0_tCH(c) (min((c), 7) << 19) +#define NDTR0_tCS(c) (min((c), 7) << 16) +#define NDTR0_tWH(c) (min((c), 7) << 11) +#define NDTR0_tWP(c) (min((c), 7) << 8) +#define NDTR0_tRH(c) (min((c), 7) << 3) +#define NDTR0_tRP(c) (min((c), 7) << 0) + +#define NDTR1_tR(c) (min((c), 65535) << 16) +#define NDTR1_tWHR(c) (min((c), 15) << 4) +#define NDTR1_tAR(c) (min((c), 15) << 0) + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, + struct pxa3xx_nand_timing *t) +{ + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0, ndtr1; + + ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | + NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | + NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | + NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | + NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | + NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | + NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +#define WAIT_EVENT_TIMEOUT 10 + +static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +{ + int timeout = WAIT_EVENT_TIMEOUT; + uint32_t ndsr; + + while (timeout--) { + ndsr = nand_readl(info, NDSR) & NDSR_MASK; + if (ndsr & event) { + nand_writel(info, NDSR, ndsr); + return 0; + } + udelay(10); + } + + return -ETIMEDOUT; +} + +static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int column, int page_addr) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + + /* calculate data size */ + switch (f->page_size) { + case 2048: + info->data_size = (info->use_ecc) ? 2088 : 2112; + break; + case 512: + info->data_size = (info->use_ecc) ? 520 : 528; + break; + default: + return -EINVAL; + } + + /* generate values for NDCBx registers */ + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb0 |= NDCB0_ADDR_CYC(f->row_addr_cycles + f->col_addr_cycles); + + if (f->col_addr_cycles == 2) { + /* large block, 2 cycles for column address + * row address starts from 3rd cycle + */ + info->ndcb1 |= (page_addr << 16) | (column & 0xffff); + if (f->row_addr_cycles == 3) + info->ndcb2 = (page_addr >> 16) & 0xff; + } else + /* small block, 1 cycles for column address + * row address starts from 2nd cycle + */ + info->ndcb1 = (page_addr << 8) | (column & 0xff); + + if (cmd == cmdset->program) + info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; + + return 0; +} + +static int prepare_erase_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int page_addr) +{ + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); + info->ndcb1 = page_addr; + info->ndcb2 = 0; + return 0; +} + +static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +{ + struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; + + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + + if (cmd == cmdset->read_id) { + info->ndcb0 |= NDCB0_CMD_TYPE(3); + info->data_size = 8; + } else if (cmd == cmdset->read_status) { + info->ndcb0 |= NDCB0_CMD_TYPE(4); + info->data_size = 8; + } else if (cmd == cmdset->reset || cmd == cmdset->lock || + cmd == cmdset->unlock) { + info->ndcb0 |= NDCB0_CMD_TYPE(5); + } else + return -EINVAL; + + return 0; +} + +static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~int_mask); +} + +static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr | int_mask); +} + +/* NOTE: it is a must to set ND_RUN firstly, then write command buffer + * otherwise, it does not work + */ +static int write_cmd(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + /* clear status bits and run */ + nand_writel(info, NDSR, NDSR_MASK); + + ndcr = info->reg_ndcr; + + ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; + ndcr |= info->use_dma ? NDCR_DMA_EN : 0; + ndcr |= NDCR_ND_RUN; + + nand_writel(info, NDCR, ndcr); + + if (wait_for_event(info, NDSR_WRCMDREQ)) { + printk(KERN_ERR "timed out writing command\n"); + return -ETIMEDOUT; + } + + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); + return 0; +} + +static int handle_data_pio(struct pxa3xx_nand_info *info) +{ + int ret, timeout = CHIP_DELAY_TIMEOUT; + + switch (info->state) { + case STATE_PIO_WRITING: + __raw_writesl(info->mmio_base + NDDB, info->data_buff, + info->data_size << 2); + + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + + ret = wait_for_completion_timeout(&info->cmd_complete, timeout); + if (!ret) { + printk(KERN_ERR "program command time out\n"); + return -1; + } + break; + case STATE_PIO_READING: + __raw_readsl(info->mmio_base + NDDB, info->data_buff, + info->data_size << 2); + break; + default: + printk(KERN_ERR "%s: invalid state %d\n", __func__, + info->state); + return -EINVAL; + } + + info->state = STATE_READY; + return 0; +} + +static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) +{ + struct pxa_dma_desc *desc = info->data_desc; + int dma_len = ALIGN(info->data_size, 32); + + desc->ddadr = DDADR_STOP; + desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; + + if (dir_out) { + desc->dsadr = info->data_buff_phys; + desc->dtadr = NDDB_DMA_ADDR; + desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; + } else { + desc->dtadr = info->data_buff_phys; + desc->dsadr = NDDB_DMA_ADDR; + desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; + } + + DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; + DDADR(info->data_dma_ch) = info->data_desc_addr; + DCSR(info->data_dma_ch) |= DCSR_RUN; +} + +static void pxa3xx_nand_data_dma_irq(int channel, void *data) +{ + struct pxa3xx_nand_info *info = data; + uint32_t dcsr; + + dcsr = DCSR(channel); + DCSR(channel) = dcsr; + + if (dcsr & DCSR_BUSERR) { + info->retcode = ERR_DMABUSERR; + complete(&info->cmd_complete); + } + + if (info->state == STATE_DMA_WRITING) { + info->state = STATE_DMA_DONE; + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + } else { + info->state = STATE_READY; + complete(&info->cmd_complete); + } +} + +static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) +{ + struct pxa3xx_nand_info *info = devid; + unsigned int status; + + status = nand_readl(info, NDSR); + + if (status & (NDSR_RDDREQ | NDSR_DBERR)) { + if (status & NDSR_DBERR) + info->retcode = ERR_DBERR; + + disable_int(info, NDSR_RDDREQ | NDSR_DBERR); + + if (info->use_dma) { + info->state = STATE_DMA_READING; + start_data_dma(info, 0); + } else { + info->state = STATE_PIO_READING; + complete(&info->cmd_complete); + } + } else if (status & NDSR_WRDREQ) { + disable_int(info, NDSR_WRDREQ); + if (info->use_dma) { + info->state = STATE_DMA_WRITING; + start_data_dma(info, 1); + } else { + info->state = STATE_PIO_WRITING; + complete(&info->cmd_complete); + } + } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { + if (status & NDSR_CS0_BBD) + info->retcode = ERR_BBERR; + + disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + info->state = STATE_READY; + complete(&info->cmd_complete); + } + nand_writel(info, NDSR, status); + return IRQ_HANDLED; +} + +static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) +{ + uint32_t ndcr; + int ret, timeout = CHIP_DELAY_TIMEOUT; + + if (write_cmd(info)) { + info->retcode = ERR_SENDCMD; + goto fail_stop; + } + + info->state = STATE_CMD_HANDLE; + + enable_int(info, event); + + ret = wait_for_completion_timeout(&info->cmd_complete, timeout); + if (!ret) { + printk(KERN_ERR "command execution timed out\n"); + info->retcode = ERR_SENDCMD; + goto fail_stop; + } + + if (info->use_dma == 0 && info->data_size > 0) + if (handle_data_pio(info)) + goto fail_stop; + + return 0; + +fail_stop: + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + udelay(10); + return -ETIMEDOUT; +} + +static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0; +} + +static inline int is_buf_blank(uint8_t *buf, size_t len) +{ + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; +} + +static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_flash *flash_info = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset; + int ret; + + info->use_dma = (use_dma) ? 1 : 0; + info->use_ecc = 0; + info->data_size = 0; + info->state = STATE_READY; + + init_completion(&info->cmd_complete); + + switch (command) { + case NAND_CMD_READOOB: + /* disable HW ECC to get all the OOB data */ + info->buf_count = mtd->writesize + mtd->oobsize; + info->buf_start = mtd->writesize + column; + + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + + /* We only are OOB, so if the data has error, does not matter */ + if (info->retcode == ERR_DBERR) + info->retcode = ERR_NONE; + break; + + case NAND_CMD_READ0: + info->use_ecc = 1; + info->retcode = ERR_NONE; + info->buf_start = column; + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xFF, info->buf_count); + + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + + if (info->retcode == ERR_DBERR) { + /* for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such double bit errors + */ + if (is_buf_blank(info->data_buff, mtd->writesize)) + info->retcode = ERR_NONE; + } + break; + case NAND_CMD_SEQIN: + info->buf_start = column; + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xff, info->buf_count); + + /* save column/page_addr for next CMD_PAGEPROG */ + info->seqin_column = column; + info->seqin_page_addr = page_addr; + break; + case NAND_CMD_PAGEPROG: + info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; + + if (prepare_read_prog_cmd(info, cmdset->program, + info->seqin_column, info->seqin_page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); + break; + case NAND_CMD_ERASE1: + if (prepare_erase_cmd(info, cmdset->erase, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + break; + case NAND_CMD_ERASE2: + break; + case NAND_CMD_READID: + case NAND_CMD_STATUS: + info->use_dma = 0; /* force PIO read */ + info->buf_start = 0; + info->buf_count = (command == NAND_CMD_READID) ? + flash_info->read_id_bytes : 1; + + if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? + cmdset->read_id : cmdset->read_status)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); + break; + case NAND_CMD_RESET: + if (prepare_other_cmd(info, cmdset->reset)) + break; + + ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); + if (ret == 0) { + int timeout = 2; + uint32_t ndcr; + + while (timeout--) { + if (nand_readl(info, NDSR) & NDSR_RDY) + break; + msleep(10); + } + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + } + break; + default: + printk(KERN_ERR "non-supported command.\n"); + break; + } + + if (info->retcode == ERR_DBERR) { + printk(KERN_ERR "double bit error @ page %08x\n", page_addr); + info->retcode = ERR_NONE; + } +} + +static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + char retval = 0xFF; + + if (info->buf_start < info->buf_count) + /* Has just send a new command? */ + retval = info->data_buff[info->buf_start++]; + + return retval; +} + +static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) +{ + struct pxa3xx_nand_info *info = mtd->priv; + u16 retval = 0xFFFF; + + if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); + info->buf_start += 2; + } + return retval; +} + +static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct pxa3xx_nand_info *info = mtd->priv; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(buf, info->data_buff + info->buf_start, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct pxa3xx_nand_info *info = mtd->priv; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(info->data_buff + info->buf_start, buf, real_len); + info->buf_start += real_len; +} + +static int pxa3xx_nand_verify_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + return 0; +} + +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct pxa3xx_nand_info *info = mtd->priv; + + /* pxa3xx_nand_send_command has waited for command complete */ + if (this->state == FL_WRITING || this->state == FL_ERASING) { + if (info->retcode == ERR_NONE) + return 0; + else { + /* + * any error make it return 0x01 which will tell + * the caller the erase and write fail + */ + return 0x01; + } + } + + return 0; +} + +static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + return; +} + +static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd, + const uint8_t *dat, uint8_t *ecc_code) +{ + return 0; +} + +static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, + uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct pxa3xx_nand_info *info = mtd->priv; + /* + * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we + * consider it as a ecc error which will tell the caller the + * read fail We have distinguish all the errors, but the + * nand_read_ecc only check this function return value + */ + if (info->retcode != ERR_NONE) + return -1; + + return 0; +} + +static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + uint32_t ndcr; + uint8_t id_buff[8]; + + if (prepare_other_cmd(info, cmdset->read_id)) { + printk(KERN_ERR "failed to prepare command\n"); + return -EINVAL; + } + + /* Send command */ + if (write_cmd(info)) + goto fail_timeout; + + /* Wait for CMDDM(command done successfully) */ + if (wait_for_event(info, NDSR_RDDREQ)) + goto fail_timeout; + + __raw_readsl(info->mmio_base + NDDB, id_buff, 2); + *id = id_buff[0] | (id_buff[1] << 8); + return 0; + +fail_timeout: + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + udelay(10); + return -ETIMEDOUT; +} + +static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, + struct pxa3xx_nand_flash *f) +{ + struct platform_device *pdev = info->pdev; + struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ + + if (f->page_size != 2048 && f->page_size != 512) + return -EINVAL; + + if (f->flash_width != 16 && f->flash_width != 8) + return -EINVAL; + + /* calculate flash information */ + f->oob_size = (f->page_size == 2048) ? 64 : 16; + f->read_id_bytes = (f->page_size == 2048) ? 4 : 2; + + /* calculate addressing information */ + f->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; + + if (f->num_blocks * f->page_per_block > 65536) + f->row_addr_cycles = 3; + else + f->row_addr_cycles = 2; + + ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + ndcr |= (f->col_addr_cycles == 2) ? NDCR_RA_START : 0; + ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; + ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; + ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; + ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; + + ndcr |= NDCR_RD_ID_CNT(f->read_id_bytes); + ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + + info->reg_ndcr = ndcr; + + pxa3xx_nand_set_timing(info, f->timing); + info->flash_info = f; + return 0; +} + +static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_flash *f; + uint32_t id; + int i; + + for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) { + + f = builtin_flash_types[i]; + + if (pxa3xx_nand_config_flash(info, f)) + continue; + + if (__readid(info, &id)) + continue; + + if (id == f->chip_id) + return 0; + } + + return -ENODEV; +} + +/* the maximum possible buffer size for large page with OOB data + * is: 2048 + 64 = 2112 bytes, allocate a page here for both the + * data buffer and the DMA descriptor + */ +#define MAX_BUFF_SIZE PAGE_SIZE + +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + + if (use_dma == 0) { + info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; + } + + info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE, + &info->data_buff_phys, GFP_KERNEL); + if (info->data_buff == NULL) { + dev_err(&pdev->dev, "failed to allocate dma buffer\n"); + return -ENOMEM; + } + + info->data_buff_size = MAX_BUFF_SIZE; + info->data_desc = (void *)info->data_buff + data_desc_offset; + info->data_desc_addr = info->data_buff_phys + data_desc_offset; + + info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW, + pxa3xx_nand_data_dma_irq, info); + if (info->data_dma_ch < 0) { + dev_err(&pdev->dev, "failed to request data dma\n"); + dma_free_coherent(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + return info->data_dma_ch; + } + + return 0; +} + +static struct nand_ecclayout hw_smallpage_ecclayout = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 11, 12, 13 }, + .oobfree = { {2, 6} } +}; + +static struct nand_ecclayout hw_largepage_ecclayout = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + +static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, + struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_flash *f = info->flash_info; + struct nand_chip *this = &info->nand_chip; + + this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; + + this->waitfunc = pxa3xx_nand_waitfunc; + this->select_chip = pxa3xx_nand_select_chip; + this->dev_ready = pxa3xx_nand_dev_ready; + this->cmdfunc = pxa3xx_nand_cmdfunc; + this->read_word = pxa3xx_nand_read_word; + this->read_byte = pxa3xx_nand_read_byte; + this->read_buf = pxa3xx_nand_read_buf; + this->write_buf = pxa3xx_nand_write_buf; + this->verify_buf = pxa3xx_nand_verify_buf; + + this->ecc.mode = NAND_ECC_HW; + this->ecc.hwctl = pxa3xx_nand_ecc_hwctl; + this->ecc.calculate = pxa3xx_nand_ecc_calculate; + this->ecc.correct = pxa3xx_nand_ecc_correct; + this->ecc.size = f->page_size; + + if (f->page_size == 2048) + this->ecc.layout = &hw_largepage_ecclayout; + else + this->ecc.layout = &hw_smallpage_ecclayout; + + this->chip_delay = 25; +} + +static int pxa3xx_nand_probe(struct platform_device *pdev) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_info *info; + struct nand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int ret = 0, irq; + + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -ENODEV; + } + + mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info), + GFP_KERNEL); + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + info = (struct pxa3xx_nand_info *)(&mtd[1]); + info->pdev = pdev; + + this = &info->nand_chip; + mtd->priv = info; + + info->clk = clk_get(&pdev->dev, "NANDCLK"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to get nand clock\n"); + ret = PTR_ERR(info->clk); + goto fail_free_mtd; + } + clk_enable(info->clk); + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_put_clk; + } + info->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined for command DMA\n"); + ret = -ENXIO; + goto fail_put_clk; + } + info->drcmr_cmd = r->start; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + ret = -ENXIO; + goto fail_put_clk; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IO memory resource defined\n"); + ret = -ENODEV; + goto fail_put_clk; + } + + r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); + if (r == NULL) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + ret = -EBUSY; + goto fail_put_clk; + } + + info->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (info->mmio_base == NULL) { + dev_err(&pdev->dev, "ioremap() failed\n"); + ret = -ENODEV; + goto fail_free_res; + } + + ret = pxa3xx_nand_init_buff(info); + if (ret) + goto fail_free_io; + + ret = request_irq(IRQ_NAND, pxa3xx_nand_irq, IRQF_DISABLED, + pdev->name, info); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_buf; + } + + ret = pxa3xx_nand_detect_flash(info); + if (ret) { + dev_err(&pdev->dev, "failed to detect flash\n"); + ret = -ENODEV; + goto fail_free_irq; + } + + pxa3xx_nand_init_mtd(mtd, info); + + platform_set_drvdata(pdev, mtd); + + if (nand_scan(mtd, 1)) { + dev_err(&pdev->dev, "failed to scan nand\n"); + ret = -ENXIO; + goto fail_free_irq; + } + + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + +fail_free_irq: + free_irq(IRQ_NAND, info); +fail_free_buf: + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + } else + kfree(info->data_buff); +fail_free_io: + iounmap(info->mmio_base); +fail_free_res: + release_mem_region(r->start, r->end - r->start + 1); +fail_put_clk: + clk_disable(info->clk); + clk_put(info->clk); +fail_free_mtd: + kfree(mtd); + return ret; +} + +static int pxa3xx_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + platform_set_drvdata(pdev, NULL); + + del_mtd_device(mtd); + del_mtd_partitions(mtd); + free_irq(IRQ_NAND, info); + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_writecombine(&pdev->dev, info->data_buff_size, + info->data_buff, info->data_buff_phys); + } else + kfree(info->data_buff); + kfree(mtd); + return 0; +} + +#ifdef CONFIG_PM +static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + if (info->state != STATE_READY) { + dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); + return -EAGAIN; + } + + return 0; +} + +static int pxa3xx_nand_resume(struct platform_device *pdev) +{ + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; + + clk_enable(info->clk); + + return pxa3xx_nand_config_flash(info); +} +#else +#define pxa3xx_nand_suspend NULL +#define pxa3xx_nand_resume NULL +#endif + +static struct platform_driver pxa3xx_nand_driver = { + .driver = { + .name = "pxa3xx-nand", + }, + .probe = pxa3xx_nand_probe, + .remove = pxa3xx_nand_remove, + .suspend = pxa3xx_nand_suspend, + .resume = pxa3xx_nand_resume, +}; + +static int __init pxa3xx_nand_init(void) +{ + return platform_driver_register(&pxa3xx_nand_driver); +} +module_init(pxa3xx_nand_init); + +static void __exit pxa3xx_nand_exit(void) +{ + platform_driver_unregister(&pxa3xx_nand_driver); +} +module_exit(pxa3xx_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PXA3xx NAND controller driver"); diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 0f6ac250f434..26f88215bc47 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -478,6 +478,7 @@ static int __init rtc_from4_init(void) struct nand_chip *this; unsigned short bcr1, bcr2, wcr2; int i; + int ret; /* Allocate memory for MTD device structure and private data */ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); @@ -537,6 +538,22 @@ static int __init rtc_from4_init(void) this->ecc.hwctl = rtc_from4_enable_hwecc; this->ecc.calculate = rtc_from4_calculate_ecc; this->ecc.correct = rtc_from4_correct_data; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 0 + * primitve element to generate roots = 1 + * generator polinomial degree = 6 + */ + rs_decoder = init_rs(10, 0x409, 0, 1, 6); + if (!rs_decoder) { + printk(KERN_ERR "Could not create a RS decoder\n"); + ret = -ENOMEM; + goto err_1; + } #else printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n"); @@ -549,8 +566,8 @@ static int __init rtc_from4_init(void) /* Scan to find existence of the device */ if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) { - kfree(rtc_from4_mtd); - return -ENXIO; + ret = -ENXIO; + goto err_2; } /* Perform 'device recovery' for each chip in case there was a power loss. */ @@ -566,28 +583,19 @@ static int __init rtc_from4_init(void) #endif /* Register the partitions */ - add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + ret = add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); + if (ret) + goto err_3; -#ifdef RTC_FROM4_HWECC - /* We could create the decoder on demand, if memory is a concern. - * This way we have it handy, if an error happens - * - * Symbolsize is 10 (bits) - * Primitve polynomial is x^10+x^3+1 - * first consecutive root is 0 - * primitve element to generate roots = 1 - * generator polinomial degree = 6 - */ - rs_decoder = init_rs(10, 0x409, 0, 1, 6); - if (!rs_decoder) { - printk(KERN_ERR "Could not create a RS decoder\n"); - nand_release(rtc_from4_mtd); - kfree(rtc_from4_mtd); - return -ENOMEM; - } -#endif /* Return happy */ return 0; +err_3: + nand_release(rtc_from4_mtd); +err_2: + free_rs(rs_decoder); +err_1: + kfree(rtc_from4_mtd); + return ret; } module_init(rtc_from4_init); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 9260ad947524..b34a460ab679 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -119,8 +119,7 @@ struct s3c2410_nand_info { void __iomem *sel_reg; int sel_bit; int mtd_count; - - unsigned long save_nfconf; + unsigned long save_sel; enum s3c_cpu_type cpu_type; }; @@ -358,6 +357,14 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, if (diff0 == 0 && diff1 == 0 && diff2 == 0) return 0; /* ECC is ok */ + /* sometimes people do not think about using the ECC, so check + * to see if we have an 0xff,0xff,0xff read ECC and then ignore + * the error, on the assumption that this is an un-eccd page. + */ + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff + && info->platform->ignore_unset_ecc) + return 0; + /* Can we correct this ECC (ie, one row and column change). * Note, this is similar to the 256 error code on smartmedia */ @@ -473,7 +480,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("%s: returning ecc %06lx\n", __func__, ecc); + pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff); return 0; } @@ -644,9 +651,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 512; - chip->ecc.bytes = 3; - chip->ecc.layout = &nand_hw_eccoob; switch (info->cpu_type) { case TYPE_S3C2410: @@ -668,6 +672,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, } else { chip->ecc.mode = NAND_ECC_SOFT; } + + if (set->ecc_layout != NULL) + chip->ecc.layout = set->ecc_layout; + + if (set->disable_ecc) + chip->ecc.mode = NAND_ECC_NONE; +} + +/* s3c2410_nand_update_chip + * + * post-probe chip update, to change any items, such as the + * layout for large page nand + */ + +static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd) +{ + struct nand_chip *chip = &nmtd->chip; + + printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift); + + if (hardware_ecc) { + /* change the behaviour depending on wether we are using + * the large or small page nand device */ + + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.layout = &nand_hw_eccoob; + } + } } /* s3c2410_nand_probe @@ -776,9 +814,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, s3c2410_nand_init_chip(info, nmtd, sets); - nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); + nmtd->scan_res = nand_scan_ident(&nmtd->mtd, + (sets) ? sets->nr_chips : 1); if (nmtd->scan_res == 0) { + s3c2410_nand_update_chip(info, nmtd); + nand_scan_tail(&nmtd->mtd); s3c2410_nand_add_partition(info, nmtd, sets); } @@ -810,15 +851,14 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) struct s3c2410_nand_info *info = platform_get_drvdata(dev); if (info) { - info->save_nfconf = readl(info->regs + S3C2410_NFCONF); + info->save_sel = readl(info->sel_reg); /* For the moment, we must ensure nFCE is high during * the time we are suspended. This really should be * handled by suspending the MTDs we are using, but * that is currently not the case. */ - writel(info->save_nfconf | info->sel_bit, - info->regs + S3C2410_NFCONF); + writel(info->save_sel | info->sel_bit, info->sel_reg); if (!allow_clk_stop(info)) clk_disable(info->clk); @@ -830,7 +870,7 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) static int s3c24xx_nand_resume(struct platform_device *dev) { struct s3c2410_nand_info *info = platform_get_drvdata(dev); - unsigned long nfconf; + unsigned long sel; if (info) { clk_enable(info->clk); @@ -838,10 +878,10 @@ static int s3c24xx_nand_resume(struct platform_device *dev) /* Restore the state of the nFCE line. */ - nfconf = readl(info->regs + S3C2410_NFCONF); - nfconf &= ~info->sel_bit; - nfconf |= info->save_nfconf & info->sel_bit; - writel(nfconf, info->regs + S3C2410_NFCONF); + sel = readl(info->sel_reg); + sel &= ~info->sel_bit; + sel |= info->save_sel & info->sel_bit; + writel(sel, info->sel_reg); if (allow_clk_stop(info)) clk_disable(info->clk); @@ -927,3 +967,6 @@ module_exit(s3c2410_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); +MODULE_ALIAS("platform:s3c2410-nand"); +MODULE_ALIAS("platform:s3c2412-nand"); +MODULE_ALIAS("platform:s3c2440-nand"); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 0513cbc8834d..345e6eff89ce 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -33,11 +33,6 @@ char nftlmountrev[]="$Revision: 1.41 $"; -extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); -extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf); - /* 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[] diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index f86e06934cd8..4f80c2fd89af 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -72,3 +72,5 @@ int __devinit of_mtd_parse_partitions(struct device *dev, return nr_parts; } EXPORT_SYMBOL(of_mtd_parse_partitions); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8d7d21be1541..5d7965f7e9ce 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -329,6 +329,21 @@ static int onenand_wait(struct mtd_info *mtd, int state) printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) printk(KERN_ERR "onenand_wait: it's locked error.\n"); + if (state == FL_READING) { + /* + * A power loss while writing can result in a page + * becoming unreadable. When the device is mounted + * again, reading that page gives controller errors. + * Upper level software like JFFS2 treat -EIO as fatal, + * refusing to mount at all. That means it is necessary + * to treat the error as an ECC error to allow recovery. + * Note that typically in this case, the eraseblock can + * still be erased and rewritten i.e. it has not become + * a bad block. + */ + mtd->ecc_stats.failed++; + return -EBADMSG; + } return -EIO; } @@ -1336,7 +1351,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, } /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -1466,7 +1481,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, } /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -2052,7 +2067,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) * * Check lock status */ -static void onenand_check_lock_status(struct onenand_chip *this) +static int onenand_check_lock_status(struct onenand_chip *this) { unsigned int value, block, status; unsigned int end; @@ -2070,9 +2085,13 @@ static void onenand_check_lock_status(struct onenand_chip *this) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) + if (!(status & ONENAND_WP_US)) { printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + return 0; + } } + + return 1; } /** @@ -2081,9 +2100,11 @@ static void onenand_check_lock_status(struct onenand_chip *this) * * Unlock all blocks */ -static int onenand_unlock_all(struct mtd_info *mtd) +static void onenand_unlock_all(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -2099,23 +2120,19 @@ static int onenand_unlock_all(struct mtd_info *mtd) & ONENAND_CTRL_ONGO) continue; + /* Check lock status */ + if (onenand_check_lock_status(this)) + return; + /* Workaround for all block unlock in DDP */ if (ONENAND_IS_DDP(this)) { - /* 1st block on another chip */ - loff_t ofs = this->chipsize >> 1; - size_t len = mtd->erasesize; - - onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; } - - onenand_check_lock_status(this); - - return 0; } - onenand_do_lock_cmd(mtd, 0x0, this->chipsize, ONENAND_CMD_UNLOCK); - - return 0; + onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); } #ifdef CONFIG_MTD_ONENAND_OTP diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aecdd50a1781..2f53b51c6805 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,9 +17,6 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/compatmac.h> -extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops); - /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @param buf the buffer to search diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 823fba4e6d2f..c84e45465499 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -823,7 +823,7 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(part); } -struct mtd_blktrans_ops rfd_ftl_tr = { +static struct mtd_blktrans_ops rfd_ftl_tr = { .name = "rfd", .major = RFD_FTL_MAJOR, .part_bits = PART_BITS, diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index b9daf159a4a7..3f063108e95f 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -24,8 +24,13 @@ config MTD_UBI_WL_THRESHOLD erase counter value and the lowest erase counter value of eraseblocks of UBI devices. When this threshold is exceeded, UBI starts performing wear leveling by means of moving data from eraseblock with low erase - counter to eraseblocks with high erase counter. Leave the default - value if unsure. + counter to eraseblocks with high erase counter. + + The default value should be OK for SLC NAND flashes, NOR flashes and + other flashes which have eraseblock life-cycle 100000 or more. + However, in case of MLC NAND flashes which typically have eraseblock + life-cycle less then 10000, the threshold should be lessened (e.g., + to 128 or 256, although it does not have to be power of 2). config MTD_UBI_BEB_RESERVE int "Percentage of reserved eraseblocks for bad eraseblocks handling" diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 275960462970..961416ac0616 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -606,8 +606,16 @@ static int io_init(struct ubi_device *ubi) ubi->ro_mode = 1; } - dbg_msg("leb_size %d", ubi->leb_size); - dbg_msg("ro_mode %d", ubi->ro_mode); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + if (ubi->hdrs_min_io_size != ubi->min_io_size) + ubi_msg("sub-page size: %d", + ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); /* * Note, ideally, we have to initialize ubi->bad_peb_count here. But @@ -755,8 +763,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) mutex_init(&ubi->volumes_mutex); spin_lock_init(&ubi->volumes_lock); - dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", - mtd->index, ubi_num, vid_hdr_offset); + ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); err = io_init(ubi); if (err) @@ -804,15 +811,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); ubi_msg("MTD device name: \"%s\"", mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); - ubi_msg("physical eraseblock size: %d bytes (%d KiB)", - ubi->peb_size, ubi->peb_size >> 10); - ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); ubi_msg("number of good PEBs: %d", ubi->good_peb_count); ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); - ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); - ubi_msg("VID header offset: %d (aligned %d)", - ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); - ubi_msg("data offset: %d", ubi->leb_start); ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); @@ -950,8 +950,7 @@ static int __init ubi_init(void) BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); if (mtd_devs > UBI_MAX_DEVICES) { - printk(KERN_ERR "UBI error: too many MTD devices, " - "maximum is %d\n", UBI_MAX_DEVICES); + ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES); return -EINVAL; } @@ -959,25 +958,25 @@ static int __init ubi_init(void) ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); if (IS_ERR(ubi_class)) { err = PTR_ERR(ubi_class); - printk(KERN_ERR "UBI error: cannot create UBI class\n"); + ubi_err("cannot create UBI class"); goto out; } err = class_create_file(ubi_class, &ubi_version); if (err) { - printk(KERN_ERR "UBI error: cannot create sysfs file\n"); + ubi_err("cannot create sysfs file"); goto out_class; } err = misc_register(&ubi_ctrl_cdev); if (err) { - printk(KERN_ERR "UBI error: cannot register device\n"); + ubi_err("cannot register device"); goto out_version; } ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", - sizeof(struct ubi_wl_entry), - 0, 0, NULL); + sizeof(struct ubi_wl_entry), + 0, 0, NULL); if (!ubi_wl_entry_slab) goto out_dev_unreg; @@ -1000,8 +999,7 @@ static int __init ubi_init(void) mutex_unlock(&ubi_devices_mutex); if (err < 0) { put_mtd_device(mtd); - printk(KERN_ERR "UBI error: cannot attach mtd%d\n", - mtd->index); + ubi_err("cannot attach mtd%d", mtd->index); goto out_detach; } } @@ -1023,7 +1021,7 @@ out_version: out_class: class_destroy(ubi_class); out: - printk(KERN_ERR "UBI error: cannot initialize UBI, error %d\n", err); + ubi_err("UBI error: cannot initialize UBI, error %d", err); return err; } module_init(ubi_init); diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 51c40b17f1ec..8ea99d8c9e1f 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -41,7 +41,7 @@ /* Generic debugging message */ #define dbg_msg(fmt, ...) \ printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ - current->pid, __FUNCTION__, ##__VA_ARGS__) + current->pid, __func__, ##__VA_ARGS__) #define ubi_dbg_dump_stack() dump_stack() @@ -99,8 +99,10 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD /* Initialization and build messages */ #define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#define UBI_IO_DEBUG 1 #else #define dbg_bld(fmt, ...) ({}) +#define UBI_IO_DEBUG 0 #endif #ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index d397219238d3..e909b390069a 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -291,11 +291,12 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) /* * In case of dynamic volume, MTD device size is just volume size. In * case of a static volume the size is equivalent to the amount of data - * bytes, which is zero at this moment and will be changed after volume - * update. + * bytes. */ if (vol->vol_type == UBI_DYNAMIC_VOLUME) mtd->size = vol->usable_leb_size * vol->reserved_pebs; + else + mtd->size = vol->used_bytes; if (add_mtd_device(mtd)) { ubi_err("cannot not add MTD device\n"); diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index db3efdef2433..4ac11df7b048 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -631,6 +631,8 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, dbg_io("read EC header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); if (err) { @@ -904,6 +906,8 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("read VID header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + if (UBI_IO_DEBUG) + verbose = 1; p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 05aa3e7daba1..96d410e106ab 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -42,6 +42,7 @@ #include <linux/err.h> #include <linux/crc32.h> +#include <asm/div64.h> #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID @@ -92,27 +93,6 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, } /** - * commit_to_mean_value - commit intermediate results to the final mean erase - * counter value. - * @si: scanning information - * - * This is a helper function which calculates partial mean erase counter mean - * value and adds it to the resulting mean value. As we can work only in - * integer arithmetic and we want to calculate the mean value of erase counter - * accurately, we first sum erase counter values in @si->ec_sum variable and - * count these components in @si->ec_count. If this temporary @si->ec_sum is - * going to overflow, we calculate the partial mean value - * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. - */ -static void commit_to_mean_value(struct ubi_scan_info *si) -{ - si->ec_sum /= si->ec_count; - if (si->ec_sum % si->ec_count >= si->ec_count / 2) - si->mean_ec += 1; - si->mean_ec += si->ec_sum; -} - -/** * validate_vid_hdr - check that volume identifier header is correct and * consistent. * @vid_hdr: the volume identifier header to check @@ -901,15 +881,8 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum adjust_mean_ec: if (!ec_corr) { - if (si->ec_sum + ec < ec) { - commit_to_mean_value(si); - si->ec_sum = 0; - si->ec_count = 0; - } else { - si->ec_sum += ec; - si->ec_count += 1; - } - + si->ec_sum += ec; + si->ec_count += 1; if (ec > si->max_ec) si->max_ec = ec; if (ec < si->min_ec) @@ -965,9 +938,11 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) dbg_msg("scanning is finished"); - /* Finish mean erase counter calculations */ - if (si->ec_count) - commit_to_mean_value(si); + /* Calculate mean erase counter */ + if (si->ec_count) { + do_div(si->ec_sum, si->ec_count); + si->mean_ec = si->ec_sum; + } if (si->is_empty) ubi_msg("empty MTD device detected"); diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 46d444af471a..966b9b682a42 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -124,7 +124,7 @@ struct ubi_scan_info { int max_ec; unsigned long long max_sqnum; int mean_ec; - int ec_sum; + uint64_t ec_sum; int ec_count; }; diff --git a/include/mtd/ubi-header.h b/drivers/mtd/ubi/ubi-media.h index 292f916ea564..c3185d9fd048 100644 --- a/include/mtd/ubi-header.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -24,11 +24,11 @@ /* * This file defines the layout of UBI headers and all the other UBI on-flash - * data structures. May be included by user-space. + * data structures. */ -#ifndef __UBI_HEADER_H__ -#define __UBI_HEADER_H__ +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ #include <asm/byteorder.h> @@ -369,4 +369,4 @@ struct ubi_vtbl_record { __be32 crc; } __attribute__ ((packed)); -#endif /* !__UBI_HEADER_H__ */ +#endif /* !__UBI_MEDIA_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a548c1d28fa8..67dcbd11c15c 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -37,10 +37,9 @@ #include <linux/string.h> #include <linux/vmalloc.h> #include <linux/mtd/mtd.h> - -#include <mtd/ubi-header.h> #include <linux/mtd/ubi.h> +#include "ubi-media.h" #include "scan.h" #include "debug.h" @@ -54,10 +53,10 @@ #define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) /* UBI warning messages */ #define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ - __FUNCTION__, ##__VA_ARGS__) + __func__, ##__VA_ARGS__) /* UBI error messages */ #define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ - __FUNCTION__, ##__VA_ARGS__) + __func__, ##__VA_ARGS__) /* Lowest number PEBs reserved for bad PEB handling */ #define MIN_RESEVED_PEBS 2 diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 978e20a1791b..1e39e78f1778 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -1248,3 +1248,4 @@ module_exit(at91ether_exit) MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AT91RM9200 EMAC Ethernet driver"); MODULE_AUTHOR("Andrew Victor"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 91a6590d107b..ecd8fc6146e9 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -897,6 +897,7 @@ static struct platform_driver ep93xx_eth_driver = { .remove = ep93xx_eth_remove, .driver = { .name = "ep93xx-eth", + .owner = THIS_MODULE, }, }; @@ -914,3 +915,4 @@ static void __exit ep93xx_eth_cleanup_module(void) module_init(ep93xx_eth_init_module); module_exit(ep93xx_eth_cleanup_module); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-eth"); diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 5586fc624688..0afe522b8f7b 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -91,6 +91,144 @@ #include "atlx.c" /* + * This is the only thing that needs to be changed to adjust the + * maximum number of ports that the driver can manage. + */ +#define ATL1_MAX_NIC 4 + +#define OPTION_UNSET -1 +#define OPTION_DISABLED 0 +#define OPTION_ENABLED 1 + +#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET } + +/* + * Interrupt Moderate Timer in units of 2 us + * + * Valid Range: 10-65535 + * + * Default Value: 100 (200us) + */ +static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; +static int num_int_mod_timer; +module_param_array_named(int_mod_timer, int_mod_timer, int, + &num_int_mod_timer, 0); +MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer"); + +#define DEFAULT_INT_MOD_CNT 100 /* 200us */ +#define MAX_INT_MOD_CNT 65000 +#define MIN_INT_MOD_CNT 50 + +struct atl1_option { + enum { enable_option, range_option, list_option } type; + char *name; + char *err; + int def; + union { + struct { /* range_option info */ + int min; + int max; + } r; + struct { /* list_option info */ + int nr; + struct atl1_opt_list { + int i; + char *str; + } *p; + } l; + } arg; +}; + +static int __devinit atl1_validate_option(int *value, struct atl1_option *opt, + struct pci_dev *pdev) +{ + if (*value == OPTION_UNSET) { + *value = opt->def; + return 0; + } + + switch (opt->type) { + case enable_option: + switch (*value) { + case OPTION_ENABLED: + dev_info(&pdev->dev, "%s enabled\n", opt->name); + return 0; + case OPTION_DISABLED: + dev_info(&pdev->dev, "%s disabled\n", opt->name); + return 0; + } + break; + case range_option: + if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { + dev_info(&pdev->dev, "%s set to %i\n", opt->name, + *value); + return 0; + } + break; + case list_option:{ + int i; + struct atl1_opt_list *ent; + + for (i = 0; i < opt->arg.l.nr; i++) { + ent = &opt->arg.l.p[i]; + if (*value == ent->i) { + if (ent->str[0] != '\0') + dev_info(&pdev->dev, "%s\n", + ent->str); + return 0; + } + } + } + break; + + default: + break; + } + + dev_info(&pdev->dev, "invalid %s specified (%i) %s\n", + opt->name, *value, opt->err); + *value = opt->def; + return -1; +} + +/* + * atl1_check_options - Range Checking for Command Line Parameters + * @adapter: board private structure + * + * This routine checks all command line parameters for valid user + * input. If an invalid value is given, or if no user specified + * value exists, a default value is used. The final value is stored + * in a variable in the adapter structure. + */ +void __devinit atl1_check_options(struct atl1_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int bd = adapter->bd_number; + if (bd >= ATL1_MAX_NIC) { + dev_notice(&pdev->dev, "no configuration for board#%i\n", bd); + dev_notice(&pdev->dev, "using defaults for all values\n"); + } + { /* Interrupt Moderate Timer */ + struct atl1_option opt = { + .type = range_option, + .name = "Interrupt Moderator Timer", + .err = "using default of " + __MODULE_STRING(DEFAULT_INT_MOD_CNT), + .def = DEFAULT_INT_MOD_CNT, + .arg = {.r = {.min = MIN_INT_MOD_CNT, + .max = MAX_INT_MOD_CNT} } + }; + int val; + if (num_int_mod_timer > bd) { + val = int_mod_timer[bd]; + atl1_validate_option(&val, &opt, pdev); + adapter->imt = (u16) val; + } else + adapter->imt = (u16) (opt.def); + } +} + +/* * atl1_pci_tbl - PCI Device ID Table */ static const struct pci_device_id atl1_pci_tbl[] = { diff --git a/drivers/net/atlx/atlx.c b/drivers/net/atlx/atlx.c index 4186326d1b94..f06b854e2501 100644 --- a/drivers/net/atlx/atlx.c +++ b/drivers/net/atlx/atlx.c @@ -253,181 +253,4 @@ static void atlx_restore_vlan(struct atlx_adapter *adapter) atlx_vlan_rx_register(adapter->netdev, adapter->vlgrp); } -/* - * This is the only thing that needs to be changed to adjust the - * maximum number of ports that the driver can manage. - */ -#define ATL1_MAX_NIC 4 - -#define OPTION_UNSET -1 -#define OPTION_DISABLED 0 -#define OPTION_ENABLED 1 - -#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET } - -/* - * Interrupt Moderate Timer in units of 2 us - * - * Valid Range: 10-65535 - * - * Default Value: 100 (200us) - */ -static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; -static int num_int_mod_timer; -module_param_array_named(int_mod_timer, int_mod_timer, int, - &num_int_mod_timer, 0); -MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer"); - -/* - * flash_vendor - * - * Valid Range: 0-2 - * - * 0 - Atmel - * 1 - SST - * 2 - ST - * - * Default Value: 0 - */ -static int __devinitdata flash_vendor[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT; -static int num_flash_vendor; -module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0); -MODULE_PARM_DESC(flash_vendor, "SPI flash vendor"); - -#define DEFAULT_INT_MOD_CNT 100 /* 200us */ -#define MAX_INT_MOD_CNT 65000 -#define MIN_INT_MOD_CNT 50 - -#define FLASH_VENDOR_DEFAULT 0 -#define FLASH_VENDOR_MIN 0 -#define FLASH_VENDOR_MAX 2 - -struct atl1_option { - enum { enable_option, range_option, list_option } type; - char *name; - char *err; - int def; - union { - struct { /* range_option info */ - int min; - int max; - } r; - struct { /* list_option info */ - int nr; - struct atl1_opt_list { - int i; - char *str; - } *p; - } l; - } arg; -}; - -static int __devinit atl1_validate_option(int *value, struct atl1_option *opt, - struct pci_dev *pdev) -{ - if (*value == OPTION_UNSET) { - *value = opt->def; - return 0; - } - - switch (opt->type) { - case enable_option: - switch (*value) { - case OPTION_ENABLED: - dev_info(&pdev->dev, "%s enabled\n", opt->name); - return 0; - case OPTION_DISABLED: - dev_info(&pdev->dev, "%s disabled\n", opt->name); - return 0; - } - break; - case range_option: - if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - dev_info(&pdev->dev, "%s set to %i\n", opt->name, - *value); - return 0; - } - break; - case list_option:{ - int i; - struct atl1_opt_list *ent; - - for (i = 0; i < opt->arg.l.nr; i++) { - ent = &opt->arg.l.p[i]; - if (*value == ent->i) { - if (ent->str[0] != '\0') - dev_info(&pdev->dev, "%s\n", - ent->str); - return 0; - } - } - } - break; - - default: - break; - } - - dev_info(&pdev->dev, "invalid %s specified (%i) %s\n", - opt->name, *value, opt->err); - *value = opt->def; - return -1; -} - -/* - * atl1_check_options - Range Checking for Command Line Parameters - * @adapter: board private structure - * - * This routine checks all command line parameters for valid user - * input. If an invalid value is given, or if no user specified - * value exists, a default value is used. The final value is stored - * in a variable in the adapter structure. - */ -void __devinit atl1_check_options(struct atl1_adapter *adapter) -{ - struct pci_dev *pdev = adapter->pdev; - int bd = adapter->bd_number; - if (bd >= ATL1_MAX_NIC) { - dev_notice(&pdev->dev, "no configuration for board#%i\n", bd); - dev_notice(&pdev->dev, "using defaults for all values\n"); - } - { /* Interrupt Moderate Timer */ - struct atl1_option opt = { - .type = range_option, - .name = "Interrupt Moderator Timer", - .err = "using default of " - __MODULE_STRING(DEFAULT_INT_MOD_CNT), - .def = DEFAULT_INT_MOD_CNT, - .arg = {.r = {.min = MIN_INT_MOD_CNT, - .max = MAX_INT_MOD_CNT} } - }; - int val; - if (num_int_mod_timer > bd) { - val = int_mod_timer[bd]; - atl1_validate_option(&val, &opt, pdev); - adapter->imt = (u16) val; - } else - adapter->imt = (u16) (opt.def); - } - - { /* Flash Vendor */ - struct atl1_option opt = { - .type = range_option, - .name = "SPI Flash Vendor", - .err = "using default of " - __MODULE_STRING(FLASH_VENDOR_DEFAULT), - .def = DEFAULT_INT_MOD_CNT, - .arg = {.r = {.min = FLASH_VENDOR_MIN, - .max = FLASH_VENDOR_MAX} } - }; - int val; - if (num_flash_vendor > bd) { - val = flash_vendor[bd]; - atl1_validate_option(&val, &opt, pdev); - adapter->hw.flash_vendor = (u8) val; - } else - adapter->hw.flash_vendor = (u8) (opt.def); - } -} - #endif /* ATLX_C */ diff --git a/drivers/net/ax88796.c b/drivers/net/ax88796.c index 194949afacd0..0b4adf4a0f7d 100644 --- a/drivers/net/ax88796.c +++ b/drivers/net/ax88796.c @@ -1005,3 +1005,4 @@ module_exit(axdrv_exit); MODULE_DESCRIPTION("AX88796 10/100 Ethernet platform driver"); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ax88796"); diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 717dcc1aa1e9..4fec8581bfd7 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -47,6 +47,7 @@ MODULE_AUTHOR(DRV_AUTHOR); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:bfin_mac"); #if defined(CONFIG_BFIN_MAC_USE_L1) # define bfin_mac_alloc(dma_handle, size) l1_data_sram_zalloc(size) @@ -1089,8 +1090,9 @@ static struct platform_driver bfin_mac_driver = { .resume = bfin_mac_resume, .suspend = bfin_mac_suspend, .driver = { - .name = DRV_NAME, - }, + .name = DRV_NAME, + .owner = THIS_MODULE, + }, }; static int __init bfin_mac_init(void) @@ -1106,3 +1108,4 @@ static void __exit bfin_mac_cleanup(void) } module_exit(bfin_mac_cleanup); + diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 9da7ff437031..2b5740b3d182 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -42,6 +42,7 @@ MODULE_AUTHOR("Eugene Konev <ejka@imfi.kspu.ru>"); MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cpmac"); static int debug_level = 8; static int dumb_switch; @@ -1103,6 +1104,7 @@ static int __devexit cpmac_remove(struct platform_device *pdev) static struct platform_driver cpmac_driver = { .driver.name = "cpmac", + .driver.owner = THIS_MODULE, .probe = cpmac_probe, .remove = __devexit_p(cpmac_remove), }; diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index d63cc93f055d..e6fe2614ea6d 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -1418,3 +1418,4 @@ module_exit(dm9000_cleanup); MODULE_AUTHOR("Sascha Hauer, Ben Dooks"); MODULE_DESCRIPTION("Davicom DM9000 network driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dm9000"); diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c index 01c88664bad3..462351ca2c81 100644 --- a/drivers/net/e1000e/82571.c +++ b/drivers/net/e1000e/82571.c @@ -1326,12 +1326,10 @@ struct e1000_info e1000_82571_info = { .mac = e1000_82571, .flags = FLAG_HAS_HW_VLAN_FILTER | FLAG_HAS_JUMBO_FRAMES - | FLAG_HAS_STATS_PTC_PRC | FLAG_HAS_WOL | FLAG_APME_IN_CTRL3 | FLAG_RX_CSUM_ENABLED | FLAG_HAS_CTRLEXT_ON_LOAD - | FLAG_HAS_STATS_ICR_ICT | FLAG_HAS_SMART_POWER_DOWN | FLAG_RESET_OVERWRITES_LAA /* errata */ | FLAG_TARC_SPEED_MODE_BIT /* errata */ @@ -1347,12 +1345,10 @@ struct e1000_info e1000_82572_info = { .mac = e1000_82572, .flags = FLAG_HAS_HW_VLAN_FILTER | FLAG_HAS_JUMBO_FRAMES - | FLAG_HAS_STATS_PTC_PRC | FLAG_HAS_WOL | FLAG_APME_IN_CTRL3 | FLAG_RX_CSUM_ENABLED | FLAG_HAS_CTRLEXT_ON_LOAD - | FLAG_HAS_STATS_ICR_ICT | FLAG_TARC_SPEED_MODE_BIT, /* errata */ .pba = 38, .get_variants = e1000_get_variants_82571, @@ -1365,11 +1361,9 @@ struct e1000_info e1000_82573_info = { .mac = e1000_82573, .flags = FLAG_HAS_HW_VLAN_FILTER | FLAG_HAS_JUMBO_FRAMES - | FLAG_HAS_STATS_PTC_PRC | FLAG_HAS_WOL | FLAG_APME_IN_CTRL3 | FLAG_RX_CSUM_ENABLED - | FLAG_HAS_STATS_ICR_ICT | FLAG_HAS_SMART_POWER_DOWN | FLAG_HAS_AMT | FLAG_HAS_ERT diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h index 572cfd44397a..2a53875cddbf 100644 --- a/drivers/net/e1000e/defines.h +++ b/drivers/net/e1000e/defines.h @@ -184,6 +184,7 @@ #define E1000_SWFW_EEP_SM 0x1 #define E1000_SWFW_PHY0_SM 0x2 #define E1000_SWFW_PHY1_SM 0x4 +#define E1000_SWFW_CSR_SM 0x8 /* Device Control */ #define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ @@ -527,8 +528,10 @@ #define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ #define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ #define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ #define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ #define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ /* NVM Control */ #define E1000_EECD_SK 0x00000001 /* NVM Clock */ diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index 5a89dff52264..38bfd0d261fe 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -64,11 +64,14 @@ struct e1000_info; /* Tx/Rx descriptor defines */ #define E1000_DEFAULT_TXD 256 #define E1000_MAX_TXD 4096 -#define E1000_MIN_TXD 80 +#define E1000_MIN_TXD 64 #define E1000_DEFAULT_RXD 256 #define E1000_MAX_RXD 4096 -#define E1000_MIN_RXD 80 +#define E1000_MIN_RXD 64 + +#define E1000_MIN_ITR_USECS 10 /* 100000 irq/sec */ +#define E1000_MAX_ITR_USECS 10000 /* 100 irq/sec */ /* Early Receive defines */ #define E1000_ERT_2048 0x100 @@ -147,6 +150,18 @@ struct e1000_ring { struct e1000_queue_stats stats; }; +/* PHY register snapshot values */ +struct e1000_phy_regs { + u16 bmcr; /* basic mode control register */ + u16 bmsr; /* basic mode status register */ + u16 advertise; /* auto-negotiation advertisement */ + u16 lpa; /* link partner ability register */ + u16 expansion; /* auto-negotiation expansion reg */ + u16 ctrl1000; /* 1000BASE-T control register */ + u16 stat1000; /* 1000BASE-T status register */ + u16 estatus; /* extended status register */ +}; + /* board specific private data structure */ struct e1000_adapter { struct timer_list watchdog_timer; @@ -202,8 +217,8 @@ struct e1000_adapter { /* Tx stats */ u64 tpt_old; u64 colc_old; - u64 gotcl_old; - u32 gotcl; + u32 gotc; + u64 gotc_old; u32 tx_timeout_count; u32 tx_fifo_head; u32 tx_head_addr; @@ -227,8 +242,8 @@ struct e1000_adapter { u64 hw_csum_err; u64 hw_csum_good; u64 rx_hdr_split; - u64 gorcl_old; - u32 gorcl; + u32 gorc; + u64 gorc_old; u32 alloc_rx_buff_failed; u32 rx_dma_failed; @@ -250,6 +265,9 @@ struct e1000_adapter { struct e1000_phy_info phy_info; struct e1000_phy_stats phy_stats; + /* Snapshot of PHY registers */ + struct e1000_phy_regs phy_regs; + struct e1000_ring test_tx_ring; struct e1000_ring test_rx_ring; u32 test_icr; @@ -286,8 +304,6 @@ struct e1000_info { #define FLAG_HAS_CTRLEXT_ON_LOAD (1 << 5) #define FLAG_HAS_SWSM_ON_LOAD (1 << 6) #define FLAG_HAS_JUMBO_FRAMES (1 << 7) -#define FLAG_HAS_STATS_ICR_ICT (1 << 9) -#define FLAG_HAS_STATS_PTC_PRC (1 << 10) #define FLAG_HAS_SMART_POWER_DOWN (1 << 11) #define FLAG_IS_QUAD_PORT_A (1 << 12) #define FLAG_IS_QUAD_PORT (1 << 13) @@ -433,6 +449,8 @@ extern s32 e1000e_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data); extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, u32 usec_interval, bool *success); extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw); +extern s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); +extern s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); extern s32 e1000e_check_downshift(struct e1000_hw *hw); static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw) diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c index d59a99ae44be..dc552d7d6fac 100644 --- a/drivers/net/e1000e/es2lan.c +++ b/drivers/net/e1000e/es2lan.c @@ -41,6 +41,7 @@ #define E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL 0x00 #define E1000_KMRNCTRLSTA_OFFSET_INB_CTRL 0x02 #define E1000_KMRNCTRLSTA_OFFSET_HD_CTRL 0x10 +#define E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE 0x1F #define E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS 0x0008 #define E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS 0x0800 @@ -48,6 +49,7 @@ #define E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT 0x0004 #define E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT 0x0000 +#define E1000_KMRNCTRLSTA_OPMODE_E_IDLE 0x2000 #define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ #define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN 0x00010000 @@ -85,6 +87,9 @@ /* Kumeran Mode Control Register (Page 193, Register 16) */ #define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 +/* Max number of times Kumeran read/write should be validated */ +#define GG82563_MAX_KMRN_RETRY 0x5 + /* Power Management Control Register (Page 193, Register 20) */ #define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enable SERDES Electrical Idle */ @@ -270,6 +275,7 @@ static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw) u16 mask; mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + mask |= E1000_SWFW_CSR_SM; return e1000_acquire_swfw_sync_80003es2lan(hw, mask); } @@ -286,6 +292,8 @@ static void e1000_release_phy_80003es2lan(struct e1000_hw *hw) u16 mask; mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + mask |= E1000_SWFW_CSR_SM; + e1000_release_swfw_sync_80003es2lan(hw, mask); } @@ -410,20 +418,27 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 page_select; u16 temp; + ret_val = e1000_acquire_phy_80003es2lan(hw); + if (ret_val) + return ret_val; + /* Select Configuration Page */ - if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) + if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; - else + } else { /* * Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; + } temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT); - ret_val = e1000e_write_phy_reg_m88(hw, page_select, temp); - if (ret_val) + ret_val = e1000e_write_phy_reg_mdic(hw, page_select, temp); + if (ret_val) { + e1000_release_phy_80003es2lan(hw); return ret_val; + } /* * The "ready" bit in the MDIC register may be incorrectly set @@ -433,20 +448,21 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, udelay(200); /* ...and verify the command was successful. */ - ret_val = e1000e_read_phy_reg_m88(hw, page_select, &temp); + ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { ret_val = -E1000_ERR_PHY; + e1000_release_phy_80003es2lan(hw); return ret_val; } udelay(200); - ret_val = e1000e_read_phy_reg_m88(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); udelay(200); + e1000_release_phy_80003es2lan(hw); return ret_val; } @@ -467,20 +483,27 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 page_select; u16 temp; + ret_val = e1000_acquire_phy_80003es2lan(hw); + if (ret_val) + return ret_val; + /* Select Configuration Page */ - if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) + if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; - else + } else { /* * Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; + } temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT); - ret_val = e1000e_write_phy_reg_m88(hw, page_select, temp); - if (ret_val) + ret_val = e1000e_write_phy_reg_mdic(hw, page_select, temp); + if (ret_val) { + e1000_release_phy_80003es2lan(hw); return ret_val; + } /* @@ -491,18 +514,20 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, udelay(200); /* ...and verify the command was successful. */ - ret_val = e1000e_read_phy_reg_m88(hw, page_select, &temp); + ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); - if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) + if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { + e1000_release_phy_80003es2lan(hw); return -E1000_ERR_PHY; + } udelay(200); - ret_val = e1000e_write_phy_reg_m88(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); udelay(200); + e1000_release_phy_80003es2lan(hw); return ret_val; } @@ -882,10 +907,10 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) struct e1000_phy_info *phy = &hw->phy; s32 ret_val; u32 ctrl_ext; - u16 data; + u32 i = 0; + u16 data, data2; - ret_val = e1e_rphy(hw, GG82563_PHY_MAC_SPEC_CTRL, - &data); + ret_val = e1e_rphy(hw, GG82563_PHY_MAC_SPEC_CTRL, &data); if (ret_val) return ret_val; @@ -893,8 +918,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) /* Use 25MHz for both link down and 1000Base-T for Tx clock. */ data |= GG82563_MSCR_TX_CLK_1000MBPS_25; - ret_val = e1e_wphy(hw, GG82563_PHY_MAC_SPEC_CTRL, - data); + ret_val = e1e_wphy(hw, GG82563_PHY_MAC_SPEC_CTRL, data); if (ret_val) return ret_val; @@ -954,6 +978,18 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; + ret_val = e1000e_read_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, + &data); + if (ret_val) + return ret_val; + data |= E1000_KMRNCTRLSTA_OPMODE_E_IDLE; + ret_val = e1000e_write_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE, + data); + if (ret_val) + return ret_val; + ret_val = e1e_rphy(hw, GG82563_PHY_SPEC_CTRL_2, &data); if (ret_val) return ret_val; @@ -983,9 +1019,18 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; - ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, &data); - if (ret_val) - return ret_val; + do { + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, + &data); + if (ret_val) + return ret_val; + + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, + &data2); + if (ret_val) + return ret_val; + i++; + } while ((data != data2) && (i < GG82563_MAX_KMRN_RETRY)); data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; ret_val = e1e_wphy(hw, GG82563_PHY_KMRN_MODE_CTRL, data); @@ -1074,7 +1119,8 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) { s32 ret_val; u32 tipg; - u16 reg_data; + u32 i = 0; + u16 reg_data, reg_data2; reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT; ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, @@ -1088,9 +1134,16 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) tipg |= DEFAULT_TIPG_IPGT_10_100_80003ES2LAN; ew32(TIPG, tipg); - ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); - if (ret_val) - return ret_val; + do { + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + if (ret_val) + return ret_val; + + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data2); + if (ret_val) + return ret_val; + i++; + } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); if (duplex == HALF_DUPLEX) reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; @@ -1112,8 +1165,9 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) { s32 ret_val; - u16 reg_data; + u16 reg_data, reg_data2; u32 tipg; + u32 i = 0; reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT; ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, @@ -1127,9 +1181,16 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) tipg |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN; ew32(TIPG, tipg); - ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); - if (ret_val) - return ret_val; + do { + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + if (ret_val) + return ret_val; + + ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data2); + if (ret_val) + return ret_val; + i++; + } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; ret_val = e1e_wphy(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); @@ -1231,12 +1292,10 @@ struct e1000_info e1000_es2_info = { .mac = e1000_80003es2lan, .flags = FLAG_HAS_HW_VLAN_FILTER | FLAG_HAS_JUMBO_FRAMES - | FLAG_HAS_STATS_PTC_PRC | FLAG_HAS_WOL | FLAG_APME_IN_CTRL3 | FLAG_RX_CSUM_ENABLED | FLAG_HAS_CTRLEXT_ON_LOAD - | FLAG_HAS_STATS_ICR_ICT | FLAG_RX_NEEDS_RESTART /* errata */ | FLAG_TARC_SET_BIT_ZERO /* errata */ | FLAG_APME_CHECK_PORT_B diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index 6d1b257bbda6..ce045acce63e 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -46,8 +46,8 @@ struct e1000_stats { static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_packets", E1000_STAT(stats.gprc) }, { "tx_packets", E1000_STAT(stats.gptc) }, - { "rx_bytes", E1000_STAT(stats.gorcl) }, - { "tx_bytes", E1000_STAT(stats.gotcl) }, + { "rx_bytes", E1000_STAT(stats.gorc) }, + { "tx_bytes", E1000_STAT(stats.gotc) }, { "rx_broadcast", E1000_STAT(stats.bprc) }, { "tx_broadcast", E1000_STAT(stats.bptc) }, { "rx_multicast", E1000_STAT(stats.mprc) }, @@ -83,7 +83,7 @@ static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_flow_control_xoff", E1000_STAT(stats.xoffrxc) }, { "tx_flow_control_xon", E1000_STAT(stats.xontxc) }, { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) }, - { "rx_long_byte_count", E1000_STAT(stats.gorcl) }, + { "rx_long_byte_count", E1000_STAT(stats.gorc) }, { "rx_csum_offload_good", E1000_STAT(hw_csum_good) }, { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) }, { "rx_header_split", E1000_STAT(rx_hdr_split) }, @@ -1770,6 +1770,47 @@ static int e1000_phys_id(struct net_device *netdev, u32 data) return 0; } +static int e1000_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + + if (adapter->itr_setting <= 3) + ec->rx_coalesce_usecs = adapter->itr_setting; + else + ec->rx_coalesce_usecs = 1000000 / adapter->itr_setting; + + return 0; +} + +static int e1000_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + + if ((ec->rx_coalesce_usecs > E1000_MAX_ITR_USECS) || + ((ec->rx_coalesce_usecs > 3) && + (ec->rx_coalesce_usecs < E1000_MIN_ITR_USECS)) || + (ec->rx_coalesce_usecs == 2)) + return -EINVAL; + + if (ec->rx_coalesce_usecs <= 3) { + adapter->itr = 20000; + adapter->itr_setting = ec->rx_coalesce_usecs; + } else { + adapter->itr = (1000000 / ec->rx_coalesce_usecs); + adapter->itr_setting = adapter->itr & ~3; + } + + if (adapter->itr_setting != 0) + ew32(ITR, 1000000000 / (adapter->itr * 256)); + else + ew32(ITR, 0); + + return 0; +} + static int e1000_nway_reset(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); @@ -1845,6 +1886,8 @@ static const struct ethtool_ops e1000_ethtool_ops = { .phys_id = e1000_phys_id, .get_ethtool_stats = e1000_get_ethtool_stats, .get_sset_count = e1000e_get_sset_count, + .get_coalesce = e1000_get_coalesce, + .set_coalesce = e1000_set_coalesce, }; void e1000e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index 53f1ac6327fa..a930e6d9cf02 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -592,10 +592,8 @@ struct e1000_hw_stats { u64 bprc; u64 mprc; u64 gptc; - u64 gorcl; - u64 gorch; - u64 gotcl; - u64 gotch; + u64 gorc; + u64 gotc; u64 rnbc; u64 ruc; u64 rfc; @@ -604,10 +602,8 @@ struct e1000_hw_stats { u64 mgprc; u64 mgpdc; u64 mgptc; - u64 torl; - u64 torh; - u64 totl; - u64 toth; + u64 tor; + u64 tot; u64 tpr; u64 tpt; u64 ptc64; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index c8dc47fd132a..8991ab8911e2 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -46,7 +46,7 @@ #include "e1000.h" -#define DRV_VERSION "0.2.0" +#define DRV_VERSION "0.2.1" char e1000e_driver_name[] = "e1000e"; const char e1000e_driver_version[] = DRV_VERSION; @@ -466,10 +466,10 @@ next_desc: if (cleaned_count) adapter->alloc_rx_buf(adapter, cleaned_count); - adapter->total_rx_packets += total_rx_packets; adapter->total_rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + adapter->total_rx_packets += total_rx_packets; adapter->net_stats.rx_bytes += total_rx_bytes; + adapter->net_stats.rx_packets += total_rx_packets; return cleaned; } @@ -606,8 +606,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) } adapter->total_tx_bytes += total_tx_bytes; adapter->total_tx_packets += total_tx_packets; - adapter->net_stats.tx_packets += total_tx_packets; adapter->net_stats.tx_bytes += total_tx_bytes; + adapter->net_stats.tx_packets += total_tx_packets; return cleaned; } @@ -775,10 +775,10 @@ next_desc: if (cleaned_count) adapter->alloc_rx_buf(adapter, cleaned_count); - adapter->total_rx_packets += total_rx_packets; adapter->total_rx_bytes += total_rx_bytes; - adapter->net_stats.rx_packets += total_rx_packets; + adapter->total_rx_packets += total_rx_packets; adapter->net_stats.rx_bytes += total_rx_bytes; + adapter->net_stats.rx_packets += total_rx_packets; return cleaned; } @@ -2506,56 +2506,27 @@ void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.crcerrs += er32(CRCERRS); adapter->stats.gprc += er32(GPRC); - adapter->stats.gorcl += er32(GORCL); - adapter->stats.gorch += er32(GORCH); + adapter->stats.gorc += er32(GORCL); + er32(GORCH); /* Clear gorc */ adapter->stats.bprc += er32(BPRC); adapter->stats.mprc += er32(MPRC); adapter->stats.roc += er32(ROC); - if (adapter->flags & FLAG_HAS_STATS_PTC_PRC) { - adapter->stats.prc64 += er32(PRC64); - adapter->stats.prc127 += er32(PRC127); - adapter->stats.prc255 += er32(PRC255); - adapter->stats.prc511 += er32(PRC511); - adapter->stats.prc1023 += er32(PRC1023); - adapter->stats.prc1522 += er32(PRC1522); - adapter->stats.symerrs += er32(SYMERRS); - adapter->stats.sec += er32(SEC); - } - adapter->stats.mpc += er32(MPC); adapter->stats.scc += er32(SCC); adapter->stats.ecol += er32(ECOL); adapter->stats.mcc += er32(MCC); adapter->stats.latecol += er32(LATECOL); adapter->stats.dc += er32(DC); - adapter->stats.rlec += er32(RLEC); adapter->stats.xonrxc += er32(XONRXC); adapter->stats.xontxc += er32(XONTXC); adapter->stats.xoffrxc += er32(XOFFRXC); adapter->stats.xofftxc += er32(XOFFTXC); - adapter->stats.fcruc += er32(FCRUC); adapter->stats.gptc += er32(GPTC); - adapter->stats.gotcl += er32(GOTCL); - adapter->stats.gotch += er32(GOTCH); + adapter->stats.gotc += er32(GOTCL); + er32(GOTCH); /* Clear gotc */ adapter->stats.rnbc += er32(RNBC); adapter->stats.ruc += er32(RUC); - adapter->stats.rfc += er32(RFC); - adapter->stats.rjc += er32(RJC); - adapter->stats.torl += er32(TORL); - adapter->stats.torh += er32(TORH); - adapter->stats.totl += er32(TOTL); - adapter->stats.toth += er32(TOTH); - adapter->stats.tpr += er32(TPR); - - if (adapter->flags & FLAG_HAS_STATS_PTC_PRC) { - adapter->stats.ptc64 += er32(PTC64); - adapter->stats.ptc127 += er32(PTC127); - adapter->stats.ptc255 += er32(PTC255); - adapter->stats.ptc511 += er32(PTC511); - adapter->stats.ptc1023 += er32(PTC1023); - adapter->stats.ptc1522 += er32(PTC1522); - } adapter->stats.mptc += er32(MPTC); adapter->stats.bptc += er32(BPTC); @@ -2574,19 +2545,6 @@ void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.tsctc += er32(TSCTC); adapter->stats.tsctfc += er32(TSCTFC); - adapter->stats.iac += er32(IAC); - - if (adapter->flags & FLAG_HAS_STATS_ICR_ICT) { - adapter->stats.icrxoc += er32(ICRXOC); - adapter->stats.icrxptc += er32(ICRXPTC); - adapter->stats.icrxatc += er32(ICRXATC); - adapter->stats.ictxptc += er32(ICTXPTC); - adapter->stats.ictxatc += er32(ICTXATC); - adapter->stats.ictxqec += er32(ICTXQEC); - adapter->stats.ictxqmtc += er32(ICTXQMTC); - adapter->stats.icrxdmtc += er32(ICRXDMTC); - } - /* Fill out the OS statistics structure */ adapter->net_stats.multicast = adapter->stats.mprc; adapter->net_stats.collisions = adapter->stats.colc; @@ -2633,6 +2591,54 @@ void e1000e_update_stats(struct e1000_adapter *adapter) spin_unlock_irqrestore(&adapter->stats_lock, irq_flags); } +/** + * e1000_phy_read_status - Update the PHY register status snapshot + * @adapter: board private structure + **/ +static void e1000_phy_read_status(struct e1000_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + struct e1000_phy_regs *phy = &adapter->phy_regs; + int ret_val; + unsigned long irq_flags; + + + spin_lock_irqsave(&adapter->stats_lock, irq_flags); + + if ((er32(STATUS) & E1000_STATUS_LU) && + (adapter->hw.phy.media_type == e1000_media_type_copper)) { + ret_val = e1e_rphy(hw, PHY_CONTROL, &phy->bmcr); + ret_val |= e1e_rphy(hw, PHY_STATUS, &phy->bmsr); + ret_val |= e1e_rphy(hw, PHY_AUTONEG_ADV, &phy->advertise); + ret_val |= e1e_rphy(hw, PHY_LP_ABILITY, &phy->lpa); + ret_val |= e1e_rphy(hw, PHY_AUTONEG_EXP, &phy->expansion); + ret_val |= e1e_rphy(hw, PHY_1000T_CTRL, &phy->ctrl1000); + ret_val |= e1e_rphy(hw, PHY_1000T_STATUS, &phy->stat1000); + ret_val |= e1e_rphy(hw, PHY_EXT_STATUS, &phy->estatus); + if (ret_val) + ndev_warn(adapter->netdev, + "Error reading PHY register\n"); + } else { + /* + * Do not read PHY registers if link is not up + * Set values to typical power-on defaults + */ + phy->bmcr = (BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_FULLDPLX); + phy->bmsr = (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | + BMSR_10HALF | BMSR_ESTATEN | BMSR_ANEGCAPABLE | + BMSR_ERCAP); + phy->advertise = (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP | + ADVERTISE_ALL | ADVERTISE_CSMA); + phy->lpa = 0; + phy->expansion = EXPANSION_ENABLENPAGE; + phy->ctrl1000 = ADVERTISE_1000FULL; + phy->stat1000 = 0; + phy->estatus = (ESTATUS_1000_TFULL | ESTATUS_1000_THALF); + } + + spin_unlock_irqrestore(&adapter->stats_lock, irq_flags); +} + static void e1000_print_link_info(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; @@ -2745,6 +2751,7 @@ static void e1000_watchdog_task(struct work_struct *work) if (!netif_carrier_ok(netdev)) { bool txb2b = 1; /* update snapshot of PHY registers on LSC */ + e1000_phy_read_status(adapter); mac->ops.get_link_up_info(&adapter->hw, &adapter->link_speed, &adapter->link_duplex); @@ -2842,10 +2849,10 @@ link_up: mac->collision_delta = adapter->stats.colc - adapter->colc_old; adapter->colc_old = adapter->stats.colc; - adapter->gorcl = adapter->stats.gorcl - adapter->gorcl_old; - adapter->gorcl_old = adapter->stats.gorcl; - adapter->gotcl = adapter->stats.gotcl - adapter->gotcl_old; - adapter->gotcl_old = adapter->stats.gotcl; + adapter->gorc = adapter->stats.gorc - adapter->gorc_old; + adapter->gorc_old = adapter->stats.gorc; + adapter->gotc = adapter->stats.gotc - adapter->gotc_old; + adapter->gotc_old = adapter->stats.gotc; e1000e_update_adaptive(&adapter->hw); @@ -3500,7 +3507,6 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, { struct e1000_adapter *adapter = netdev_priv(netdev); struct mii_ioctl_data *data = if_mii(ifr); - unsigned long irq_flags; if (adapter->hw.phy.media_type != e1000_media_type_copper) return -EOPNOTSUPP; @@ -3512,13 +3518,40 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, case SIOCGMIIREG: if (!capable(CAP_NET_ADMIN)) return -EPERM; - spin_lock_irqsave(&adapter->stats_lock, irq_flags); - if (e1e_rphy(&adapter->hw, data->reg_num & 0x1F, - &data->val_out)) { - spin_unlock_irqrestore(&adapter->stats_lock, irq_flags); + switch (data->reg_num & 0x1F) { + case MII_BMCR: + data->val_out = adapter->phy_regs.bmcr; + break; + case MII_BMSR: + data->val_out = adapter->phy_regs.bmsr; + break; + case MII_PHYSID1: + data->val_out = (adapter->hw.phy.id >> 16); + break; + case MII_PHYSID2: + data->val_out = (adapter->hw.phy.id & 0xFFFF); + break; + case MII_ADVERTISE: + data->val_out = adapter->phy_regs.advertise; + break; + case MII_LPA: + data->val_out = adapter->phy_regs.lpa; + break; + case MII_EXPANSION: + data->val_out = adapter->phy_regs.expansion; + break; + case MII_CTRL1000: + data->val_out = adapter->phy_regs.ctrl1000; + break; + case MII_STAT1000: + data->val_out = adapter->phy_regs.stat1000; + break; + case MII_ESTATUS: + data->val_out = adapter->phy_regs.estatus; + break; + default: return -EIO; } - spin_unlock_irqrestore(&adapter->stats_lock, irq_flags); break; case SIOCSMIIREG: default: @@ -3774,6 +3807,7 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); + pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); @@ -3900,6 +3934,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, goto err_pci_reg; pci_set_master(pdev); + pci_save_state(pdev); err = -ENOMEM; netdev = alloc_etherdev(sizeof(struct e1000_adapter)); diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c index 3a4574caa75b..e102332a6bee 100644 --- a/drivers/net/e1000e/phy.c +++ b/drivers/net/e1000e/phy.c @@ -116,7 +116,7 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw) } /** - * e1000_read_phy_reg_mdic - Read MDI control register + * e1000e_read_phy_reg_mdic - Read MDI control register * @hw: pointer to the HW structure * @offset: register offset to be read * @data: pointer to the read data @@ -124,7 +124,7 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw) * Reads the MDI control register in the PHY at offset and stores the * information read to data. **/ -static s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; @@ -150,7 +150,7 @@ static s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) * Increasing the time out as testing showed failures with * the lower time out */ - for (i = 0; i < 64; i++) { + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { udelay(50); mdic = er32(MDIC); if (mdic & E1000_MDIC_READY) @@ -170,14 +170,14 @@ static s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) } /** - * e1000_write_phy_reg_mdic - Write MDI control register + * e1000e_write_phy_reg_mdic - Write MDI control register * @hw: pointer to the HW structure * @offset: register offset to write to * @data: data to write to register at offset * * Writes data to MDI control register in the PHY at offset. **/ -static s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) { struct e1000_phy_info *phy = &hw->phy; u32 i, mdic = 0; @@ -199,9 +199,13 @@ static s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) ew32(MDIC, mdic); - /* Poll the ready bit to see if the MDI read completed */ - for (i = 0; i < E1000_GEN_POLL_TIMEOUT; i++) { - udelay(5); + /* + * Poll the ready bit to see if the MDI read completed + * Increasing the time out as testing showed failures with + * the lower time out + */ + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { + udelay(50); mdic = er32(MDIC); if (mdic & E1000_MDIC_READY) break; @@ -210,6 +214,10 @@ static s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) hw_dbg(hw, "MDI Write did not complete\n"); return -E1000_ERR_PHY; } + if (mdic & E1000_MDIC_ERROR) { + hw_dbg(hw, "MDI Error\n"); + return -E1000_ERR_PHY; + } return 0; } @@ -232,9 +240,8 @@ s32 e1000e_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) if (ret_val) return ret_val; - ret_val = e1000_read_phy_reg_mdic(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); hw->phy.ops.release_phy(hw); @@ -258,9 +265,8 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) if (ret_val) return ret_val; - ret_val = e1000_write_phy_reg_mdic(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); hw->phy.ops.release_phy(hw); @@ -286,18 +292,17 @@ s32 e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) return ret_val; if (offset > MAX_PHY_MULTI_PAGE_REG) { - ret_val = e1000_write_phy_reg_mdic(hw, - IGP01E1000_PHY_PAGE_SELECT, - (u16)offset); + ret_val = e1000e_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); if (ret_val) { hw->phy.ops.release_phy(hw); return ret_val; } } - ret_val = e1000_read_phy_reg_mdic(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); hw->phy.ops.release_phy(hw); @@ -322,18 +327,17 @@ s32 e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) return ret_val; if (offset > MAX_PHY_MULTI_PAGE_REG) { - ret_val = e1000_write_phy_reg_mdic(hw, - IGP01E1000_PHY_PAGE_SELECT, - (u16)offset); + ret_val = e1000e_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); if (ret_val) { hw->phy.ops.release_phy(hw); return ret_val; } } - ret_val = e1000_write_phy_reg_mdic(hw, - MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); hw->phy.ops.release_phy(hw); @@ -420,7 +424,9 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + /* For newer PHYs this bit is downshift enable */ + if (phy->type == e1000_phy_m88) + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; /* * Options: @@ -463,7 +469,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - if (phy->revision < 4) { + if ((phy->type == e1000_phy_m88) && (phy->revision < 4)) { /* * Force TX_CLK in the Extended PHY Specific Control Register * to 25MHz clock. @@ -518,8 +524,11 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw) return ret_val; } - /* Wait 15ms for MAC to configure PHY from NVM settings. */ - msleep(15); + /* + * Wait 100ms for MAC to configure PHY from NVM settings, to avoid + * timeout issues when LFS is enabled. + */ + msleep(100); /* disable lplu d0 during driver init */ ret_val = e1000_set_d0_lplu_state(hw, 0); @@ -1152,9 +1161,7 @@ s32 e1000e_set_d3_lplu_state(struct e1000_hw *hw, bool active) if (!active) { data &= ~IGP02E1000_PM_D3_LPLU; - ret_val = e1e_wphy(hw, - IGP02E1000_PHY_POWER_MGMT, - data); + ret_val = e1e_wphy(hw, IGP02E1000_PHY_POWER_MGMT, data); if (ret_val) return ret_val; /* diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 9ff7538b7595..f9bc21c74b59 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -2611,7 +2611,7 @@ static int ehea_stop(struct net_device *dev) return ret; } -void ehea_purge_sq(struct ehea_qp *orig_qp) +static void ehea_purge_sq(struct ehea_qp *orig_qp) { struct ehea_qp qp = *orig_qp; struct ehea_qp_init_attr *init_attr = &qp.init_attr; @@ -2625,7 +2625,7 @@ void ehea_purge_sq(struct ehea_qp *orig_qp) } } -void ehea_flush_sq(struct ehea_port *port) +static void ehea_flush_sq(struct ehea_port *port) { int i; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 8c4214b0ee1f..35f66d4a4595 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -96,6 +96,7 @@ #define DEV_HAS_PAUSEFRAME_TX_V2 0x10000 /* device supports tx pause frames version 2 */ #define DEV_HAS_PAUSEFRAME_TX_V3 0x20000 /* device supports tx pause frames version 3 */ #define DEV_NEED_TX_LIMIT 0x40000 /* device needs to limit tx */ +#define DEV_HAS_GEAR_MODE 0x80000 /* device supports gear mode */ enum { NvRegIrqStatus = 0x000, @@ -174,11 +175,13 @@ enum { NvRegReceiverStatus = 0x98, #define NVREG_RCVSTAT_BUSY 0x01 - NvRegRandomSeed = 0x9c, -#define NVREG_RNDSEED_MASK 0x00ff -#define NVREG_RNDSEED_FORCE 0x7f00 -#define NVREG_RNDSEED_FORCE2 0x2d00 -#define NVREG_RNDSEED_FORCE3 0x7400 + NvRegSlotTime = 0x9c, +#define NVREG_SLOTTIME_LEGBF_ENABLED 0x80000000 +#define NVREG_SLOTTIME_10_100_FULL 0x00007f00 +#define NVREG_SLOTTIME_1000_FULL 0x0003ff00 +#define NVREG_SLOTTIME_HALF 0x0000ff00 +#define NVREG_SLOTTIME_DEFAULT 0x00007f00 +#define NVREG_SLOTTIME_MASK 0x000000ff NvRegTxDeferral = 0xA0, #define NVREG_TX_DEFERRAL_DEFAULT 0x15050f @@ -201,6 +204,11 @@ enum { NvRegPhyInterface = 0xC0, #define PHY_RGMII 0x10000000 + NvRegBackOffControl = 0xC4, +#define NVREG_BKOFFCTRL_DEFAULT 0x70000000 +#define NVREG_BKOFFCTRL_SEED_MASK 0x000003ff +#define NVREG_BKOFFCTRL_SELECT 24 +#define NVREG_BKOFFCTRL_GEAR 12 NvRegTxRingPhysAddr = 0x100, NvRegRxRingPhysAddr = 0x104, @@ -352,6 +360,7 @@ union ring_type { #define NV_TX_LASTPACKET (1<<16) #define NV_TX_RETRYERROR (1<<19) +#define NV_TX_RETRYCOUNT_MASK (0xF<<20) #define NV_TX_FORCED_INTERRUPT (1<<24) #define NV_TX_DEFERRED (1<<26) #define NV_TX_CARRIERLOST (1<<27) @@ -362,6 +371,7 @@ union ring_type { #define NV_TX2_LASTPACKET (1<<29) #define NV_TX2_RETRYERROR (1<<18) +#define NV_TX2_RETRYCOUNT_MASK (0xF<<19) #define NV_TX2_FORCED_INTERRUPT (1<<30) #define NV_TX2_DEFERRED (1<<25) #define NV_TX2_CARRIERLOST (1<<26) @@ -473,16 +483,22 @@ union ring_type { #define DESC_VER_3 3 /* PHY defines */ -#define PHY_OUI_MARVELL 0x5043 -#define PHY_OUI_CICADA 0x03f1 -#define PHY_OUI_VITESSE 0x01c1 -#define PHY_OUI_REALTEK 0x0732 +#define PHY_OUI_MARVELL 0x5043 +#define PHY_OUI_CICADA 0x03f1 +#define PHY_OUI_VITESSE 0x01c1 +#define PHY_OUI_REALTEK 0x0732 +#define PHY_OUI_REALTEK2 0x0020 #define PHYID1_OUI_MASK 0x03ff #define PHYID1_OUI_SHFT 6 #define PHYID2_OUI_MASK 0xfc00 #define PHYID2_OUI_SHFT 10 #define PHYID2_MODEL_MASK 0x03f0 -#define PHY_MODEL_MARVELL_E3016 0x220 +#define PHY_MODEL_REALTEK_8211 0x0110 +#define PHY_REV_MASK 0x0001 +#define PHY_REV_REALTEK_8211B 0x0000 +#define PHY_REV_REALTEK_8211C 0x0001 +#define PHY_MODEL_REALTEK_8201 0x0200 +#define PHY_MODEL_MARVELL_E3016 0x0220 #define PHY_MARVELL_E3016_INITMASK 0x0300 #define PHY_CICADA_INIT1 0x0f000 #define PHY_CICADA_INIT2 0x0e00 @@ -509,10 +525,18 @@ union ring_type { #define PHY_REALTEK_INIT_REG1 0x1f #define PHY_REALTEK_INIT_REG2 0x19 #define PHY_REALTEK_INIT_REG3 0x13 +#define PHY_REALTEK_INIT_REG4 0x14 +#define PHY_REALTEK_INIT_REG5 0x18 +#define PHY_REALTEK_INIT_REG6 0x11 #define PHY_REALTEK_INIT1 0x0000 #define PHY_REALTEK_INIT2 0x8e00 #define PHY_REALTEK_INIT3 0x0001 #define PHY_REALTEK_INIT4 0xad17 +#define PHY_REALTEK_INIT5 0xfb54 +#define PHY_REALTEK_INIT6 0xf5c7 +#define PHY_REALTEK_INIT7 0x1000 +#define PHY_REALTEK_INIT8 0x0003 +#define PHY_REALTEK_INIT_MSK1 0x0003 #define PHY_GIGABIT 0x0100 @@ -691,6 +715,7 @@ struct fe_priv { int wolenabled; unsigned int phy_oui; unsigned int phy_model; + unsigned int phy_rev; u16 gigabit; int intr_test; int recover_error; @@ -704,6 +729,7 @@ struct fe_priv { u32 txrxctl_bits; u32 vlanctl_bits; u32 driver_data; + u32 device_id; u32 register_size; int rx_csum; u32 mac_in_use; @@ -814,6 +840,16 @@ enum { }; static int dma_64bit = NV_DMA_64BIT_ENABLED; +/* + * Crossover Detection + * Realtek 8201 phy + some OEM boards do not work properly. + */ +enum { + NV_CROSSOVER_DETECTION_DISABLED, + NV_CROSSOVER_DETECTION_ENABLED +}; +static int phy_cross = NV_CROSSOVER_DETECTION_DISABLED; + static inline struct fe_priv *get_nvpriv(struct net_device *dev) { return netdev_priv(dev); @@ -1078,25 +1114,53 @@ static int phy_init(struct net_device *dev) } } if (np->phy_oui == PHY_OUI_REALTEK) { - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; + if (np->phy_model == PHY_MODEL_REALTEK_8211 && + np->phy_rev == PHY_REV_REALTEK_8211B) { + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; + if (np->phy_model == PHY_MODEL_REALTEK_8201) { + if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) { + phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ); + phy_reserved |= PHY_REALTEK_INIT7; + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + } } } @@ -1236,26 +1300,71 @@ static int phy_init(struct net_device *dev) } } if (np->phy_oui == PHY_OUI_REALTEK) { - /* reset could have cleared these out, set them back */ - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; - } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; + if (np->phy_model == PHY_MODEL_REALTEK_8211 && + np->phy_rev == PHY_REV_REALTEK_8211B) { + /* reset could have cleared these out, set them back */ + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } } - if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { - printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); - return PHY_ERROR; + if (np->phy_model == PHY_MODEL_REALTEK_8201) { + if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 || + np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) { + phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ); + phy_reserved |= PHY_REALTEK_INIT7; + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + } + if (phy_cross == NV_CROSSOVER_DETECTION_DISABLED) { + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ); + phy_reserved &= ~PHY_REALTEK_INIT_MSK1; + phy_reserved |= PHY_REALTEK_INIT3; + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { + printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); + return PHY_ERROR; + } + } } } @@ -1769,6 +1878,115 @@ static inline u32 nv_get_empty_tx_slots(struct fe_priv *np) return (u32)(np->tx_ring_size - ((np->tx_ring_size + (np->put_tx_ctx - np->get_tx_ctx)) % np->tx_ring_size)); } +static void nv_legacybackoff_reseed(struct net_device *dev) +{ + u8 __iomem *base = get_hwbase(dev); + u32 reg; + u32 low; + int tx_status = 0; + + reg = readl(base + NvRegSlotTime) & ~NVREG_SLOTTIME_MASK; + get_random_bytes(&low, sizeof(low)); + reg |= low & NVREG_SLOTTIME_MASK; + + /* Need to stop tx before change takes effect. + * Caller has already gained np->lock. + */ + tx_status = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_START; + if (tx_status) + nv_stop_tx(dev); + nv_stop_rx(dev); + writel(reg, base + NvRegSlotTime); + if (tx_status) + nv_start_tx(dev); + nv_start_rx(dev); +} + +/* Gear Backoff Seeds */ +#define BACKOFF_SEEDSET_ROWS 8 +#define BACKOFF_SEEDSET_LFSRS 15 + +/* Known Good seed sets */ +static const u32 main_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = { + {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874}, + {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 385, 761, 790, 974}, + {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874}, + {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 386, 761, 790, 974}, + {266, 265, 276, 585, 397, 208, 345, 355, 365, 376, 385, 396, 771, 700, 984}, + {266, 265, 276, 586, 397, 208, 346, 355, 365, 376, 285, 396, 771, 700, 984}, + {366, 365, 376, 686, 497, 308, 447, 455, 466, 476, 485, 496, 871, 800, 84}, + {466, 465, 476, 786, 597, 408, 547, 555, 566, 576, 585, 597, 971, 900, 184}}; + +static const u32 gear_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = { + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 397}, + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}}; + +static void nv_gear_backoff_reseed(struct net_device *dev) +{ + u8 __iomem *base = get_hwbase(dev); + u32 miniseed1, miniseed2, miniseed2_reversed, miniseed3, miniseed3_reversed; + u32 temp, seedset, combinedSeed; + int i; + + /* Setup seed for free running LFSR */ + /* We are going to read the time stamp counter 3 times + and swizzle bits around to increase randomness */ + get_random_bytes(&miniseed1, sizeof(miniseed1)); + miniseed1 &= 0x0fff; + if (miniseed1 == 0) + miniseed1 = 0xabc; + + get_random_bytes(&miniseed2, sizeof(miniseed2)); + miniseed2 &= 0x0fff; + if (miniseed2 == 0) + miniseed2 = 0xabc; + miniseed2_reversed = + ((miniseed2 & 0xF00) >> 8) | + (miniseed2 & 0x0F0) | + ((miniseed2 & 0x00F) << 8); + + get_random_bytes(&miniseed3, sizeof(miniseed3)); + miniseed3 &= 0x0fff; + if (miniseed3 == 0) + miniseed3 = 0xabc; + miniseed3_reversed = + ((miniseed3 & 0xF00) >> 8) | + (miniseed3 & 0x0F0) | + ((miniseed3 & 0x00F) << 8); + + combinedSeed = ((miniseed1 ^ miniseed2_reversed) << 12) | + (miniseed2 ^ miniseed3_reversed); + + /* Seeds can not be zero */ + if ((combinedSeed & NVREG_BKOFFCTRL_SEED_MASK) == 0) + combinedSeed |= 0x08; + if ((combinedSeed & (NVREG_BKOFFCTRL_SEED_MASK << NVREG_BKOFFCTRL_GEAR)) == 0) + combinedSeed |= 0x8000; + + /* No need to disable tx here */ + temp = NVREG_BKOFFCTRL_DEFAULT | (0 << NVREG_BKOFFCTRL_SELECT); + temp |= combinedSeed & NVREG_BKOFFCTRL_SEED_MASK; + temp |= combinedSeed >> NVREG_BKOFFCTRL_GEAR; + writel(temp,base + NvRegBackOffControl); + + /* Setup seeds for all gear LFSRs. */ + get_random_bytes(&seedset, sizeof(seedset)); + seedset = seedset % BACKOFF_SEEDSET_ROWS; + for (i = 1; i <= BACKOFF_SEEDSET_LFSRS; i++) + { + temp = NVREG_BKOFFCTRL_DEFAULT | (i << NVREG_BKOFFCTRL_SELECT); + temp |= main_seedset[seedset][i-1] & 0x3ff; + temp |= ((gear_seedset[seedset][i-1] & 0x3ff) << NVREG_BKOFFCTRL_GEAR); + writel(temp, base + NvRegBackOffControl); + } +} + /* * nv_start_xmit: dev->hard_start_xmit function * Called with netif_tx_lock held. @@ -2088,6 +2306,8 @@ static void nv_tx_done(struct net_device *dev) dev->stats.tx_fifo_errors++; if (flags & NV_TX_CARRIERLOST) dev->stats.tx_carrier_errors++; + if ((flags & NV_TX_RETRYERROR) && !(flags & NV_TX_RETRYCOUNT_MASK)) + nv_legacybackoff_reseed(dev); dev->stats.tx_errors++; } else { dev->stats.tx_packets++; @@ -2103,6 +2323,8 @@ static void nv_tx_done(struct net_device *dev) dev->stats.tx_fifo_errors++; if (flags & NV_TX2_CARRIERLOST) dev->stats.tx_carrier_errors++; + if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK)) + nv_legacybackoff_reseed(dev); dev->stats.tx_errors++; } else { dev->stats.tx_packets++; @@ -2144,6 +2366,15 @@ static void nv_tx_done_optimized(struct net_device *dev, int limit) if (flags & NV_TX2_LASTPACKET) { if (!(flags & NV_TX2_ERROR)) dev->stats.tx_packets++; + else { + if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK)) { + if (np->driver_data & DEV_HAS_GEAR_MODE) + nv_gear_backoff_reseed(dev); + else + nv_legacybackoff_reseed(dev); + } + } + dev_kfree_skb_any(np->get_tx_ctx->skb); np->get_tx_ctx->skb = NULL; @@ -2905,15 +3136,14 @@ set_speed: } if (np->gigabit == PHY_GIGABIT) { - phyreg = readl(base + NvRegRandomSeed); + phyreg = readl(base + NvRegSlotTime); phyreg &= ~(0x3FF00); - if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10) - phyreg |= NVREG_RNDSEED_FORCE3; - else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100) - phyreg |= NVREG_RNDSEED_FORCE2; + if (((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10) || + ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)) + phyreg |= NVREG_SLOTTIME_10_100_FULL; else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000) - phyreg |= NVREG_RNDSEED_FORCE; - writel(phyreg, base + NvRegRandomSeed); + phyreg |= NVREG_SLOTTIME_1000_FULL; + writel(phyreg, base + NvRegSlotTime); } phyreg = readl(base + NvRegPhyInterface); @@ -4843,6 +5073,7 @@ static int nv_open(struct net_device *dev) u8 __iomem *base = get_hwbase(dev); int ret = 1; int oom, i; + u32 low; dprintk(KERN_DEBUG "nv_open: begin\n"); @@ -4902,8 +5133,20 @@ static int nv_open(struct net_device *dev) writel(np->rx_buf_sz, base + NvRegOffloadConfig); writel(readl(base + NvRegReceiverStatus), base + NvRegReceiverStatus); - get_random_bytes(&i, sizeof(i)); - writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed); + + get_random_bytes(&low, sizeof(low)); + low &= NVREG_SLOTTIME_MASK; + if (np->desc_ver == DESC_VER_1) { + writel(low|NVREG_SLOTTIME_DEFAULT, base + NvRegSlotTime); + } else { + if (!(np->driver_data & DEV_HAS_GEAR_MODE)) { + /* setup legacy backoff */ + writel(NVREG_SLOTTIME_LEGBF_ENABLED|NVREG_SLOTTIME_10_100_FULL|low, base + NvRegSlotTime); + } else { + writel(NVREG_SLOTTIME_10_100_FULL, base + NvRegSlotTime); + nv_gear_backoff_reseed(dev); + } + } writel(NVREG_TX_DEFERRAL_DEFAULT, base + NvRegTxDeferral); writel(NVREG_RX_DEFERRAL_DEFAULT, base + NvRegRxDeferral); if (poll_interval == -1) { @@ -5110,6 +5353,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i /* copy of driver data */ np->driver_data = id->driver_data; + /* copy of device id */ + np->device_id = id->device; /* handle different descriptor versions */ if (id->driver_data & DEV_HAS_HIGH_DMA) { @@ -5399,6 +5644,14 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i pci_name(pci_dev), id1, id2, phyaddr); np->phyaddr = phyaddr; np->phy_oui = id1 | id2; + + /* Realtek hardcoded phy id1 to all zero's on certain phys */ + if (np->phy_oui == PHY_OUI_REALTEK2) + np->phy_oui = PHY_OUI_REALTEK; + /* Setup phy revision for Realtek */ + if (np->phy_oui == PHY_OUI_REALTEK && np->phy_model == PHY_MODEL_REALTEK_8211) + np->phy_rev = mii_rw(dev, phyaddr, MII_RESV1, MII_READ) & PHY_REV_MASK; + break; } if (i == 33) { @@ -5477,6 +5730,28 @@ out: return err; } +static void nv_restore_phy(struct net_device *dev) +{ + struct fe_priv *np = netdev_priv(dev); + u16 phy_reserved, mii_control; + + if (np->phy_oui == PHY_OUI_REALTEK && + np->phy_model == PHY_MODEL_REALTEK_8201 && + phy_cross == NV_CROSSOVER_DETECTION_DISABLED) { + mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3); + phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ); + phy_reserved &= ~PHY_REALTEK_INIT_MSK1; + phy_reserved |= PHY_REALTEK_INIT8; + mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved); + mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1); + + /* restart auto negotiation */ + mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ); + mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE); + mii_rw(dev, np->phyaddr, MII_BMCR, mii_control); + } +} + static void __devexit nv_remove(struct pci_dev *pci_dev) { struct net_device *dev = pci_get_drvdata(pci_dev); @@ -5493,6 +5768,9 @@ static void __devexit nv_remove(struct pci_dev *pci_dev) writel(readl(base + NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll); + /* restore any phy related changes */ + nv_restore_phy(dev); + /* free all structures */ free_rings(dev); iounmap(get_hwbase(dev)); @@ -5632,83 +5910,83 @@ static struct pci_device_id pci_tbl[] = { }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_20), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_21), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_22), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_23), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_24), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_25), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_26), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_27), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE, }, { /* MCP73 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_28), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE, }, { /* MCP73 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_29), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE, }, { /* MCP73 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_30), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE, }, { /* MCP73 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_31), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE, }, { /* MCP77 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_32), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP77 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_33), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP77 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_34), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP77 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_35), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP79 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP79 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP79 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_38), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, { /* MCP79 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_39), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE, }, {0,}, }; @@ -5744,6 +6022,8 @@ module_param(msix, int, 0); MODULE_PARM_DESC(msix, "MSIX interrupts are enabled by setting to 1 and disabled by setting to 0."); module_param(dma_64bit, int, 0); MODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0."); +module_param(phy_cross, int, 0); +MODULE_PARM_DESC(phy_cross, "Phy crossover detection for Realtek 8201 phy is enabled by setting to 1 and disabled by setting to 0."); MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>"); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index c8c3df737d73..99a4b990939f 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -98,7 +98,6 @@ #include "gianfar_mii.h" #define TX_TIMEOUT (1*HZ) -#define SKB_ALLOC_TIMEOUT 1000000 #undef BRIEF_GFAR_ERRORS #undef VERBOSE_GFAR_ERRORS @@ -115,7 +114,9 @@ static int gfar_enet_open(struct net_device *dev); static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); -struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp); +struct sk_buff *gfar_new_skb(struct net_device *dev); +static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp, + struct sk_buff *skb); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); static irqreturn_t gfar_error(int irq, void *dev_id); @@ -783,14 +784,21 @@ int startup_gfar(struct net_device *dev) rxbdp = priv->rx_bd_base; for (i = 0; i < priv->rx_ring_size; i++) { - struct sk_buff *skb = NULL; + struct sk_buff *skb; - rxbdp->status = 0; + skb = gfar_new_skb(dev); - skb = gfar_new_skb(dev, rxbdp); + if (!skb) { + printk(KERN_ERR "%s: Can't allocate RX buffers\n", + dev->name); + + goto err_rxalloc_fail; + } priv->rx_skbuff[i] = skb; + gfar_new_rxbdp(dev, rxbdp, skb); + rxbdp++; } @@ -916,6 +924,7 @@ rx_irq_fail: tx_irq_fail: free_irq(priv->interruptError, dev); err_irq_fail: +err_rxalloc_fail: rx_skb_fail: free_skb_resources(priv); tx_skb_fail: @@ -1328,18 +1337,37 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id) return IRQ_HANDLED; } -struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) +static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp, + struct sk_buff *skb) +{ + struct gfar_private *priv = netdev_priv(dev); + u32 * status_len = (u32 *)bdp; + u16 flags; + + bdp->bufPtr = dma_map_single(&dev->dev, skb->data, + priv->rx_buffer_size, DMA_FROM_DEVICE); + + flags = RXBD_EMPTY | RXBD_INTERRUPT; + + if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1) + flags |= RXBD_WRAP; + + eieio(); + + *status_len = (u32)flags << 16; +} + + +struct sk_buff * gfar_new_skb(struct net_device *dev) { unsigned int alignamount; struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb = NULL; - unsigned int timeout = SKB_ALLOC_TIMEOUT; /* We have to allocate the skb, so keep trying till we succeed */ - while ((!skb) && timeout--) - skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT); + skb = netdev_alloc_skb(dev, priv->rx_buffer_size + RXBUF_ALIGNMENT); - if (NULL == skb) + if (!skb) return NULL; alignamount = RXBUF_ALIGNMENT - @@ -1350,15 +1378,6 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) */ skb_reserve(skb, alignamount); - bdp->bufPtr = dma_map_single(&dev->dev, skb->data, - priv->rx_buffer_size, DMA_FROM_DEVICE); - - bdp->length = 0; - - /* Mark the buffer empty */ - eieio(); - bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT); - return skb; } @@ -1544,10 +1563,31 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) bdp = priv->cur_rx; while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { + struct sk_buff *newskb; rmb(); + + /* Add another skb for the future */ + newskb = gfar_new_skb(dev); + skb = priv->rx_skbuff[priv->skb_currx]; - if ((bdp->status & RXBD_LAST) && !(bdp->status & RXBD_ERR)) { + /* We drop the frame if we failed to allocate a new buffer */ + if (unlikely(!newskb || !(bdp->status & RXBD_LAST) || + bdp->status & RXBD_ERR)) { + count_errors(bdp->status, dev); + + if (unlikely(!newskb)) + newskb = skb; + + if (skb) { + dma_unmap_single(&priv->dev->dev, + bdp->bufPtr, + priv->rx_buffer_size, + DMA_FROM_DEVICE); + + dev_kfree_skb_any(skb); + } + } else { /* Increment the number of packets */ dev->stats.rx_packets++; howmany++; @@ -1558,23 +1598,14 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) gfar_process_frame(dev, skb, pkt_len); dev->stats.rx_bytes += pkt_len; - } else { - count_errors(bdp->status, dev); - - if (skb) - dev_kfree_skb_any(skb); - - priv->rx_skbuff[priv->skb_currx] = NULL; } dev->last_rx = jiffies; - /* Clear the status flags for this buffer */ - bdp->status &= ~RXBD_STATS; + priv->rx_skbuff[priv->skb_currx] = newskb; - /* Add another skb for the future */ - skb = gfar_new_skb(dev, bdp); - priv->rx_skbuff[priv->skb_currx] = skb; + /* Setup the new bdp */ + gfar_new_rxbdp(dev, bdp, newskb); /* Update to the next pointer */ if (bdp->status & RXBD_WRAP) @@ -1584,9 +1615,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) /* update to point at the next skb */ priv->skb_currx = - (priv->skb_currx + - 1) & RX_RING_MOD_MASK(priv->rx_ring_size); - + (priv->skb_currx + 1) & + RX_RING_MOD_MASK(priv->rx_ring_size); } /* Update the current rxbd pointer to be the next one */ @@ -2001,12 +2031,16 @@ static irqreturn_t gfar_error(int irq, void *dev_id) return IRQ_HANDLED; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:fsl-gianfar"); + /* Structure for a device driver */ static struct platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, .driver = { .name = "fsl-gianfar", + .owner = THIS_MODULE, }, }; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 378a23963495..5d2108c5ac7c 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -43,6 +43,8 @@ #include <asm/io.h> #include <asm/dma.h> #include <asm/uaccess.h> +#include <asm/dcr.h> +#include <asm/dcr-regs.h> #include "core.h" @@ -127,10 +129,35 @@ static struct device_node *emac_boot_list[EMAC_BOOT_LIST_SIZE]; static inline void emac_report_timeout_error(struct emac_instance *dev, const char *error) { - if (net_ratelimit()) + if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX | + EMAC_FTR_440EP_PHY_CLK_FIX)) + DBG(dev, "%s" NL, error); + else if (net_ratelimit()) printk(KERN_ERR "%s: %s\n", dev->ndev->name, error); } +/* EMAC PHY clock workaround: + * 440EP/440GR has more sane SDR0_MFR register implementation than 440GX, + * which allows controlling each EMAC clock + */ +static inline void emac_rx_clk_tx(struct emac_instance *dev) +{ +#ifdef CONFIG_PPC_DCR_NATIVE + if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) + dcri_clrset(SDR0, SDR0_MFR, + 0, SDR0_MFR_ECS >> dev->cell_index); +#endif +} + +static inline void emac_rx_clk_default(struct emac_instance *dev) +{ +#ifdef CONFIG_PPC_DCR_NATIVE + if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX)) + dcri_clrset(SDR0, SDR0_MFR, + SDR0_MFR_ECS >> dev->cell_index, 0); +#endif +} + /* PHY polling intervals */ #define PHY_POLL_LINK_ON HZ #define PHY_POLL_LINK_OFF (HZ / 5) @@ -524,7 +551,10 @@ static int emac_configure(struct emac_instance *dev) rx_size = dev->rx_fifo_size_gige; if (dev->ndev->mtu > ETH_DATA_LEN) { - mr1 |= EMAC_MR1_JPSM; + if (emac_has_feature(dev, EMAC_FTR_EMAC4)) + mr1 |= EMAC4_MR1_JPSM; + else + mr1 |= EMAC_MR1_JPSM; dev->stop_timeout = STOP_TIMEOUT_1000_JUMBO; } else dev->stop_timeout = STOP_TIMEOUT_1000; @@ -708,7 +738,7 @@ static int __emac_mdio_read(struct emac_instance *dev, u8 id, u8 reg) rgmii_get_mdio(dev->rgmii_dev, dev->rgmii_port); /* Wait for management interface to become idle */ - n = 10; + n = 20; while (!emac_phy_done(dev, in_be32(&p->stacr))) { udelay(1); if (!--n) { @@ -733,7 +763,7 @@ static int __emac_mdio_read(struct emac_instance *dev, u8 id, u8 reg) out_be32(&p->stacr, r); /* Wait for read to complete */ - n = 100; + n = 200; while (!emac_phy_done(dev, (r = in_be32(&p->stacr)))) { udelay(1); if (!--n) { @@ -780,7 +810,7 @@ static void __emac_mdio_write(struct emac_instance *dev, u8 id, u8 reg, rgmii_get_mdio(dev->rgmii_dev, dev->rgmii_port); /* Wait for management interface to be idle */ - n = 10; + n = 20; while (!emac_phy_done(dev, in_be32(&p->stacr))) { udelay(1); if (!--n) { @@ -806,7 +836,7 @@ static void __emac_mdio_write(struct emac_instance *dev, u8 id, u8 reg, out_be32(&p->stacr, r); /* Wait for write to complete */ - n = 100; + n = 200; while (!emac_phy_done(dev, in_be32(&p->stacr))) { udelay(1); if (!--n) { @@ -1094,9 +1124,11 @@ static int emac_open(struct net_device *ndev) int link_poll_interval; if (dev->phy.def->ops->poll_link(&dev->phy)) { dev->phy.def->ops->read_link(&dev->phy); + emac_rx_clk_default(dev); netif_carrier_on(dev->ndev); link_poll_interval = PHY_POLL_LINK_ON; } else { + emac_rx_clk_tx(dev); netif_carrier_off(dev->ndev); link_poll_interval = PHY_POLL_LINK_OFF; } @@ -1174,6 +1206,7 @@ static void emac_link_timer(struct work_struct *work) if (dev->phy.def->ops->poll_link(&dev->phy)) { if (!netif_carrier_ok(dev->ndev)) { + emac_rx_clk_default(dev); /* Get new link parameters */ dev->phy.def->ops->read_link(&dev->phy); @@ -1186,6 +1219,7 @@ static void emac_link_timer(struct work_struct *work) link_poll_interval = PHY_POLL_LINK_ON; } else { if (netif_carrier_ok(dev->ndev)) { + emac_rx_clk_tx(dev); netif_carrier_off(dev->ndev); netif_tx_disable(dev->ndev); emac_reinitialize(dev); @@ -2237,7 +2271,7 @@ static int __devinit emac_of_bus_notify(struct notifier_block *nb, return 0; } -static struct notifier_block emac_of_bus_notifier = { +static struct notifier_block emac_of_bus_notifier __devinitdata = { .notifier_call = emac_of_bus_notify }; @@ -2330,6 +2364,19 @@ static int __devinit emac_init_phy(struct emac_instance *dev) dev->phy.mdio_read = emac_mdio_read; dev->phy.mdio_write = emac_mdio_write; + /* Enable internal clock source */ +#ifdef CONFIG_PPC_DCR_NATIVE + if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX)) + dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS); +#endif + /* PHY clock workaround */ + emac_rx_clk_tx(dev); + + /* Enable internal clock source on 440GX*/ +#ifdef CONFIG_PPC_DCR_NATIVE + if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX)) + dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS); +#endif /* Configure EMAC with defaults so we can at least use MDIO * This is needed mostly for 440GX */ @@ -2362,6 +2409,12 @@ static int __devinit emac_init_phy(struct emac_instance *dev) if (!emac_mii_phy_probe(&dev->phy, i)) break; } + + /* Enable external clock source */ +#ifdef CONFIG_PPC_DCR_NATIVE + if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX)) + dcri_clrset(SDR0, SDR0_MFR, SDR0_MFR_ECS, 0); +#endif mutex_unlock(&emac_phy_map_lock); if (i == 0x20) { printk(KERN_WARNING "%s: can't find PHY!\n", np->full_name); @@ -2487,8 +2540,15 @@ static int __devinit emac_init_config(struct emac_instance *dev) } /* Check EMAC version */ - if (of_device_is_compatible(np, "ibm,emac4")) + if (of_device_is_compatible(np, "ibm,emac4")) { dev->features |= EMAC_FTR_EMAC4; + if (of_device_is_compatible(np, "ibm,emac-440gx")) + dev->features |= EMAC_FTR_440GX_PHY_CLK_FIX; + } else { + if (of_device_is_compatible(np, "ibm,emac-440ep") || + of_device_is_compatible(np, "ibm,emac-440gr")) + dev->features |= EMAC_FTR_440EP_PHY_CLK_FIX; + } /* Fixup some feature bits based on the device tree */ if (of_get_property(np, "has-inverted-stacr-oc", NULL)) @@ -2559,8 +2619,11 @@ static int __devinit emac_probe(struct of_device *ofdev, struct device_node **blist = NULL; int err, i; - /* Skip unused/unwired EMACS */ - if (of_get_property(np, "unused", NULL)) + /* Skip unused/unwired EMACS. We leave the check for an unused + * property here for now, but new flat device trees should set a + * status property to "disabled" instead. + */ + if (of_get_property(np, "unused", NULL) || !of_device_is_available(np)) return -ENODEV; /* Find ourselves in the bootlist if we are there */ diff --git a/drivers/net/ibm_newemac/core.h b/drivers/net/ibm_newemac/core.h index 4e74d8287c65..1683db9870a4 100644 --- a/drivers/net/ibm_newemac/core.h +++ b/drivers/net/ibm_newemac/core.h @@ -301,6 +301,14 @@ struct emac_instance { * Set if we have new type STACR with STAOPC */ #define EMAC_FTR_HAS_NEW_STACR 0x00000040 +/* + * Set if we need phy clock workaround for 440gx + */ +#define EMAC_FTR_440GX_PHY_CLK_FIX 0x00000080 +/* + * Set if we need phy clock workaround for 440ep or 440gr + */ +#define EMAC_FTR_440EP_PHY_CLK_FIX 0x00000100 /* Right now, we don't quite handle the always/possible masks on the @@ -312,8 +320,8 @@ enum { EMAC_FTRS_POSSIBLE = #ifdef CONFIG_IBM_NEW_EMAC_EMAC4 - EMAC_FTR_EMAC4 | EMAC_FTR_HAS_NEW_STACR | - EMAC_FTR_STACR_OC_INVERT | + EMAC_FTR_EMAC4 | EMAC_FTR_HAS_NEW_STACR | + EMAC_FTR_STACR_OC_INVERT | EMAC_FTR_440GX_PHY_CLK_FIX | #endif #ifdef CONFIG_IBM_NEW_EMAC_TAH EMAC_FTR_HAS_TAH | @@ -324,7 +332,7 @@ enum { #ifdef CONFIG_IBM_NEW_EMAC_RGMII EMAC_FTR_HAS_RGMII | #endif - 0, + EMAC_FTR_440EP_PHY_CLK_FIX, }; static inline int emac_has_feature(struct emac_instance *dev, diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c index 6869f08c9dcb..10c267b2b961 100644 --- a/drivers/net/ibm_newemac/mal.c +++ b/drivers/net/ibm_newemac/mal.c @@ -61,8 +61,8 @@ int __devinit mal_register_commac(struct mal_instance *mal, return 0; } -void __devexit mal_unregister_commac(struct mal_instance *mal, - struct mal_commac *commac) +void mal_unregister_commac(struct mal_instance *mal, + struct mal_commac *commac) { unsigned long flags; @@ -136,6 +136,14 @@ void mal_enable_rx_channel(struct mal_instance *mal, int channel) { unsigned long flags; + /* + * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple + * of 8, but enabling in MAL_RXCASR needs the divided by 8 value + * for the bitmask + */ + if (!(channel % 8)) + channel >>= 3; + spin_lock_irqsave(&mal->lock, flags); MAL_DBG(mal, "enable_rx(%d)" NL, channel); @@ -148,6 +156,14 @@ void mal_enable_rx_channel(struct mal_instance *mal, int channel) void mal_disable_rx_channel(struct mal_instance *mal, int channel) { + /* + * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple + * of 8, but enabling in MAL_RXCASR needs the divided by 8 value + * for the bitmask + */ + if (!(channel % 8)) + channel >>= 3; + set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel)); MAL_DBG(mal, "disable_rx(%d)" NL, channel); diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c index 5757788227be..e32da3de2695 100644 --- a/drivers/net/ibm_newemac/rgmii.c +++ b/drivers/net/ibm_newemac/rgmii.c @@ -179,7 +179,7 @@ void rgmii_put_mdio(struct of_device *ofdev, int input) mutex_unlock(&dev->lock); } -void __devexit rgmii_detach(struct of_device *ofdev, int input) +void rgmii_detach(struct of_device *ofdev, int input) { struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); struct rgmii_regs __iomem *p = dev->base; diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ibm_newemac/tah.c index b023d10d7e1c..30173a9fb557 100644 --- a/drivers/net/ibm_newemac/tah.c +++ b/drivers/net/ibm_newemac/tah.c @@ -35,7 +35,7 @@ int __devinit tah_attach(struct of_device *ofdev, int channel) return 0; } -void __devexit tah_detach(struct of_device *ofdev, int channel) +void tah_detach(struct of_device *ofdev, int channel) { struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/ibm_newemac/zmii.c b/drivers/net/ibm_newemac/zmii.c index 2ea472aeab06..17b154124943 100644 --- a/drivers/net/ibm_newemac/zmii.c +++ b/drivers/net/ibm_newemac/zmii.c @@ -189,7 +189,7 @@ void zmii_set_speed(struct of_device *ofdev, int input, int speed) mutex_unlock(&dev->lock); } -void __devexit zmii_detach(struct of_device *ofdev, int input) +void zmii_detach(struct of_device *ofdev, int input) { struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index aaee02e9e3f0..ae398f04c7b4 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -871,6 +871,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, goto err_pci_reg; pci_set_master(pdev); + pci_save_state(pdev); err = -ENOMEM; netdev = alloc_etherdev(sizeof(struct igb_adapter)); @@ -4079,6 +4080,7 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); + pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 9f584521304a..083b0dd70fef 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -60,6 +60,7 @@ static struct platform_driver ali_ircc_driver = { .resume = ali_ircc_resume, .driver = { .name = ALI_IRCC_DRIVER_NAME, + .owner = THIS_MODULE, }, }; @@ -2256,6 +2257,7 @@ static void FIR2SIR(int iobase) MODULE_AUTHOR("Benjamin Kong <benjamin_kong@ali.com.tw>"); MODULE_DESCRIPTION("ALi FIR Controller Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" ALI_IRCC_DRIVER_NAME); module_param_array(io, int, NULL, 0); diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 8db71ab20456..d5c2d27f3ea4 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -908,6 +908,7 @@ static int pxa_irda_remove(struct platform_device *_dev) static struct platform_driver pxa_ir_driver = { .driver = { .name = "pxa2xx-ir", + .owner = THIS_MODULE, }, .probe = pxa_irda_probe, .remove = pxa_irda_remove, @@ -929,3 +930,4 @@ module_init(pxa_irda_init); module_exit(pxa_irda_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-ir"); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 056639f72bec..1bc8518f9197 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -1008,6 +1008,7 @@ static struct platform_driver sa1100ir_driver = { .resume = sa1100_irda_resume, .driver = { .name = "sa11x0-ir", + .owner = THIS_MODULE, }, }; @@ -1041,3 +1042,4 @@ MODULE_LICENSE("GPL"); MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)"); MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode"); MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); +MODULE_ALIAS("platform:sa11x0-ir"); diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index cb371a8c24a7..7b859220c255 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -3431,6 +3431,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, } pci_set_master(pdev); + pci_save_state(pdev); #ifdef CONFIG_NETDEVICES_MULTIQUEUE netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), MAX_TX_QUEUES); @@ -3721,6 +3722,7 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); + pci_restore_state(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3cold, 0); diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c index 5c154fe13859..07944820f745 100644 --- a/drivers/net/jazzsonic.c +++ b/drivers/net/jazzsonic.c @@ -249,6 +249,7 @@ out: MODULE_DESCRIPTION("Jazz SONIC ethernet driver"); module_param(sonic_debug, int, 0); MODULE_PARM_DESC(sonic_debug, "jazzsonic debug level (1-4)"); +MODULE_ALIAS("platform:jazzsonic"); #include "sonic.c" @@ -271,6 +272,7 @@ static struct platform_driver jazz_sonic_driver = { .remove = __devexit_p(jazz_sonic_device_remove), .driver = { .name = jazz_sonic_string, + .owner = THIS_MODULE, }, }; diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 1d24a73a0e1a..e18576316bda 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -883,7 +883,7 @@ static int korina_init(struct net_device *dev) static int korina_restart(struct net_device *dev) { struct korina_private *lp = netdev_priv(dev); - int ret = 0; + int ret; /* * Disable interrupts @@ -987,7 +987,7 @@ static void korina_poll_controller(struct net_device *dev) static int korina_open(struct net_device *dev) { struct korina_private *lp = netdev_priv(dev); - int ret = 0; + int ret; /* Initialize */ ret = korina_init(dev); @@ -1031,6 +1031,8 @@ static int korina_open(struct net_device *dev) dev->name, lp->und_irq); goto err_free_ovr_irq; } +out: + return ret; err_free_ovr_irq: free_irq(lp->ovr_irq, dev); @@ -1041,8 +1043,6 @@ err_free_rx_irq: err_release: korina_free_ring(dev); goto out; -out: - return ret; } static int korina_close(struct net_device *dev) @@ -1082,7 +1082,7 @@ static int korina_probe(struct platform_device *pdev) struct korina_private *lp; struct net_device *dev; struct resource *r; - int retval, err; + int rc; dev = alloc_etherdev(sizeof(struct korina_private)); if (!dev) { @@ -1106,7 +1106,7 @@ static int korina_probe(struct platform_device *pdev) lp->eth_regs = ioremap_nocache(r->start, r->end - r->start); if (!lp->eth_regs) { printk(KERN_ERR DRV_NAME "cannot remap registers\n"); - retval = -ENXIO; + rc = -ENXIO; goto probe_err_out; } @@ -1114,7 +1114,7 @@ static int korina_probe(struct platform_device *pdev) lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start); if (!lp->rx_dma_regs) { printk(KERN_ERR DRV_NAME "cannot remap Rx DMA registers\n"); - retval = -ENXIO; + rc = -ENXIO; goto probe_err_dma_rx; } @@ -1122,14 +1122,14 @@ static int korina_probe(struct platform_device *pdev) lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start); if (!lp->tx_dma_regs) { printk(KERN_ERR DRV_NAME "cannot remap Tx DMA registers\n"); - retval = -ENXIO; + rc = -ENXIO; goto probe_err_dma_tx; } lp->td_ring = kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL); if (!lp->td_ring) { printk(KERN_ERR DRV_NAME "cannot allocate descriptors\n"); - retval = -ENOMEM; + rc = -ENXIO; goto probe_err_td_ring; } @@ -1166,14 +1166,14 @@ static int korina_probe(struct platform_device *pdev) lp->mii_if.phy_id_mask = 0x1f; lp->mii_if.reg_num_mask = 0x1f; - err = register_netdev(dev); - if (err) { + rc = register_netdev(dev); + if (rc < 0) { printk(KERN_ERR DRV_NAME - ": cannot register net device %d\n", err); - retval = -EINVAL; + ": cannot register net device %d\n", rc); goto probe_err_register; } - return 0; +out: + return rc; probe_err_register: kfree(lp->td_ring); @@ -1185,7 +1185,7 @@ probe_err_dma_rx: iounmap(lp->eth_regs); probe_err_out: free_netdev(dev); - return retval; + goto out; } static int korina_remove(struct platform_device *pdev) @@ -1193,12 +1193,9 @@ static int korina_remove(struct platform_device *pdev) struct korina_device *bif = platform_get_drvdata(pdev); struct korina_private *lp = netdev_priv(bif->dev); - if (lp->eth_regs) - iounmap(lp->eth_regs); - if (lp->rx_dma_regs) - iounmap(lp->rx_dma_regs); - if (lp->tx_dma_regs) - iounmap(lp->tx_dma_regs); + iounmap(lp->eth_regs); + iounmap(lp->rx_dma_regs); + iounmap(lp->tx_dma_regs); platform_set_drvdata(pdev, NULL); unregister_netdev(bif->dev); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index d513bb8a4902..92dccd43bdca 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -1281,6 +1281,7 @@ static struct platform_driver macb_driver = { .remove = __exit_p(macb_remove), .driver = { .name = "macb", + .owner = THIS_MODULE, }, }; @@ -1300,3 +1301,4 @@ module_exit(macb_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Atmel MACB Ethernet driver"); MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_ALIAS("platform:macb"); diff --git a/drivers/net/meth.c b/drivers/net/meth.c index cdaa8fc21809..0b32648a2136 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -830,6 +830,7 @@ static struct platform_driver meth_driver = { .remove = __devexit_p(meth_remove), .driver = { .name = "meth", + .owner = THIS_MODULE, } }; @@ -855,3 +856,4 @@ module_exit(meth_exit_module); MODULE_AUTHOR("Ilya Volynets <ilya@theIlya.com>"); MODULE_DESCRIPTION("SGI O2 Builtin Fast Ethernet driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:meth"); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 601ffd69ebc8..381b36e5f64c 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2030,6 +2030,7 @@ static struct platform_driver mv643xx_eth_driver = { .shutdown = mv643xx_eth_shutdown, .driver = { .name = MV643XX_ETH_NAME, + .owner = THIS_MODULE, }, }; @@ -2038,6 +2039,7 @@ static struct platform_driver mv643xx_eth_shared_driver = { .remove = mv643xx_eth_shared_remove, .driver = { .name = MV643XX_ETH_SHARED_NAME, + .owner = THIS_MODULE, }, }; @@ -2085,7 +2087,8 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR( "Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani" " and Dale Farnsworth"); MODULE_DESCRIPTION("Ethernet driver for Marvell MV643XX"); -MODULE_ALIAS("platform:mv643xx_eth"); +MODULE_ALIAS("platform:" MV643XX_ETH_NAME); +MODULE_ALIAS("platform:" MV643XX_ETH_SHARED_NAME); /* * The second part is the low level driver of the gigE ethernet ports. diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c index 78d34af13a1c..dc442e370850 100644 --- a/drivers/net/netx-eth.c +++ b/drivers/net/netx-eth.c @@ -502,4 +502,4 @@ module_exit(netx_eth_cleanup); MODULE_AUTHOR("Sascha Hauer, Pengutronix"); MODULE_LICENSE("GPL"); - +MODULE_ALIAS("platform:" CARDNAME); diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index 05748ca6f216..af7356468251 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -1132,8 +1132,8 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) u32 fw_minor = 0; u32 fw_build = 0; char brd_name[NETXEN_MAX_SHORT_NAME]; - struct netxen_new_user_info user_info; - int i, addr = NETXEN_USER_START; + char serial_num[32]; + int i, addr; __le32 *ptr32; struct netxen_board_info *board_info = &(adapter->ahw.boardcfg); @@ -1150,10 +1150,10 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) valid = 0; } if (valid) { - ptr32 = (u32 *) & user_info; - for (i = 0; - i < sizeof(struct netxen_new_user_info) / sizeof(u32); - i++) { + ptr32 = (u32 *)&serial_num; + addr = NETXEN_USER_START + + offsetof(struct netxen_new_user_info, serial_num); + for (i = 0; i < 8; i++) { if (netxen_rom_fast_read(adapter, addr, ptr32) == -1) { printk("%s: ERROR reading %s board userarea.\n", netxen_nic_driver_name, @@ -1163,10 +1163,11 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) ptr32++; addr += sizeof(u32); } + get_brd_name_by_type(board_info->board_type, brd_name); printk("NetXen %s Board S/N %s Chip id 0x%x\n", - brd_name, user_info.serial_num, board_info->chip_id); + brd_name, serial_num, board_info->chip_id); printk("NetXen %s Board #%d, Chip id 0x%x\n", board_info->board_type == 0x0b ? "XGB" : "GBE", diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 7565c2d7f30e..4009c4ce96b4 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -33,8 +33,8 @@ #define DRV_MODULE_NAME "niu" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "0.7" -#define DRV_MODULE_RELDATE "February 18, 2008" +#define DRV_MODULE_VERSION "0.8" +#define DRV_MODULE_RELDATE "April 24, 2008" static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -673,11 +673,16 @@ static int serdes_init_10g(struct niu *np) } if ((sig & mask) != val) { + if (np->flags & NIU_FLAGS_HOTPLUG_PHY) { + np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; + return 0; + } dev_err(np->device, PFX "Port %u signal bits [%08x] are not " "[%08x]\n", np->port, (int) (sig & mask), (int) val); return -ENODEV; } - + if (np->flags & NIU_FLAGS_HOTPLUG_PHY) + np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT; return 0; } @@ -998,6 +1003,28 @@ static int bcm8704_user_dev3_readback(struct niu *np, int reg) return 0; } +static int bcm8706_init_user_dev3(struct niu *np) +{ + int err; + + + err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, + BCM8704_USER_OPT_DIGITAL_CTRL); + if (err < 0) + return err; + err &= ~USER_ODIG_CTRL_GPIOS; + err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT); + err |= USER_ODIG_CTRL_RESV2; + err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR, + BCM8704_USER_OPT_DIGITAL_CTRL, err); + if (err) + return err; + + mdelay(1000); + + return 0; +} + static int bcm8704_init_user_dev3(struct niu *np) { int err; @@ -1127,33 +1154,11 @@ static int xcvr_init_10g_mrvl88x2011(struct niu *np) MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX); } -static int xcvr_init_10g_bcm8704(struct niu *np) + +static int xcvr_diag_bcm870x(struct niu *np) { - struct niu_link_config *lp = &np->link_config; u16 analog_stat0, tx_alarm_status; - int err; - - err = bcm8704_reset(np); - if (err) - return err; - - err = bcm8704_init_user_dev3(np); - if (err) - return err; - - err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, - MII_BMCR); - if (err < 0) - return err; - err &= ~BMCR_LOOPBACK; - - if (lp->loopback_mode == LOOPBACK_MAC) - err |= BMCR_LOOPBACK; - - err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, - MII_BMCR, err); - if (err) - return err; + int err = 0; #if 1 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, @@ -1211,6 +1216,89 @@ static int xcvr_init_10g_bcm8704(struct niu *np) return 0; } +static int xcvr_10g_set_lb_bcm870x(struct niu *np) +{ + struct niu_link_config *lp = &np->link_config; + int err; + + err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, + MII_BMCR); + if (err < 0) + return err; + + err &= ~BMCR_LOOPBACK; + + if (lp->loopback_mode == LOOPBACK_MAC) + err |= BMCR_LOOPBACK; + + err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, + MII_BMCR, err); + if (err) + return err; + + return 0; +} + +static int xcvr_init_10g_bcm8706(struct niu *np) +{ + int err = 0; + u64 val; + + if ((np->flags & NIU_FLAGS_HOTPLUG_PHY) && + (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) == 0) + return err; + + val = nr64_mac(XMAC_CONFIG); + val &= ~XMAC_CONFIG_LED_POLARITY; + val |= XMAC_CONFIG_FORCE_LED_ON; + nw64_mac(XMAC_CONFIG, val); + + val = nr64(MIF_CONFIG); + val |= MIF_CONFIG_INDIRECT_MODE; + nw64(MIF_CONFIG, val); + + err = bcm8704_reset(np); + if (err) + return err; + + err = xcvr_10g_set_lb_bcm870x(np); + if (err) + return err; + + err = bcm8706_init_user_dev3(np); + if (err) + return err; + + err = xcvr_diag_bcm870x(np); + if (err) + return err; + + return 0; +} + +static int xcvr_init_10g_bcm8704(struct niu *np) +{ + int err; + + err = bcm8704_reset(np); + if (err) + return err; + + err = bcm8704_init_user_dev3(np); + if (err) + return err; + + err = xcvr_10g_set_lb_bcm870x(np); + if (err) + return err; + + err = xcvr_diag_bcm870x(np); + if (err) + return err; + + return 0; +} + static int xcvr_init_10g(struct niu *np) { int phy_id, err; @@ -1548,6 +1636,59 @@ out: return err; } +static int link_status_10g_bcm8706(struct niu *np, int *link_up_p) +{ + int err, link_up; + link_up = 0; + + err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, + BCM8704_PMD_RCV_SIGDET); + if (err < 0) + goto out; + if (!(err & PMD_RCV_SIGDET_GLOBAL)) { + err = 0; + goto out; + } + + err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR, + BCM8704_PCS_10G_R_STATUS); + if (err < 0) + goto out; + + if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) { + err = 0; + goto out; + } + + err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR, + BCM8704_PHYXS_XGXS_LANE_STAT); + if (err < 0) + goto out; + if (err != (PHYXS_XGXS_LANE_STAT_ALINGED | + PHYXS_XGXS_LANE_STAT_MAGIC | + PHYXS_XGXS_LANE_STAT_PATTEST | + PHYXS_XGXS_LANE_STAT_LANE3 | + PHYXS_XGXS_LANE_STAT_LANE2 | + PHYXS_XGXS_LANE_STAT_LANE1 | + PHYXS_XGXS_LANE_STAT_LANE0)) { + err = 0; + np->link_config.active_speed = SPEED_INVALID; + np->link_config.active_duplex = DUPLEX_INVALID; + goto out; + } + + link_up = 1; + np->link_config.active_speed = SPEED_10000; + np->link_config.active_duplex = DUPLEX_FULL; + err = 0; + +out: + *link_up_p = link_up; + if (np->flags & NIU_FLAGS_HOTPLUG_PHY) + err = 0; + return err; +} + static int link_status_10g_bcom(struct niu *np, int *link_up_p) { int err, link_up; @@ -1627,6 +1768,82 @@ static int link_status_10g(struct niu *np, int *link_up_p) return err; } +static int niu_10g_phy_present(struct niu *np) +{ + u64 sig, mask, val; + + sig = nr64(ESR_INT_SIGNALS); + switch (np->port) { + case 0: + mask = ESR_INT_SIGNALS_P0_BITS; + val = (ESR_INT_SRDY0_P0 | + ESR_INT_DET0_P0 | + ESR_INT_XSRDY_P0 | + ESR_INT_XDP_P0_CH3 | + ESR_INT_XDP_P0_CH2 | + ESR_INT_XDP_P0_CH1 | + ESR_INT_XDP_P0_CH0); + break; + + case 1: + mask = ESR_INT_SIGNALS_P1_BITS; + val = (ESR_INT_SRDY0_P1 | + ESR_INT_DET0_P1 | + ESR_INT_XSRDY_P1 | + ESR_INT_XDP_P1_CH3 | + ESR_INT_XDP_P1_CH2 | + ESR_INT_XDP_P1_CH1 | + ESR_INT_XDP_P1_CH0); + break; + + default: + return 0; + } + + if ((sig & mask) != val) + return 0; + return 1; +} + +static int link_status_10g_hotplug(struct niu *np, int *link_up_p) +{ + unsigned long flags; + int err = 0; + int phy_present; + int phy_present_prev; + + spin_lock_irqsave(&np->lock, flags); + + if (np->link_config.loopback_mode == LOOPBACK_DISABLED) { + phy_present_prev = (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) ? + 1 : 0; + phy_present = niu_10g_phy_present(np); + if (phy_present != phy_present_prev) { + /* state change */ + if (phy_present) { + np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT; + if (np->phy_ops->xcvr_init) + err = np->phy_ops->xcvr_init(np); + if (err) { + /* debounce */ + np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; + } + } else { + np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT; + *link_up_p = 0; + niuwarn(LINK, "%s: Hotplug PHY Removed\n", + np->dev->name); + } + } + if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) + err = link_status_10g_bcm8706(np, link_up_p); + } + + spin_unlock_irqrestore(&np->lock, flags); + + return err; +} + static int link_status_1g(struct niu *np, int *link_up_p) { struct niu_link_config *lp = &np->link_config; @@ -1761,6 +1978,12 @@ static const struct niu_phy_ops phy_ops_10g_fiber = { .link_status = link_status_10g, }; +static const struct niu_phy_ops phy_ops_10g_fiber_hotplug = { + .serdes_init = serdes_init_10g, + .xcvr_init = xcvr_init_10g_bcm8706, + .link_status = link_status_10g_hotplug, +}; + static const struct niu_phy_ops phy_ops_10g_copper = { .serdes_init = serdes_init_10g, .link_status = link_status_10g, /* XXX */ @@ -1792,6 +2015,11 @@ static const struct niu_phy_template phy_template_10g_fiber = { .phy_addr_base = 8, }; +static const struct niu_phy_template phy_template_10g_fiber_hotplug = { + .ops = &phy_ops_10g_fiber_hotplug, + .phy_addr_base = 8, +}; + static const struct niu_phy_template phy_template_10g_copper = { .ops = &phy_ops_10g_copper, .phy_addr_base = 10, @@ -1996,6 +2224,13 @@ static int niu_determine_phy_disposition(struct niu *np) plat_type == PLAT_TYPE_VF_P1) phy_addr_off = 8; phy_addr_off += np->port; + if (np->flags & NIU_FLAGS_HOTPLUG_PHY) { + tp = &phy_template_10g_fiber_hotplug; + if (np->port == 0) + phy_addr_off = 8; + if (np->port == 1) + phy_addr_off = 12; + } break; case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: @@ -6773,6 +7008,37 @@ static int __devinit niu_phy_type_prop_decode(struct niu *np, return 0; } +/* niu board models have a trailing dash version incremented + * with HW rev change. Need to ingnore the dash version while + * checking for match + * + * for example, for the 10G card the current vpd.board_model + * is 501-5283-04, of which -04 is the dash version and have + * to be ignored + */ +static int niu_board_model_match(struct niu *np, const char *model) +{ + return !strncmp(np->vpd.board_model, model, strlen(model)); +} + +static int niu_pci_vpd_get_nports(struct niu *np) +{ + int ports = 0; + + if ((niu_board_model_match(np, NIU_QGC_LP_BM_STR)) || + (niu_board_model_match(np, NIU_QGC_PEM_BM_STR)) || + (niu_board_model_match(np, NIU_ALONSO_BM_STR))) { + ports = 4; + } else if ((niu_board_model_match(np, NIU_2XGF_LP_BM_STR)) || + (niu_board_model_match(np, NIU_2XGF_PEM_BM_STR)) || + (niu_board_model_match(np, NIU_FOXXY_BM_STR)) || + (niu_board_model_match(np, NIU_2XGF_MRVL_BM_STR))) { + ports = 2; + } + + return ports; +} + static void __devinit niu_pci_vpd_validate(struct niu *np) { struct net_device *dev = np->dev; @@ -6799,6 +7065,9 @@ static void __devinit niu_pci_vpd_validate(struct niu *np) } if (np->flags & NIU_FLAGS_10G) np->mac_xcvr = MAC_XCVR_XPCS; + } else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) { + np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER | + NIU_FLAGS_HOTPLUG_PHY); } else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { dev_err(np->device, PFX "Illegal phy string [%s].\n", np->vpd.phy_type); @@ -6987,11 +7256,17 @@ static int __devinit niu_get_and_validate_port(struct niu *np) if (parent->plat_type == PLAT_TYPE_NIU) { parent->num_ports = 2; } else { - parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) & - ESPC_NUM_PORTS_MACS_VAL; - - if (!parent->num_ports) - parent->num_ports = 4; + parent->num_ports = niu_pci_vpd_get_nports(np); + if (!parent->num_ports) { + /* Fall back to SPROM as last resort. + * This will fail on most cards. + */ + parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) & + ESPC_NUM_PORTS_MACS_VAL; + + if (!parent->num_ports) + return -ENODEV; + } } } @@ -7015,7 +7290,8 @@ static int __devinit phy_record(struct niu_parent *parent, return 0; if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) { if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) && - ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011)) + ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011) && + ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8706)) return 0; } else { if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R) @@ -7262,7 +7538,6 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent) u32 val; int err; - if (!strcmp(np->vpd.model, "SUNW,CP3220") || !strcmp(np->vpd.model, "SUNW,CP3260")) { num_10g = 0; @@ -7273,6 +7548,12 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent) phy_encode(PORT_TYPE_1G, 1) | phy_encode(PORT_TYPE_1G, 2) | phy_encode(PORT_TYPE_1G, 3)); + } else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) { + num_10g = 2; + num_1g = 0; + parent->num_ports = 2; + val = (phy_encode(PORT_TYPE_10G, 0) | + phy_encode(PORT_TYPE_10G, 1)); } else { err = fill_phy_probe_info(np, parent, info); if (err) @@ -7733,15 +8014,16 @@ static int __devinit niu_get_invariants(struct niu *np) have_props = !err; - err = niu_get_and_validate_port(np); - if (err) - return err; - err = niu_init_mac_ipp_pcs_base(np); if (err) return err; - if (!have_props) { + if (have_props) { + err = niu_get_and_validate_port(np); + if (err) + return err; + + } else { if (np->parent->plat_type == PLAT_TYPE_NIU) return -EINVAL; @@ -7753,10 +8035,17 @@ static int __devinit niu_get_invariants(struct niu *np) niu_pci_vpd_fetch(np, offset); nw64(ESPC_PIO_EN, 0); - if (np->flags & NIU_FLAGS_VPD_VALID) + if (np->flags & NIU_FLAGS_VPD_VALID) { niu_pci_vpd_validate(np); + err = niu_get_and_validate_port(np); + if (err) + return err; + } if (!(np->flags & NIU_FLAGS_VPD_VALID)) { + err = niu_get_and_validate_port(np); + if (err) + return err; err = niu_pci_probe_sprom(np); if (err) return err; diff --git a/drivers/net/niu.h b/drivers/net/niu.h index 336aed08b275..97ffbe137bcb 100644 --- a/drivers/net/niu.h +++ b/drivers/net/niu.h @@ -2537,6 +2537,7 @@ struct fcram_hash_ipv6 { #define NIU_PHY_ID_MASK 0xfffff0f0 #define NIU_PHY_ID_BCM8704 0x00206030 +#define NIU_PHY_ID_BCM8706 0x00206035 #define NIU_PHY_ID_BCM5464R 0x002060b0 #define NIU_PHY_ID_MRVL88X2011 0x01410020 @@ -2937,6 +2938,15 @@ struct rx_ring_info { #define NIU_MAX_MTU 9216 +/* VPD strings */ +#define NIU_QGC_LP_BM_STR "501-7606" +#define NIU_2XGF_LP_BM_STR "501-7283" +#define NIU_QGC_PEM_BM_STR "501-7765" +#define NIU_2XGF_PEM_BM_STR "501-7626" +#define NIU_ALONSO_BM_STR "373-0202" +#define NIU_FOXXY_BM_STR "501-7961" +#define NIU_2XGF_MRVL_BM_STR "SK-6E82" + #define NIU_VPD_MIN_MAJOR 3 #define NIU_VPD_MIN_MINOR 4 @@ -3199,6 +3209,8 @@ struct niu { struct niu_parent *parent; u32 flags; +#define NIU_FLAGS_HOTPLUG_PHY_PRESENT 0x02000000 /* Removebale PHY detected*/ +#define NIU_FLAGS_HOTPLUG_PHY 0x01000000 /* Removebale PHY */ #define NIU_FLAGS_VPD_VALID 0x00800000 /* VPD has valid version */ #define NIU_FLAGS_MSIX 0x00400000 /* MSI-X in use */ #define NIU_FLAGS_MCAST 0x00200000 /* multicast filter enabled */ diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 963630c65ca9..94e0b7ed76f1 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -89,6 +89,9 @@ int mdiobus_register(struct mii_bus *bus) phydev->bus = bus; + /* Run all of the fixups for this PHY */ + phy_scan_fixups(phydev); + err = device_register(&phydev->dev); if (err) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 12fccb1c76dc..3c18bb594957 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -406,8 +406,10 @@ int phy_mii_ioctl(struct phy_device *phydev, if (mii_data->reg_num == MII_BMCR && val & BMCR_RESET - && phydev->drv->config_init) + && phydev->drv->config_init) { + phy_scan_fixups(phydev); phydev->drv->config_init(phydev); + } break; default: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8b1121b02f98..ddf8d51832a6 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -53,6 +53,96 @@ static void phy_device_release(struct device *dev) phy_device_free(to_phy_device(dev)); } +static LIST_HEAD(phy_fixup_list); +static DEFINE_MUTEX(phy_fixup_lock); + +/* + * Creates a new phy_fixup and adds it to the list + * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) + * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) + * It can also be PHY_ANY_UID + * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before + * comparison + * @run: The actual code to be run when a matching PHY is found + */ +int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)) +{ + struct phy_fixup *fixup; + + fixup = kzalloc(sizeof(struct phy_fixup), GFP_KERNEL); + if (!fixup) + return -ENOMEM; + + strncpy(fixup->bus_id, bus_id, BUS_ID_SIZE); + fixup->phy_uid = phy_uid; + fixup->phy_uid_mask = phy_uid_mask; + fixup->run = run; + + mutex_lock(&phy_fixup_lock); + list_add_tail(&fixup->list, &phy_fixup_list); + mutex_unlock(&phy_fixup_lock); + + return 0; +} +EXPORT_SYMBOL(phy_register_fixup); + +/* Registers a fixup to be run on any PHY with the UID in phy_uid */ +int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)) +{ + return phy_register_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask, run); +} +EXPORT_SYMBOL(phy_register_fixup_for_uid); + +/* Registers a fixup to be run on the PHY with id string bus_id */ +int phy_register_fixup_for_id(const char *bus_id, + int (*run)(struct phy_device *)) +{ + return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run); +} +EXPORT_SYMBOL(phy_register_fixup_for_id); + +/* + * Returns 1 if fixup matches phydev in bus_id and phy_uid. + * Fixups can be set to match any in one or more fields. + */ +static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) +{ + if (strcmp(fixup->bus_id, phydev->dev.bus_id) != 0) + if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) + return 0; + + if ((fixup->phy_uid & fixup->phy_uid_mask) != + (phydev->phy_id & fixup->phy_uid_mask)) + if (fixup->phy_uid != PHY_ANY_UID) + return 0; + + return 1; +} + +/* Runs any matching fixups for this phydev */ +int phy_scan_fixups(struct phy_device *phydev) +{ + struct phy_fixup *fixup; + + mutex_lock(&phy_fixup_lock); + list_for_each_entry(fixup, &phy_fixup_list, list) { + if (phy_needs_fixup(phydev, fixup)) { + int err; + + err = fixup->run(phydev); + + if (err < 0) + return err; + } + } + mutex_unlock(&phy_fixup_lock); + + return 0; +} +EXPORT_SYMBOL(phy_scan_fixups); + struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *dev; @@ -179,13 +269,13 @@ void phy_prepare_link(struct phy_device *phydev, * choose to call only the subset of functions which provide * the desired functionality. */ -struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, +struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, void (*handler)(struct net_device *), u32 flags, phy_interface_t interface) { struct phy_device *phydev; - phydev = phy_attach(dev, phy_id, flags, interface); + phydev = phy_attach(dev, bus_id, flags, interface); if (IS_ERR(phydev)) return phydev; @@ -226,7 +316,7 @@ static int phy_compare_id(struct device *dev, void *data) /** * phy_attach - attach a network device to a particular PHY device * @dev: network device to attach - * @phy_id: PHY device to attach + * @bus_id: PHY device to attach * @flags: PHY device's dev_flags * @interface: PHY device's interface * @@ -238,7 +328,7 @@ static int phy_compare_id(struct device *dev, void *data) * change. The phy_device is returned to the attaching driver. */ struct phy_device *phy_attach(struct net_device *dev, - const char *phy_id, u32 flags, phy_interface_t interface) + const char *bus_id, u32 flags, phy_interface_t interface) { struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; @@ -246,12 +336,12 @@ struct phy_device *phy_attach(struct net_device *dev, /* Search the list of PHY devices on the mdio bus for the * PHY with the requested name */ - d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id); + d = bus_find_device(bus, NULL, (void *)bus_id, phy_compare_id); if (d) { phydev = to_phy_device(d); } else { - printk(KERN_ERR "%s not found\n", phy_id); + printk(KERN_ERR "%s not found\n", bus_id); return ERR_PTR(-ENODEV); } @@ -271,7 +361,7 @@ struct phy_device *phy_attach(struct net_device *dev, if (phydev->attached_dev) { printk(KERN_ERR "%s: %s already attached\n", - dev->name, phy_id); + dev->name, bus_id); return ERR_PTR(-EBUSY); } @@ -287,6 +377,11 @@ struct phy_device *phy_attach(struct net_device *dev, if (phydev->drv->config_init) { int err; + err = phy_scan_fixups(phydev); + + if (err < 0) + return ERR_PTR(err); + err = phydev->drv->config_init(phydev); if (err < 0) @@ -395,6 +490,7 @@ EXPORT_SYMBOL(genphy_config_advert); */ int genphy_setup_forced(struct phy_device *phydev) { + int err; int ctl = 0; phydev->pause = phydev->asym_pause = 0; @@ -407,17 +503,26 @@ int genphy_setup_forced(struct phy_device *phydev) if (DUPLEX_FULL == phydev->duplex) ctl |= BMCR_FULLDPLX; - ctl = phy_write(phydev, MII_BMCR, ctl); + err = phy_write(phydev, MII_BMCR, ctl); - if (ctl < 0) - return ctl; + if (err < 0) + return err; + + /* + * Run the fixups on this PHY, just in case the + * board code needs to change something after a reset + */ + err = phy_scan_fixups(phydev); + + if (err < 0) + return err; /* We just reset the device, so we'd better configure any * settings the PHY requires to operate */ if (phydev->drv->config_init) - ctl = phydev->drv->config_init(phydev); + err = phydev->drv->config_init(phydev); - return ctl; + return err; } diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index dcbe01b0ca0d..157fd932e951 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -86,7 +86,7 @@ #include "s2io.h" #include "s2io-regs.h" -#define DRV_VERSION "2.0.26.20" +#define DRV_VERSION "2.0.26.22" /* S2io Driver name & version. */ static char s2io_driver_name[] = "Neterion"; @@ -117,20 +117,6 @@ static inline int RXD_IS_UP2DT(struct RxD_t *rxdp) #define LINK_IS_UP(val64) (!(val64 & (ADAPTER_STATUS_RMAC_REMOTE_FAULT | \ ADAPTER_STATUS_RMAC_LOCAL_FAULT))) -#define TASKLET_IN_USE test_and_set_bit(0, (&sp->tasklet_status)) -#define PANIC 1 -#define LOW 2 -static inline int rx_buffer_level(struct s2io_nic * sp, int rxb_size, int ring) -{ - struct mac_info *mac_control; - - mac_control = &sp->mac_control; - if (rxb_size <= rxd_count[sp->rxd_mode]) - return PANIC; - else if ((mac_control->rings[ring].pkt_cnt - rxb_size) > 16) - return LOW; - return 0; -} static inline int is_s2io_card_up(const struct s2io_nic * sp) { @@ -2458,7 +2444,7 @@ static void free_tx_buffers(struct s2io_nic *nic) for (i = 0; i < config->tx_fifo_num; i++) { unsigned long flags; spin_lock_irqsave(&mac_control->fifos[i].tx_lock, flags); - for (j = 0; j < config->tx_cfg[i].fifo_len - 1; j++) { + for (j = 0; j < config->tx_cfg[i].fifo_len; j++) { txdp = (struct TxD *) \ mac_control->fifos[i].list_info[j].list_virt_addr; skb = s2io_txdl_getskb(&mac_control->fifos[i], txdp, j); @@ -2544,7 +2530,6 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) struct config_param *config; u64 tmp; struct buffAdd *ba; - unsigned long flags; struct RxD_t *first_rxdp = NULL; u64 Buffer0_ptr = 0, Buffer1_ptr = 0; struct RxD1 *rxdp1; @@ -2592,15 +2577,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) DBG_PRINT(INTR_DBG, "%s: Next block at: %p\n", dev->name, rxdp); } - if(!napi) { - spin_lock_irqsave(&nic->put_lock, flags); - mac_control->rings[ring_no].put_pos = - (block_no * (rxd_count[nic->rxd_mode] + 1)) + off; - spin_unlock_irqrestore(&nic->put_lock, flags); - } else { - mac_control->rings[ring_no].put_pos = - (block_no * (rxd_count[nic->rxd_mode] + 1)) + off; - } + if ((rxdp->Control_1 & RXD_OWN_XENA) && ((nic->rxd_mode == RXD_MODE_3B) && (rxdp->Control_2 & s2BIT(0)))) { @@ -2978,7 +2955,7 @@ static void rx_intr_handler(struct ring_info *ring_data) { struct s2io_nic *nic = ring_data->nic; struct net_device *dev = (struct net_device *) nic->dev; - int get_block, put_block, put_offset; + int get_block, put_block; struct rx_curr_get_info get_info, put_info; struct RxD_t *rxdp; struct sk_buff *skb; @@ -2987,19 +2964,11 @@ static void rx_intr_handler(struct ring_info *ring_data) struct RxD1* rxdp1; struct RxD3* rxdp3; - spin_lock(&nic->rx_lock); - get_info = ring_data->rx_curr_get_info; get_block = get_info.block_index; memcpy(&put_info, &ring_data->rx_curr_put_info, sizeof(put_info)); put_block = put_info.block_index; rxdp = ring_data->rx_blocks[get_block].rxds[get_info.offset].virt_addr; - if (!napi) { - spin_lock(&nic->put_lock); - put_offset = ring_data->put_pos; - spin_unlock(&nic->put_lock); - } else - put_offset = ring_data->put_pos; while (RXD_IS_UP2DT(rxdp)) { /* @@ -3016,7 +2985,6 @@ static void rx_intr_handler(struct ring_info *ring_data) DBG_PRINT(ERR_DBG, "%s: The skb is ", dev->name); DBG_PRINT(ERR_DBG, "Null in Rx Intr\n"); - spin_unlock(&nic->rx_lock); return; } if (nic->rxd_mode == RXD_MODE_1) { @@ -3072,8 +3040,6 @@ static void rx_intr_handler(struct ring_info *ring_data) } } } - - spin_unlock(&nic->rx_lock); } /** @@ -4105,7 +4071,6 @@ static int s2io_close(struct net_device *dev) do_s2io_delete_unicast_mc(sp, tmp64); } - /* Reset card, kill tasklet and free Tx and Rx buffers. */ s2io_card_down(sp); return 0; @@ -4370,29 +4335,9 @@ s2io_alarm_handle(unsigned long data) static int s2io_chk_rx_buffers(struct s2io_nic *sp, int rng_n) { - int rxb_size, level; - - if (!sp->lro) { - rxb_size = atomic_read(&sp->rx_bufs_left[rng_n]); - level = rx_buffer_level(sp, rxb_size, rng_n); - - if ((level == PANIC) && (!TASKLET_IN_USE)) { - int ret; - DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__); - DBG_PRINT(INTR_DBG, "PANIC levels\n"); - if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) { - DBG_PRINT(INFO_DBG, "Out of memory in %s", - __FUNCTION__); - clear_bit(0, (&sp->tasklet_status)); - return -1; - } - clear_bit(0, (&sp->tasklet_status)); - } else if (level == LOW) - tasklet_schedule(&sp->task); - - } else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { - DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name); - DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); + if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { + DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name); + DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } return 0; } @@ -6770,49 +6715,6 @@ static int s2io_change_mtu(struct net_device *dev, int new_mtu) } /** - * s2io_tasklet - Bottom half of the ISR. - * @dev_adr : address of the device structure in dma_addr_t format. - * Description: - * This is the tasklet or the bottom half of the ISR. This is - * an extension of the ISR which is scheduled by the scheduler to be run - * when the load on the CPU is low. All low priority tasks of the ISR can - * be pushed into the tasklet. For now the tasklet is used only to - * replenish the Rx buffers in the Rx buffer descriptors. - * Return value: - * void. - */ - -static void s2io_tasklet(unsigned long dev_addr) -{ - struct net_device *dev = (struct net_device *) dev_addr; - struct s2io_nic *sp = dev->priv; - int i, ret; - struct mac_info *mac_control; - struct config_param *config; - - mac_control = &sp->mac_control; - config = &sp->config; - - if (!TASKLET_IN_USE) { - for (i = 0; i < config->rx_ring_num; i++) { - ret = fill_rx_buffers(sp, i); - if (ret == -ENOMEM) { - DBG_PRINT(INFO_DBG, "%s: Out of ", - dev->name); - DBG_PRINT(INFO_DBG, "memory in tasklet\n"); - break; - } else if (ret == -EFILL) { - DBG_PRINT(INFO_DBG, - "%s: Rx Ring %d is full\n", - dev->name, i); - break; - } - } - clear_bit(0, (&sp->tasklet_status)); - } -} - -/** * s2io_set_link - Set the LInk status * @data: long pointer to device private structue * Description: Sets the link status for the adapter @@ -7161,7 +7063,6 @@ static void do_s2io_card_down(struct s2io_nic * sp, int do_io) { int cnt = 0; struct XENA_dev_config __iomem *bar0 = sp->bar0; - unsigned long flags; register u64 val64 = 0; struct config_param *config; config = &sp->config; @@ -7186,9 +7087,6 @@ static void do_s2io_card_down(struct s2io_nic * sp, int do_io) s2io_rem_isr(sp); - /* Kill tasklet. */ - tasklet_kill(&sp->task); - /* Check if the device is Quiescent and then Reset the NIC */ while(do_io) { /* As per the HW requirement we need to replenish the @@ -7223,9 +7121,7 @@ static void do_s2io_card_down(struct s2io_nic * sp, int do_io) free_tx_buffers(sp); /* Free all Rx buffers */ - spin_lock_irqsave(&sp->rx_lock, flags); free_rx_buffers(sp); - spin_unlock_irqrestore(&sp->rx_lock, flags); clear_bit(__S2IO_STATE_LINK_TASK, &(sp->state)); } @@ -7314,9 +7210,6 @@ static int s2io_card_up(struct s2io_nic * sp) S2IO_TIMER_CONF(sp->alarm_timer, s2io_alarm_handle, sp, (HZ/2)); - /* Enable tasklet for the device */ - tasklet_init(&sp->task, s2io_tasklet, (unsigned long) dev); - /* Enable select interrupts */ en_dis_err_alarms(sp, ENA_ALL_INTRS, ENABLE_INTRS); if (sp->config.intr_type != INTA) @@ -8119,20 +8012,15 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) s2io_reset(sp); /* - * Initialize the tasklet status and link state flags + * Initialize link state flags * and the card state parameter */ - sp->tasklet_status = 0; sp->state = 0; /* Initialize spinlocks */ for (i = 0; i < sp->config.tx_fifo_num; i++) spin_lock_init(&mac_control->fifos[i].tx_lock); - if (!napi) - spin_lock_init(&sp->put_lock); - spin_lock_init(&sp->rx_lock); - /* * SXE-002: Configure link and activity LED to init state * on driver load. diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index e68fdf7e4260..ce53a02105f2 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -703,9 +703,6 @@ struct ring_info { */ struct rx_curr_get_info rx_curr_get_info; - /* Index to the absolute position of the put pointer of Rx ring */ - int put_pos; - /* Buffer Address store. */ struct buffAdd **ba; struct s2io_nic *nic; @@ -868,8 +865,6 @@ struct s2io_nic { int device_enabled_once; char name[60]; - struct tasklet_struct task; - volatile unsigned long tasklet_status; /* Timer that handles I/O errors/exceptions */ struct timer_list alarm_timer; @@ -879,8 +874,6 @@ struct s2io_nic { atomic_t rx_bufs_left[MAX_RX_RINGS]; - spinlock_t put_lock; - #define PROMISC 1 #define ALL_MULTI 2 @@ -964,7 +957,6 @@ struct s2io_nic { u8 lro; u16 lro_max_aggr_per_sess; volatile unsigned long state; - spinlock_t rx_lock; u64 general_int_mask; #define VPD_STRING_LEN 80 u8 product_name[VPD_STRING_LEN]; @@ -1094,7 +1086,6 @@ static void s2io_handle_errors(void * dev_id); static int s2io_starter(void); static void s2io_closer(void); static void s2io_tx_watchdog(struct net_device *dev); -static void s2io_tasklet(unsigned long dev_addr); static void s2io_set_multicast(struct net_device *dev); static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp); static void s2io_link(struct s2io_nic * sp, int link); diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index 78994ede0cb0..6261201403cd 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -825,7 +825,8 @@ static struct platform_driver sgiseeq_driver = { .probe = sgiseeq_probe, .remove = __devexit_p(sgiseeq_remove), .driver = { - .name = "sgiseeq" + .name = "sgiseeq", + .owner = THIS_MODULE, } }; @@ -850,3 +851,4 @@ module_exit(sgiseeq_module_exit); MODULE_DESCRIPTION("SGI Seeq 8003 driver"); MODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sgiseeq"); diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 76cc1d3adf71..4e2800205189 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -92,6 +92,7 @@ module_param(tx_fifo_kb, int, 0400); MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1<x<15)(default=8)"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:smc911x"); /* * The internal workings of the driver. If you are changing anything @@ -243,7 +244,7 @@ static void smc911x_reset(struct net_device *dev) do { udelay(10); reg = SMC_GET_PMT_CTRL() & PMT_CTRL_READY_; - } while ( timeout-- && !reg); + } while (--timeout && !reg); if (timeout == 0) { PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name); return; @@ -267,7 +268,7 @@ static void smc911x_reset(struct net_device *dev) resets++; break; } - } while ( timeout-- && (reg & HW_CFG_SRST_)); + } while (--timeout && (reg & HW_CFG_SRST_)); } if (timeout == 0) { PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name); @@ -413,7 +414,7 @@ static inline void smc911x_drop_pkt(struct net_device *dev) do { udelay(10); reg = SMC_GET_RX_DP_CTRL() & RX_DP_CTRL_FFWD_BUSY_; - } while ( timeout-- && reg); + } while (--timeout && reg); if (timeout == 0) { PRINTK("%s: timeout waiting for RX fast forward\n", dev->name); } @@ -2262,6 +2263,7 @@ static struct platform_driver smc911x_driver = { .resume = smc911x_drv_resume, .driver = { .name = CARDNAME, + .owner = THIS_MODULE, }, }; diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 600b92af3334..a188e33484e6 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -132,6 +132,7 @@ module_param(watchdog, int, 0400); MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:smc91x"); /* * The internal workings of the driver. If you are changing anything @@ -2308,6 +2309,7 @@ static struct platform_driver smc_driver = { .resume = smc_drv_resume, .driver = { .name = CARDNAME, + .owner = THIS_MODULE, }, }; diff --git a/drivers/net/sni_82596.c b/drivers/net/sni_82596.c index 2cf6794acb4f..854ccf2b4105 100644 --- a/drivers/net/sni_82596.c +++ b/drivers/net/sni_82596.c @@ -44,6 +44,7 @@ static const char sni_82596_string[] = "snirm_82596"; MODULE_AUTHOR("Thomas Bogendoerfer"); MODULE_DESCRIPTION("i82596 driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:snirm_82596"); module_param(i596_debug, int, 0); MODULE_PARM_DESC(i596_debug, "82596 debug mask"); @@ -166,6 +167,7 @@ static struct platform_driver sni_82596_driver = { .remove = __devexit_p(sni_82596_driver_remove), .driver = { .name = sni_82596_string, + .owner = THIS_MODULE, }, }; diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c index 17585e5eed53..e83b166aa6b9 100644 --- a/drivers/net/tehuti.c +++ b/drivers/net/tehuti.c @@ -625,6 +625,12 @@ static void __init bdx_firmware_endianess(void) s_firmLoad[i] = CPU_CHIP_SWAP32(s_firmLoad[i]); } +static int bdx_range_check(struct bdx_priv *priv, u32 offset) +{ + return (offset > (u32) (BDX_REGS_SIZE / priv->nic->port_num)) ? + -EINVAL : 0; +} + static int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd) { struct bdx_priv *priv = ndev->priv; @@ -643,9 +649,15 @@ static int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd) DBG("%d 0x%x 0x%x\n", data[0], data[1], data[2]); } + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + switch (data[0]) { case BDX_OP_READ: + error = bdx_range_check(priv, data[1]); + if (error < 0) + return error; data[2] = READ_REG(priv, data[1]); DBG("read_reg(0x%x)=0x%x (dec %d)\n", data[1], data[2], data[2]); @@ -655,6 +667,9 @@ static int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd) break; case BDX_OP_WRITE: + error = bdx_range_check(priv, data[1]); + if (error < 0) + return error; WRITE_REG(priv, data[1], data[2]); DBG("write_reg(0x%x, 0x%x)\n", data[1], data[2]); break; diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index bc4c62b8e81a..e3f74c9f78bd 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -4017,6 +4017,8 @@ static int tg3_halt(struct tg3 *, int, int); * Invoked with tp->lock held. */ static int tg3_restart_hw(struct tg3 *tp, int reset_phy) + __releases(tp->lock) + __acquires(tp->lock) { int err; diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 6f33f84d37b0..6017d5267d08 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -162,6 +162,7 @@ static struct platform_driver tsi_eth_driver = { .remove = tsi108_ether_remove, .driver = { .name = "tsi-ethernet", + .owner = THIS_MODULE, }, }; @@ -1729,3 +1730,4 @@ module_exit(tsi108_ether_exit); MODULE_AUTHOR("Tundra Semiconductor Corporation"); MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tsi-ethernet"); diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 333961bb7873..c0dd25ba7a18 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -2183,7 +2183,6 @@ typhoon_resume(struct pci_dev *pdev) } netif_device_attach(dev); - netif_start_queue(dev); return 0; reset: diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 2f11254bcc07..281ce3d39532 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -3932,7 +3932,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); fixed_link = of_get_property(np, "fixed-link", NULL); if (fixed_link) { - ug_info->mdio_bus = 0; + snprintf(ug_info->mdio_bus, MII_BUS_ID_SIZE, "0"); ug_info->phy_address = fixed_link[0]; phy = NULL; } else { diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index ed1afaf683a4..6b8d882d197b 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -605,7 +605,6 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index, static void velocity_init_cam_filter(struct velocity_info *vptr) { struct mac_regs __iomem * regs = vptr->mac_regs; - unsigned short vid; /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); @@ -617,29 +616,33 @@ static void velocity_init_cam_filter(struct velocity_info *vptr) mac_set_vlan_cam_mask(regs, vptr->vCAMmask); mac_set_cam_mask(regs, vptr->mCAMmask); - /* Enable first VCAM */ + /* Enable VCAMs */ if (vptr->vlgrp) { - for (vid = 0; vid < VLAN_VID_MASK; vid++) { - if (vlan_group_get_device(vptr->vlgrp, vid)) { - /* If Tagging option is enabled and - VLAN ID is not zero, then - turn on MCFG_RTGOPT also */ - if (vid != 0) - WORD_REG_BITS_ON(MCFG_RTGOPT, ®s->MCFG); + unsigned int vid, i = 0; + + if (!vlan_group_get_device(vptr->vlgrp, 0)) + WORD_REG_BITS_ON(MCFG_RTGOPT, ®s->MCFG); - mac_set_vlan_cam(regs, 0, (u8 *) &vid); + for (vid = 1; (vid < VLAN_VID_MASK); vid++) { + if (vlan_group_get_device(vptr->vlgrp, vid)) { + mac_set_vlan_cam(regs, i, (u8 *) &vid); + vptr->vCAMmask[i / 8] |= 0x1 << (i % 8); + if (++i >= VCAM_SIZE) + break; } } - vptr->vCAMmask[0] |= 1; mac_set_vlan_cam_mask(regs, vptr->vCAMmask); - } else { - u16 temp = 0; - mac_set_vlan_cam(regs, 0, (u8 *) &temp); - temp = 1; - mac_set_vlan_cam_mask(regs, (u8 *) &temp); } } +static void velocity_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct velocity_info *vptr = netdev_priv(dev); + + vptr->vlgrp = grp; +} + static void velocity_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct velocity_info *vptr = netdev_priv(dev); @@ -959,11 +962,13 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi dev->vlan_rx_add_vid = velocity_vlan_rx_add_vid; dev->vlan_rx_kill_vid = velocity_vlan_rx_kill_vid; + dev->vlan_rx_register = velocity_vlan_rx_register; #ifdef VELOCITY_ZERO_COPY_SUPPORT dev->features |= NETIF_F_SG; #endif - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER; + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | + NETIF_F_HW_VLAN_RX; if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) dev->features |= NETIF_F_IP_CSUM; @@ -1597,8 +1602,13 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) skb_put(skb, pkt_len - 4); skb->protocol = eth_type_trans(skb, vptr->dev); + if (vptr->vlgrp && (rd->rdesc0.RSR & RSR_DETAG)) { + vlan_hwaccel_rx(skb, vptr->vlgrp, + swab16(le16_to_cpu(rd->rdesc1.PQTAG))); + } else + netif_rx(skb); + stats->rx_bytes += pkt_len; - netif_rx(skb); return 0; } diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index c4c8eab8574f..c2cc42f723d5 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -402,7 +402,7 @@ static int __init c101_init(void) #ifdef MODULE printk(KERN_INFO "c101: no card initialized\n"); #endif - return -ENOSYS; /* no parameters specified, abort */ + return -EINVAL; /* no parameters specified, abort */ } printk(KERN_INFO "%s\n", version); @@ -420,11 +420,11 @@ static int __init c101_init(void) c101_run(irq, ram); if (*hw == '\x0') - return first_card ? 0 : -ENOSYS; + return first_card ? 0 : -EINVAL; }while(*hw++ == ':'); printk(KERN_ERR "c101: invalid hardware parameters\n"); - return first_card ? 0 : -ENOSYS; + return first_card ? 0 : -EINVAL; } diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index c4ab0326f911..520bb0b1a9a2 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -1090,10 +1090,6 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) pvc_device *pvc = NULL; struct net_device *dev; int result, used; - char * prefix = "pvc%d"; - - if (type == ARPHRD_ETHER) - prefix = "pvceth%d"; if ((pvc = add_pvc(frad, dlci)) == NULL) { printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n", diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 7483d45bc5bc..e62018a36133 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1809,3 +1809,5 @@ module_exit(netif_exit); MODULE_DESCRIPTION("Xen virtual network device frontend"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:vif"); +MODULE_ALIAS("xennet"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1bd5fb30237d..e3dc8f8d0c3e 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1930,6 +1930,20 @@ config FB_VIRTUAL If unsure, say N. +config XEN_FBDEV_FRONTEND + tristate "Xen virtual frame buffer support" + depends on FB && XEN + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + default y + help + This driver implements the front-end of the Xen virtual + frame buffer driver. It communicates with a back-end + in another domain. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 11c0e5e05f21..f172b9b73314 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_FB_OMAP) += omap/ +obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o # Platform or fallback drivers go here obj-$(CONFIG_FB_UVESA) += uvesafb.o diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c new file mode 100644 index 000000000000..619a6f8d65a2 --- /dev/null +++ b/drivers/video/xen-fbfront.c @@ -0,0 +1,550 @@ +/* + * Xen para-virtual frame buffer device + * + * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com> + * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> + * + * Based on linux/drivers/video/q40fb.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +/* + * TODO: + * + * Switch to grant tables when they become capable of dealing with the + * frame buffer. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <asm/xen/hypervisor.h> +#include <xen/events.h> +#include <xen/page.h> +#include <xen/interface/io/fbif.h> +#include <xen/interface/io/protocols.h> +#include <xen/xenbus.h> + +struct xenfb_info { + unsigned char *fb; + struct fb_info *fb_info; + int x1, y1, x2, y2; /* dirty rectangle, + protected by dirty_lock */ + spinlock_t dirty_lock; + int nr_pages; + int irq; + struct xenfb_page *page; + unsigned long *mfns; + int update_wanted; /* XENFB_TYPE_UPDATE wanted */ + + struct xenbus_device *xbdev; +}; + +static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; + +static int xenfb_remove(struct xenbus_device *); +static void xenfb_init_shared_page(struct xenfb_info *); +static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); +static void xenfb_disconnect_backend(struct xenfb_info *); + +static void xenfb_do_update(struct xenfb_info *info, + int x, int y, int w, int h) +{ + union xenfb_out_event event; + u32 prod; + + event.type = XENFB_TYPE_UPDATE; + event.update.x = x; + event.update.y = y; + event.update.width = w; + event.update.height = h; + + prod = info->page->out_prod; + /* caller ensures !xenfb_queue_full() */ + mb(); /* ensure ring space available */ + XENFB_OUT_RING_REF(info->page, prod) = event; + wmb(); /* ensure ring contents visible */ + info->page->out_prod = prod + 1; + + notify_remote_via_irq(info->irq); +} + +static int xenfb_queue_full(struct xenfb_info *info) +{ + u32 cons, prod; + + prod = info->page->out_prod; + cons = info->page->out_cons; + return prod - cons == XENFB_OUT_RING_LEN; +} + +static void xenfb_refresh(struct xenfb_info *info, + int x1, int y1, int w, int h) +{ + unsigned long flags; + int y2 = y1 + h - 1; + int x2 = x1 + w - 1; + + if (!info->update_wanted) + return; + + spin_lock_irqsave(&info->dirty_lock, flags); + + /* Combine with dirty rectangle: */ + if (info->y1 < y1) + y1 = info->y1; + if (info->y2 > y2) + y2 = info->y2; + if (info->x1 < x1) + x1 = info->x1; + if (info->x2 > x2) + x2 = info->x2; + + if (xenfb_queue_full(info)) { + /* Can't send right now, stash it in the dirty rectangle */ + info->x1 = x1; + info->x2 = x2; + info->y1 = y1; + info->y2 = y2; + spin_unlock_irqrestore(&info->dirty_lock, flags); + return; + } + + /* Clear dirty rectangle: */ + info->x1 = info->y1 = INT_MAX; + info->x2 = info->y2 = 0; + + spin_unlock_irqrestore(&info->dirty_lock, flags); + + if (x1 <= x2 && y1 <= y2) + xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1); +} + +static void xenfb_deferred_io(struct fb_info *fb_info, + struct list_head *pagelist) +{ + struct xenfb_info *info = fb_info->par; + struct page *page; + unsigned long beg, end; + int y1, y2, miny, maxy; + + miny = INT_MAX; + maxy = 0; + list_for_each_entry(page, pagelist, lru) { + beg = page->index << PAGE_SHIFT; + end = beg + PAGE_SIZE - 1; + y1 = beg / fb_info->fix.line_length; + y2 = end / fb_info->fix.line_length; + if (y2 >= fb_info->var.yres) + y2 = fb_info->var.yres - 1; + if (miny > y1) + miny = y1; + if (maxy < y2) + maxy = y2; + } + xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1); +} + +static struct fb_deferred_io xenfb_defio = { + .delay = HZ / 20, + .deferred_io = xenfb_deferred_io, +}; + +static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 v; + + if (regno > info->cmap.len) + return 1; + +#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); +#undef CNVT_TOHW + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + + switch (info->var.bits_per_pixel) { + case 16: + case 24: + case 32: + ((u32 *)info->pseudo_palette)[regno] = v; + break; + } + + return 0; +} + +static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + struct xenfb_info *info = p->par; + + sys_fillrect(p, rect); + xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); +} + +static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) +{ + struct xenfb_info *info = p->par; + + sys_imageblit(p, image); + xenfb_refresh(info, image->dx, image->dy, image->width, image->height); +} + +static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + struct xenfb_info *info = p->par; + + sys_copyarea(p, area); + xenfb_refresh(info, area->dx, area->dy, area->width, area->height); +} + +static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct xenfb_info *info = p->par; + ssize_t res; + + res = fb_sys_write(p, buf, count, ppos); + xenfb_refresh(info, 0, 0, info->page->width, info->page->height); + return res; +} + +static struct fb_ops xenfb_fb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = xenfb_write, + .fb_setcolreg = xenfb_setcolreg, + .fb_fillrect = xenfb_fillrect, + .fb_copyarea = xenfb_copyarea, + .fb_imageblit = xenfb_imageblit, +}; + +static irqreturn_t xenfb_event_handler(int rq, void *dev_id) +{ + /* + * No in events recognized, simply ignore them all. + * If you need to recognize some, see xen-kbdfront's + * input_handler() for how to do that. + */ + struct xenfb_info *info = dev_id; + struct xenfb_page *page = info->page; + + if (page->in_cons != page->in_prod) { + info->page->in_cons = info->page->in_prod; + notify_remote_via_irq(info->irq); + } + + /* Flush dirty rectangle: */ + xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX); + + return IRQ_HANDLED; +} + +static int __devinit xenfb_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + struct xenfb_info *info; + struct fb_info *fb_info; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + dev->dev.driver_data = info; + info->xbdev = dev; + info->irq = -1; + info->x1 = info->y1 = INT_MAX; + spin_lock_init(&info->dirty_lock); + + info->fb = vmalloc(xenfb_mem_len); + if (info->fb == NULL) + goto error_nomem; + memset(info->fb, 0, xenfb_mem_len); + + info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; + + info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); + if (!info->mfns) + goto error_nomem; + + /* set up shared page */ + info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!info->page) + goto error_nomem; + + xenfb_init_shared_page(info); + + /* abusing framebuffer_alloc() to allocate pseudo_palette */ + fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); + if (fb_info == NULL) + goto error_nomem; + + /* complete the abuse: */ + fb_info->pseudo_palette = fb_info->par; + fb_info->par = info; + + fb_info->screen_base = info->fb; + + fb_info->fbops = &xenfb_fb_ops; + fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; + fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; + fb_info->var.bits_per_pixel = info->page->depth; + + fb_info->var.red = (struct fb_bitfield){16, 8, 0}; + fb_info->var.green = (struct fb_bitfield){8, 8, 0}; + fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; + + fb_info->var.activate = FB_ACTIVATE_NOW; + fb_info->var.height = -1; + fb_info->var.width = -1; + fb_info->var.vmode = FB_VMODE_NONINTERLACED; + + fb_info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_info->fix.line_length = info->page->line_length; + fb_info->fix.smem_start = 0; + fb_info->fix.smem_len = xenfb_mem_len; + strcpy(fb_info->fix.id, "xen"); + fb_info->fix.type = FB_TYPE_PACKED_PIXELS; + fb_info->fix.accel = FB_ACCEL_NONE; + + fb_info->flags = FBINFO_FLAG_DEFAULT; + + ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); + if (ret < 0) { + framebuffer_release(fb_info); + xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); + goto error; + } + + fb_info->fbdefio = &xenfb_defio; + fb_deferred_io_init(fb_info); + + ret = register_framebuffer(fb_info); + if (ret) { + fb_deferred_io_cleanup(fb_info); + fb_dealloc_cmap(&fb_info->cmap); + framebuffer_release(fb_info); + xenbus_dev_fatal(dev, ret, "register_framebuffer"); + goto error; + } + info->fb_info = fb_info; + + ret = xenfb_connect_backend(dev, info); + if (ret < 0) + goto error; + + return 0; + + error_nomem: + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + error: + xenfb_remove(dev); + return ret; +} + +static int xenfb_resume(struct xenbus_device *dev) +{ + struct xenfb_info *info = dev->dev.driver_data; + + xenfb_disconnect_backend(info); + xenfb_init_shared_page(info); + return xenfb_connect_backend(dev, info); +} + +static int xenfb_remove(struct xenbus_device *dev) +{ + struct xenfb_info *info = dev->dev.driver_data; + + xenfb_disconnect_backend(info); + if (info->fb_info) { + fb_deferred_io_cleanup(info->fb_info); + unregister_framebuffer(info->fb_info); + fb_dealloc_cmap(&info->fb_info->cmap); + framebuffer_release(info->fb_info); + } + free_page((unsigned long)info->page); + vfree(info->mfns); + vfree(info->fb); + kfree(info); + + return 0; +} + +static unsigned long vmalloc_to_mfn(void *address) +{ + return pfn_to_mfn(vmalloc_to_pfn(address)); +} + +static void xenfb_init_shared_page(struct xenfb_info *info) +{ + int i; + + for (i = 0; i < info->nr_pages; i++) + info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); + + info->page->pd[0] = vmalloc_to_mfn(info->mfns); + info->page->pd[1] = 0; + info->page->width = XENFB_WIDTH; + info->page->height = XENFB_HEIGHT; + info->page->depth = XENFB_DEPTH; + info->page->line_length = (info->page->depth / 8) * info->page->width; + info->page->mem_length = xenfb_mem_len; + info->page->in_cons = info->page->in_prod = 0; + info->page->out_cons = info->page->out_prod = 0; +} + +static int xenfb_connect_backend(struct xenbus_device *dev, + struct xenfb_info *info) +{ + int ret, evtchn; + struct xenbus_transaction xbt; + + ret = xenbus_alloc_evtchn(dev, &evtchn); + if (ret) + return ret; + ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, + 0, dev->devicetype, info); + if (ret < 0) { + xenbus_free_evtchn(dev, evtchn); + xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); + return ret; + } + info->irq = ret; + + again: + ret = xenbus_transaction_start(&xbt); + if (ret) { + xenbus_dev_fatal(dev, ret, "starting transaction"); + return ret; + } + ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + virt_to_mfn(info->page)); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + evtchn); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", + XEN_IO_PROTO_ABI_NATIVE); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); + if (ret) + goto error_xenbus; + ret = xenbus_transaction_end(xbt, 0); + if (ret) { + if (ret == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, ret, "completing transaction"); + return ret; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + return 0; + + error_xenbus: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, ret, "writing xenstore"); + return ret; +} + +static void xenfb_disconnect_backend(struct xenfb_info *info) +{ + if (info->irq >= 0) + unbind_from_irqhandler(info->irq, info); + info->irq = -1; +} + +static void xenfb_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct xenfb_info *info = dev->dev.driver_data; + int val; + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: +InitWait: + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateConnected: + /* + * Work around xenbus race condition: If backend goes + * through InitWait to Connected fast enough, we can + * get Connected twice here. + */ + if (dev->state != XenbusStateConnected) + goto InitWait; /* no InitWait seen yet, fudge it */ + + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "request-update", "%d", &val) < 0) + val = 0; + if (val) + info->update_wanted = 1; + break; + + case XenbusStateClosing: + xenbus_frontend_closed(dev); + break; + } +} + +static struct xenbus_device_id xenfb_ids[] = { + { "vfb" }, + { "" } +}; + +static struct xenbus_driver xenfb = { + .name = "vfb", + .owner = THIS_MODULE, + .ids = xenfb_ids, + .probe = xenfb_probe, + .remove = xenfb_remove, + .resume = xenfb_resume, + .otherend_changed = xenfb_backend_changed, +}; + +static int __init xenfb_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + /* Nothing to do if running in dom0. */ + if (is_initial_xendomain()) + return -ENODEV; + + return xenbus_register_frontend(&xenfb); +} + +static void __exit xenfb_cleanup(void) +{ + xenbus_unregister_driver(&xenfb); +} + +module_init(xenfb_init); +module_exit(xenfb_cleanup); + +MODULE_LICENSE("GPL"); diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig new file mode 100644 index 000000000000..4b75a16de009 --- /dev/null +++ b/drivers/xen/Kconfig @@ -0,0 +1,19 @@ +config XEN_BALLOON + bool "Xen memory balloon driver" + depends on XEN + default y + help + The balloon driver allows the Xen domain to request more memory from + the system to expand the domain's memory allocation, or alternatively + return unneeded memory to the system. + +config XEN_SCRUB_PAGES + bool "Scrub pages before returning them to system" + depends on XEN_BALLOON + default y + help + Scrub pages before returning them to the system for reuse by + other domains. This makes sure that any confidential data + is not accidentally visible to other domains. Is it more + secure, but slightly less efficient. + If in doubt, say yes. diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 56592f0d6cef..37af04f1ffd9 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,2 +1,4 @@ -obj-y += grant-table.o +obj-y += grant-table.o features.o events.o obj-y += xenbus/ +obj-$(CONFIG_XEN_XENCOMM) += xencomm.o +obj-$(CONFIG_XEN_BALLOON) += balloon.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c new file mode 100644 index 000000000000..ab25ba6cbbb9 --- /dev/null +++ b/drivers/xen/balloon.c @@ -0,0 +1,712 @@ +/****************************************************************************** + * balloon.c + * + * Xen balloon driver - enables returning/claiming memory to/from Xen. + * + * Copyright (c) 2003, B Dragovic + * Copyright (c) 2003-2004, M Williamson, K Fraser + * Copyright (c) 2005 Dan M. Smith, IBM Corporation + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/pagemap.h> +#include <linux/highmem.h> +#include <linux/mutex.h> +#include <linux/highmem.h> +#include <linux/list.h> +#include <linux/sysdev.h> + +#include <asm/xen/hypervisor.h> +#include <asm/page.h> +#include <asm/pgalloc.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/tlb.h> + +#include <xen/interface/memory.h> +#include <xen/balloon.h> +#include <xen/xenbus.h> +#include <xen/features.h> +#include <xen/page.h> + +#define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) + +#define BALLOON_CLASS_NAME "memory" + +struct balloon_stats { + /* We aim for 'current allocation' == 'target allocation'. */ + unsigned long current_pages; + unsigned long target_pages; + /* We may hit the hard limit in Xen. If we do then we remember it. */ + unsigned long hard_limit; + /* + * Drivers may alter the memory reservation independently, but they + * must inform the balloon driver so we avoid hitting the hard limit. + */ + unsigned long driver_pages; + /* Number of pages in high- and low-memory balloons. */ + unsigned long balloon_low; + unsigned long balloon_high; +}; + +static DEFINE_MUTEX(balloon_mutex); + +static struct sys_device balloon_sysdev; + +static int register_balloon(struct sys_device *sysdev); + +/* + * Protects atomic reservation decrease/increase against concurrent increases. + * Also protects non-atomic updates of current_pages and driver_pages, and + * balloon lists. + */ +static DEFINE_SPINLOCK(balloon_lock); + +static struct balloon_stats balloon_stats; + +/* We increase/decrease in batches which fit in a page */ +static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)]; + +/* VM /proc information for memory */ +extern unsigned long totalram_pages; + +#ifdef CONFIG_HIGHMEM +extern unsigned long totalhigh_pages; +#define inc_totalhigh_pages() (totalhigh_pages++) +#define dec_totalhigh_pages() (totalhigh_pages--) +#else +#define inc_totalhigh_pages() do {} while(0) +#define dec_totalhigh_pages() do {} while(0) +#endif + +/* List of ballooned pages, threaded through the mem_map array. */ +static LIST_HEAD(ballooned_pages); + +/* Main work function, always executed in process context. */ +static void balloon_process(struct work_struct *work); +static DECLARE_WORK(balloon_worker, balloon_process); +static struct timer_list balloon_timer; + +/* When ballooning out (allocating memory to return to Xen) we don't really + want the kernel to try too hard since that can trigger the oom killer. */ +#define GFP_BALLOON \ + (GFP_HIGHUSER | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC) + +static void scrub_page(struct page *page) +{ +#ifdef CONFIG_XEN_SCRUB_PAGES + if (PageHighMem(page)) { + void *v = kmap(page); + clear_page(v); + kunmap(v); + } else { + void *v = page_address(page); + clear_page(v); + } +#endif +} + +/* balloon_append: add the given page to the balloon. */ +static void balloon_append(struct page *page) +{ + /* Lowmem is re-populated first, so highmem pages go at list tail. */ + if (PageHighMem(page)) { + list_add_tail(&page->lru, &ballooned_pages); + balloon_stats.balloon_high++; + dec_totalhigh_pages(); + } else { + list_add(&page->lru, &ballooned_pages); + balloon_stats.balloon_low++; + } +} + +/* balloon_retrieve: rescue a page from the balloon, if it is not empty. */ +static struct page *balloon_retrieve(void) +{ + struct page *page; + + if (list_empty(&ballooned_pages)) + return NULL; + + page = list_entry(ballooned_pages.next, struct page, lru); + list_del(&page->lru); + + if (PageHighMem(page)) { + balloon_stats.balloon_high--; + inc_totalhigh_pages(); + } + else + balloon_stats.balloon_low--; + + return page; +} + +static struct page *balloon_first_page(void) +{ + if (list_empty(&ballooned_pages)) + return NULL; + return list_entry(ballooned_pages.next, struct page, lru); +} + +static struct page *balloon_next_page(struct page *page) +{ + struct list_head *next = page->lru.next; + if (next == &ballooned_pages) + return NULL; + return list_entry(next, struct page, lru); +} + +static void balloon_alarm(unsigned long unused) +{ + schedule_work(&balloon_worker); +} + +static unsigned long current_target(void) +{ + unsigned long target = min(balloon_stats.target_pages, balloon_stats.hard_limit); + + target = min(target, + balloon_stats.current_pages + + balloon_stats.balloon_low + + balloon_stats.balloon_high); + + return target; +} + +static int increase_reservation(unsigned long nr_pages) +{ + unsigned long pfn, i, flags; + struct page *page; + long rc; + struct xen_memory_reservation reservation = { + .address_bits = 0, + .extent_order = 0, + .domid = DOMID_SELF + }; + + if (nr_pages > ARRAY_SIZE(frame_list)) + nr_pages = ARRAY_SIZE(frame_list); + + spin_lock_irqsave(&balloon_lock, flags); + + page = balloon_first_page(); + for (i = 0; i < nr_pages; i++) { + BUG_ON(page == NULL); + frame_list[i] = page_to_pfn(page);; + page = balloon_next_page(page); + } + + reservation.extent_start = (unsigned long)frame_list; + reservation.nr_extents = nr_pages; + rc = HYPERVISOR_memory_op( + XENMEM_populate_physmap, &reservation); + if (rc < nr_pages) { + if (rc > 0) { + int ret; + + /* We hit the Xen hard limit: reprobe. */ + reservation.nr_extents = rc; + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, + &reservation); + BUG_ON(ret != rc); + } + if (rc >= 0) + balloon_stats.hard_limit = (balloon_stats.current_pages + rc - + balloon_stats.driver_pages); + goto out; + } + + for (i = 0; i < nr_pages; i++) { + page = balloon_retrieve(); + BUG_ON(page == NULL); + + pfn = page_to_pfn(page); + BUG_ON(!xen_feature(XENFEAT_auto_translated_physmap) && + phys_to_machine_mapping_valid(pfn)); + + set_phys_to_machine(pfn, frame_list[i]); + + /* Link back into the page tables if not highmem. */ + if (pfn < max_low_pfn) { + int ret; + ret = HYPERVISOR_update_va_mapping( + (unsigned long)__va(pfn << PAGE_SHIFT), + mfn_pte(frame_list[i], PAGE_KERNEL), + 0); + BUG_ON(ret); + } + + /* Relinquish the page back to the allocator. */ + ClearPageReserved(page); + init_page_count(page); + __free_page(page); + } + + balloon_stats.current_pages += nr_pages; + totalram_pages = balloon_stats.current_pages; + + out: + spin_unlock_irqrestore(&balloon_lock, flags); + + return 0; +} + +static int decrease_reservation(unsigned long nr_pages) +{ + unsigned long pfn, i, flags; + struct page *page; + int need_sleep = 0; + int ret; + struct xen_memory_reservation reservation = { + .address_bits = 0, + .extent_order = 0, + .domid = DOMID_SELF + }; + + if (nr_pages > ARRAY_SIZE(frame_list)) + nr_pages = ARRAY_SIZE(frame_list); + + for (i = 0; i < nr_pages; i++) { + if ((page = alloc_page(GFP_BALLOON)) == NULL) { + nr_pages = i; + need_sleep = 1; + break; + } + + pfn = page_to_pfn(page); + frame_list[i] = pfn_to_mfn(pfn); + + scrub_page(page); + } + + /* Ensure that ballooned highmem pages don't have kmaps. */ + kmap_flush_unused(); + flush_tlb_all(); + + spin_lock_irqsave(&balloon_lock, flags); + + /* No more mappings: invalidate P2M and add to balloon. */ + for (i = 0; i < nr_pages; i++) { + pfn = mfn_to_pfn(frame_list[i]); + set_phys_to_machine(pfn, INVALID_P2M_ENTRY); + balloon_append(pfn_to_page(pfn)); + } + + reservation.extent_start = (unsigned long)frame_list; + reservation.nr_extents = nr_pages; + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); + BUG_ON(ret != nr_pages); + + balloon_stats.current_pages -= nr_pages; + totalram_pages = balloon_stats.current_pages; + + spin_unlock_irqrestore(&balloon_lock, flags); + + return need_sleep; +} + +/* + * We avoid multiple worker processes conflicting via the balloon mutex. + * We may of course race updates of the target counts (which are protected + * by the balloon lock), or with changes to the Xen hard limit, but we will + * recover from these in time. + */ +static void balloon_process(struct work_struct *work) +{ + int need_sleep = 0; + long credit; + + mutex_lock(&balloon_mutex); + + do { + credit = current_target() - balloon_stats.current_pages; + if (credit > 0) + need_sleep = (increase_reservation(credit) != 0); + if (credit < 0) + need_sleep = (decrease_reservation(-credit) != 0); + +#ifndef CONFIG_PREEMPT + if (need_resched()) + schedule(); +#endif + } while ((credit != 0) && !need_sleep); + + /* Schedule more work if there is some still to be done. */ + if (current_target() != balloon_stats.current_pages) + mod_timer(&balloon_timer, jiffies + HZ); + + mutex_unlock(&balloon_mutex); +} + +/* Resets the Xen limit, sets new target, and kicks off processing. */ +void balloon_set_new_target(unsigned long target) +{ + /* No need for lock. Not read-modify-write updates. */ + balloon_stats.hard_limit = ~0UL; + balloon_stats.target_pages = target; + schedule_work(&balloon_worker); +} + +static struct xenbus_watch target_watch = +{ + .node = "memory/target" +}; + +/* React to a change in the target key */ +static void watch_target(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + unsigned long long new_target; + int err; + + err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target); + if (err != 1) { + /* This is ok (for domain0 at least) - so just return */ + return; + } + + /* The given memory/target value is in KiB, so it needs converting to + * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10. + */ + balloon_set_new_target(new_target >> (PAGE_SHIFT - 10)); +} + +static int balloon_init_watcher(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + int err; + + err = register_xenbus_watch(&target_watch); + if (err) + printk(KERN_ERR "Failed to set balloon watcher\n"); + + return NOTIFY_DONE; +} + +static struct notifier_block xenstore_notifier; + +static int __init balloon_init(void) +{ + unsigned long pfn; + struct page *page; + + if (!is_running_on_xen()) + return -ENODEV; + + pr_info("xen_balloon: Initialising balloon driver.\n"); + + balloon_stats.current_pages = min(xen_start_info->nr_pages, max_pfn); + totalram_pages = balloon_stats.current_pages; + balloon_stats.target_pages = balloon_stats.current_pages; + balloon_stats.balloon_low = 0; + balloon_stats.balloon_high = 0; + balloon_stats.driver_pages = 0UL; + balloon_stats.hard_limit = ~0UL; + + init_timer(&balloon_timer); + balloon_timer.data = 0; + balloon_timer.function = balloon_alarm; + + register_balloon(&balloon_sysdev); + + /* Initialise the balloon with excess memory space. */ + for (pfn = xen_start_info->nr_pages; pfn < max_pfn; pfn++) { + page = pfn_to_page(pfn); + if (!PageReserved(page)) + balloon_append(page); + } + + target_watch.callback = watch_target; + xenstore_notifier.notifier_call = balloon_init_watcher; + + register_xenstore_notifier(&xenstore_notifier); + + return 0; +} + +subsys_initcall(balloon_init); + +static void balloon_exit(void) +{ + /* XXX - release balloon here */ + return; +} + +module_exit(balloon_exit); + +static void balloon_update_driver_allowance(long delta) +{ + unsigned long flags; + + spin_lock_irqsave(&balloon_lock, flags); + balloon_stats.driver_pages += delta; + spin_unlock_irqrestore(&balloon_lock, flags); +} + +static int dealloc_pte_fn( + pte_t *pte, struct page *pmd_page, unsigned long addr, void *data) +{ + unsigned long mfn = pte_mfn(*pte); + int ret; + struct xen_memory_reservation reservation = { + .nr_extents = 1, + .extent_order = 0, + .domid = DOMID_SELF + }; + reservation.extent_start = (unsigned long)&mfn; + set_pte_at(&init_mm, addr, pte, __pte_ma(0ull)); + set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY); + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); + BUG_ON(ret != 1); + return 0; +} + +static struct page **alloc_empty_pages_and_pagevec(int nr_pages) +{ + unsigned long vaddr, flags; + struct page *page, **pagevec; + int i, ret; + + pagevec = kmalloc(sizeof(page) * nr_pages, GFP_KERNEL); + if (pagevec == NULL) + return NULL; + + for (i = 0; i < nr_pages; i++) { + page = pagevec[i] = alloc_page(GFP_KERNEL); + if (page == NULL) + goto err; + + vaddr = (unsigned long)page_address(page); + + scrub_page(page); + + spin_lock_irqsave(&balloon_lock, flags); + + if (xen_feature(XENFEAT_auto_translated_physmap)) { + unsigned long gmfn = page_to_pfn(page); + struct xen_memory_reservation reservation = { + .nr_extents = 1, + .extent_order = 0, + .domid = DOMID_SELF + }; + reservation.extent_start = (unsigned long)&gmfn; + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, + &reservation); + if (ret == 1) + ret = 0; /* success */ + } else { + ret = apply_to_page_range(&init_mm, vaddr, PAGE_SIZE, + dealloc_pte_fn, NULL); + } + + if (ret != 0) { + spin_unlock_irqrestore(&balloon_lock, flags); + __free_page(page); + goto err; + } + + totalram_pages = --balloon_stats.current_pages; + + spin_unlock_irqrestore(&balloon_lock, flags); + } + + out: + schedule_work(&balloon_worker); + flush_tlb_all(); + return pagevec; + + err: + spin_lock_irqsave(&balloon_lock, flags); + while (--i >= 0) + balloon_append(pagevec[i]); + spin_unlock_irqrestore(&balloon_lock, flags); + kfree(pagevec); + pagevec = NULL; + goto out; +} + +static void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) +{ + unsigned long flags; + int i; + + if (pagevec == NULL) + return; + + spin_lock_irqsave(&balloon_lock, flags); + for (i = 0; i < nr_pages; i++) { + BUG_ON(page_count(pagevec[i]) != 1); + balloon_append(pagevec[i]); + } + spin_unlock_irqrestore(&balloon_lock, flags); + + kfree(pagevec); + + schedule_work(&balloon_worker); +} + +static void balloon_release_driver_page(struct page *page) +{ + unsigned long flags; + + spin_lock_irqsave(&balloon_lock, flags); + balloon_append(page); + balloon_stats.driver_pages--; + spin_unlock_irqrestore(&balloon_lock, flags); + + schedule_work(&balloon_worker); +} + + +#define BALLOON_SHOW(name, format, args...) \ + static ssize_t show_##name(struct sys_device *dev, \ + char *buf) \ + { \ + return sprintf(buf, format, ##args); \ + } \ + static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) + +BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); +BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low)); +BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high)); +BALLOON_SHOW(hard_limit_kb, + (balloon_stats.hard_limit!=~0UL) ? "%lu\n" : "???\n", + (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0); +BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages)); + +static ssize_t show_target_kb(struct sys_device *dev, char *buf) +{ + return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); +} + +static ssize_t store_target_kb(struct sys_device *dev, + const char *buf, + size_t count) +{ + char memstring[64], *endchar; + unsigned long long target_bytes; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (count <= 1) + return -EBADMSG; /* runt */ + if (count > sizeof(memstring)) + return -EFBIG; /* too long */ + strcpy(memstring, buf); + + target_bytes = memparse(memstring, &endchar); + balloon_set_new_target(target_bytes >> PAGE_SHIFT); + + return count; +} + +static SYSDEV_ATTR(target_kb, S_IRUGO | S_IWUSR, + show_target_kb, store_target_kb); + +static struct sysdev_attribute *balloon_attrs[] = { + &attr_target_kb, +}; + +static struct attribute *balloon_info_attrs[] = { + &attr_current_kb.attr, + &attr_low_kb.attr, + &attr_high_kb.attr, + &attr_hard_limit_kb.attr, + &attr_driver_kb.attr, + NULL +}; + +static struct attribute_group balloon_info_group = { + .name = "info", + .attrs = balloon_info_attrs, +}; + +static struct sysdev_class balloon_sysdev_class = { + .name = BALLOON_CLASS_NAME, +}; + +static int register_balloon(struct sys_device *sysdev) +{ + int i, error; + + error = sysdev_class_register(&balloon_sysdev_class); + if (error) + return error; + + sysdev->id = 0; + sysdev->cls = &balloon_sysdev_class; + + error = sysdev_register(sysdev); + if (error) { + sysdev_class_unregister(&balloon_sysdev_class); + return error; + } + + for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) { + error = sysdev_create_file(sysdev, balloon_attrs[i]); + if (error) + goto fail; + } + + error = sysfs_create_group(&sysdev->kobj, &balloon_info_group); + if (error) + goto fail; + + return 0; + + fail: + while (--i >= 0) + sysdev_remove_file(sysdev, balloon_attrs[i]); + sysdev_unregister(sysdev); + sysdev_class_unregister(&balloon_sysdev_class); + return error; +} + +static void unregister_balloon(struct sys_device *sysdev) +{ + int i; + + sysfs_remove_group(&sysdev->kobj, &balloon_info_group); + for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) + sysdev_remove_file(sysdev, balloon_attrs[i]); + sysdev_unregister(sysdev); + sysdev_class_unregister(&balloon_sysdev_class); +} + +static void balloon_sysfs_exit(void) +{ + unregister_balloon(&balloon_sysdev); +} + +MODULE_LICENSE("GPL"); diff --git a/arch/x86/xen/events.c b/drivers/xen/events.c index dcf613e17581..4f0f22b020ea 100644 --- a/arch/x86/xen/events.c +++ b/drivers/xen/events.c @@ -33,12 +33,11 @@ #include <asm/xen/hypercall.h> #include <asm/xen/hypervisor.h> +#include <xen/xen-ops.h> #include <xen/events.h> #include <xen/interface/xen.h> #include <xen/interface/event_channel.h> -#include "xen-ops.h" - /* * This lock protects updates to the following mapping and reference-count * arrays. The lock does not need to be acquired to read the mapping tables. @@ -455,6 +454,53 @@ void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) notify_remote_via_irq(irq); } +irqreturn_t xen_debug_interrupt(int irq, void *dev_id) +{ + struct shared_info *sh = HYPERVISOR_shared_info; + int cpu = smp_processor_id(); + int i; + unsigned long flags; + static DEFINE_SPINLOCK(debug_lock); + + spin_lock_irqsave(&debug_lock, flags); + + printk("vcpu %d\n ", cpu); + + for_each_online_cpu(i) { + struct vcpu_info *v = per_cpu(xen_vcpu, i); + printk("%d: masked=%d pending=%d event_sel %08lx\n ", i, + (get_irq_regs() && i == cpu) ? xen_irqs_disabled(get_irq_regs()) : v->evtchn_upcall_mask, + v->evtchn_upcall_pending, + v->evtchn_pending_sel); + } + printk("pending:\n "); + for(i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--) + printk("%08lx%s", sh->evtchn_pending[i], + i % 8 == 0 ? "\n " : " "); + printk("\nmasks:\n "); + for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--) + printk("%08lx%s", sh->evtchn_mask[i], + i % 8 == 0 ? "\n " : " "); + + printk("\nunmasked:\n "); + for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--) + printk("%08lx%s", sh->evtchn_pending[i] & ~sh->evtchn_mask[i], + i % 8 == 0 ? "\n " : " "); + + printk("\npending list:\n"); + for(i = 0; i < NR_EVENT_CHANNELS; i++) { + if (sync_test_bit(i, sh->evtchn_pending)) { + printk(" %d: event %d -> irq %d\n", + cpu_evtchn[i], i, + evtchn_to_irq[i]); + } + } + + spin_unlock_irqrestore(&debug_lock, flags); + + return IRQ_HANDLED; +} + /* * Search the CPUs pending events bitmasks. For each one found, map @@ -470,29 +516,44 @@ void xen_evtchn_do_upcall(struct pt_regs *regs) int cpu = get_cpu(); struct shared_info *s = HYPERVISOR_shared_info; struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); - unsigned long pending_words; + static DEFINE_PER_CPU(unsigned, nesting_count); + unsigned count; - vcpu_info->evtchn_upcall_pending = 0; + do { + unsigned long pending_words; - /* NB. No need for a barrier here -- XCHG is a barrier on x86. */ - pending_words = xchg(&vcpu_info->evtchn_pending_sel, 0); - while (pending_words != 0) { - unsigned long pending_bits; - int word_idx = __ffs(pending_words); - pending_words &= ~(1UL << word_idx); + vcpu_info->evtchn_upcall_pending = 0; - while ((pending_bits = active_evtchns(cpu, s, word_idx)) != 0) { - int bit_idx = __ffs(pending_bits); - int port = (word_idx * BITS_PER_LONG) + bit_idx; - int irq = evtchn_to_irq[port]; + if (__get_cpu_var(nesting_count)++) + goto out; - if (irq != -1) { - regs->orig_ax = ~irq; - do_IRQ(regs); +#ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ + /* Clear master flag /before/ clearing selector flag. */ + rmb(); +#endif + pending_words = xchg(&vcpu_info->evtchn_pending_sel, 0); + while (pending_words != 0) { + unsigned long pending_bits; + int word_idx = __ffs(pending_words); + pending_words &= ~(1UL << word_idx); + + while ((pending_bits = active_evtchns(cpu, s, word_idx)) != 0) { + int bit_idx = __ffs(pending_bits); + int port = (word_idx * BITS_PER_LONG) + bit_idx; + int irq = evtchn_to_irq[port]; + + if (irq != -1) + xen_do_IRQ(irq, regs); } } - } + BUG_ON(!irqs_disabled()); + + count = __get_cpu_var(nesting_count); + __get_cpu_var(nesting_count) = 0; + } while(count != 1); + +out: put_cpu(); } @@ -525,6 +586,22 @@ static void set_affinity_irq(unsigned irq, cpumask_t dest) rebind_irq_to_cpu(irq, tcpu); } +int resend_irq_on_evtchn(unsigned int irq) +{ + int masked, evtchn = evtchn_from_irq(irq); + struct shared_info *s = HYPERVISOR_shared_info; + + if (!VALID_EVTCHN(evtchn)) + return 1; + + masked = sync_test_and_set_bit(evtchn, s->evtchn_mask); + sync_set_bit(evtchn, s->evtchn_pending); + if (!masked) + unmask_evtchn(evtchn); + + return 1; +} + static void enable_dynirq(unsigned int irq) { int evtchn = evtchn_from_irq(irq); @@ -554,10 +631,16 @@ static void ack_dynirq(unsigned int irq) static int retrigger_dynirq(unsigned int irq) { int evtchn = evtchn_from_irq(irq); + struct shared_info *sh = HYPERVISOR_shared_info; int ret = 0; if (VALID_EVTCHN(evtchn)) { - set_evtchn(evtchn); + int masked; + + masked = sync_test_and_set_bit(evtchn, sh->evtchn_mask); + sync_set_bit(evtchn, sh->evtchn_pending); + if (!masked) + unmask_evtchn(evtchn); ret = 1; } diff --git a/arch/x86/xen/features.c b/drivers/xen/features.c index 0707714e40d6..0707714e40d6 100644 --- a/arch/x86/xen/features.c +++ b/drivers/xen/features.c diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index d85dc6d41c2a..52b6b41b909d 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -439,24 +439,6 @@ static inline unsigned int max_nr_grant_frames(void) return xen_max; } -static int map_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) -{ - unsigned long **frames = (unsigned long **)data; - - set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); - (*frames)++; - return 0; -} - -static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) -{ - - set_pte_at(&init_mm, addr, pte, __pte(0)); - return 0; -} - static int gnttab_map(unsigned int start_idx, unsigned int end_idx) { struct gnttab_setup_table setup; @@ -470,7 +452,7 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) setup.dom = DOMID_SELF; setup.nr_frames = nr_gframes; - setup.frame_list = frames; + set_xen_guest_handle(setup.frame_list, frames); rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); if (rc == -ENOSYS) { @@ -480,17 +462,9 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) BUG_ON(rc || setup.status); - if (shared == NULL) { - struct vm_struct *area; - area = alloc_vm_area(PAGE_SIZE * max_nr_grant_frames()); - BUG_ON(area == NULL); - shared = area->addr; - } - rc = apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, - map_pte_fn, &frames); + rc = arch_gnttab_map_shared(frames, nr_gframes, max_nr_grant_frames(), + &shared); BUG_ON(rc); - frames -= nr_gframes; /* adjust after map_pte_fn() */ kfree(frames); @@ -506,10 +480,7 @@ static int gnttab_resume(void) static int gnttab_suspend(void) { - apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_grant_frames, - unmap_pte_fn, NULL); - + arch_gnttab_unmap_shared(shared, nr_grant_frames); return 0; } diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 9fd2f70ab46d..0f86b0ff7879 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -399,7 +399,7 @@ int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) *vaddr = NULL; - area = alloc_vm_area(PAGE_SIZE); + area = xen_alloc_vm_area(PAGE_SIZE); if (!area) return -ENOMEM; @@ -409,7 +409,7 @@ int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) BUG(); if (op.status != GNTST_okay) { - free_vm_area(area); + xen_free_vm_area(area); xenbus_dev_fatal(dev, op.status, "mapping in shared page %d from domain %d", gnt_ref, dev->otherend_id); @@ -508,7 +508,7 @@ int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) BUG(); if (op.status == GNTST_okay) - free_vm_area(area); + xen_free_vm_area(area); else xenbus_dev_error(dev, op.status, "unmapping page at handle %d error %d", diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 4750de316ad3..57ceb5346b74 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -88,6 +88,16 @@ int xenbus_match(struct device *_dev, struct device_driver *_drv) return match_device(drv->ids, to_xenbus_device(_dev)) != NULL; } +static int xenbus_uevent(struct device *_dev, struct kobj_uevent_env *env) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + + if (add_uevent_var(env, "MODALIAS=xen:%s", dev->devicetype)) + return -ENOMEM; + + return 0; +} + /* device/<type>/<id> => <type>-<id> */ static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) { @@ -166,6 +176,7 @@ static struct xen_bus_type xenbus_frontend = { .bus = { .name = "xen", .match = xenbus_match, + .uevent = xenbus_uevent, .probe = xenbus_dev_probe, .remove = xenbus_dev_remove, .shutdown = xenbus_dev_shutdown, @@ -438,6 +449,12 @@ static ssize_t xendev_show_devtype(struct device *dev, } DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL); +static ssize_t xendev_show_modalias(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "xen:%s\n", to_xenbus_device(dev)->devicetype); +} +DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL); int xenbus_probe_node(struct xen_bus_type *bus, const char *type, @@ -492,10 +509,16 @@ int xenbus_probe_node(struct xen_bus_type *bus, err = device_create_file(&xendev->dev, &dev_attr_devtype); if (err) - goto fail_remove_file; + goto fail_remove_nodename; + + err = device_create_file(&xendev->dev, &dev_attr_modalias); + if (err) + goto fail_remove_devtype; return 0; -fail_remove_file: +fail_remove_devtype: + device_remove_file(&xendev->dev, &dev_attr_devtype); +fail_remove_nodename: device_remove_file(&xendev->dev, &dev_attr_nodename); fail_unregister: device_unregister(&xendev->dev); @@ -846,6 +869,7 @@ static int is_disconnected_device(struct device *dev, void *data) { struct xenbus_device *xendev = to_xenbus_device(dev); struct device_driver *drv = data; + struct xenbus_driver *xendrv; /* * A device with no driver will never connect. We care only about @@ -858,7 +882,9 @@ static int is_disconnected_device(struct device *dev, void *data) if (drv && (dev->driver != drv)) return 0; - return (xendev->state != XenbusStateConnected); + xendrv = to_xenbus_driver(dev->driver); + return (xendev->state != XenbusStateConnected || + (xendrv->is_ready && !xendrv->is_ready(xendev))); } static int exists_disconnected_device(struct device_driver *drv) diff --git a/drivers/xen/xencomm.c b/drivers/xen/xencomm.c new file mode 100644 index 000000000000..797cb4e31f07 --- /dev/null +++ b/drivers/xen/xencomm.c @@ -0,0 +1,232 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corp. 2006 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + */ + +#include <linux/gfp.h> +#include <linux/mm.h> +#include <asm/page.h> +#include <xen/xencomm.h> +#include <xen/interface/xen.h> +#ifdef __ia64__ +#include <asm/xen/xencomm.h> /* for is_kern_addr() */ +#endif + +#ifdef HAVE_XEN_PLATFORM_COMPAT_H +#include <xen/platform-compat.h> +#endif + +static int xencomm_init(struct xencomm_desc *desc, + void *buffer, unsigned long bytes) +{ + unsigned long recorded = 0; + int i = 0; + + while ((recorded < bytes) && (i < desc->nr_addrs)) { + unsigned long vaddr = (unsigned long)buffer + recorded; + unsigned long paddr; + int offset; + int chunksz; + + offset = vaddr % PAGE_SIZE; /* handle partial pages */ + chunksz = min(PAGE_SIZE - offset, bytes - recorded); + + paddr = xencomm_vtop(vaddr); + if (paddr == ~0UL) { + printk(KERN_DEBUG "%s: couldn't translate vaddr %lx\n", + __func__, vaddr); + return -EINVAL; + } + + desc->address[i++] = paddr; + recorded += chunksz; + } + + if (recorded < bytes) { + printk(KERN_DEBUG + "%s: could only translate %ld of %ld bytes\n", + __func__, recorded, bytes); + return -ENOSPC; + } + + /* mark remaining addresses invalid (just for safety) */ + while (i < desc->nr_addrs) + desc->address[i++] = XENCOMM_INVALID; + + desc->magic = XENCOMM_MAGIC; + + return 0; +} + +static struct xencomm_desc *xencomm_alloc(gfp_t gfp_mask, + void *buffer, unsigned long bytes) +{ + struct xencomm_desc *desc; + unsigned long buffer_ulong = (unsigned long)buffer; + unsigned long start = buffer_ulong & PAGE_MASK; + unsigned long end = (buffer_ulong + bytes) | ~PAGE_MASK; + unsigned long nr_addrs = (end - start + 1) >> PAGE_SHIFT; + unsigned long size = sizeof(*desc) + + sizeof(desc->address[0]) * nr_addrs; + + /* + * slab allocator returns at least sizeof(void*) aligned pointer. + * When sizeof(*desc) > sizeof(void*), struct xencomm_desc might + * cross page boundary. + */ + if (sizeof(*desc) > sizeof(void *)) { + unsigned long order = get_order(size); + desc = (struct xencomm_desc *)__get_free_pages(gfp_mask, + order); + if (desc == NULL) + return NULL; + + desc->nr_addrs = + ((PAGE_SIZE << order) - sizeof(struct xencomm_desc)) / + sizeof(*desc->address); + } else { + desc = kmalloc(size, gfp_mask); + if (desc == NULL) + return NULL; + + desc->nr_addrs = nr_addrs; + } + return desc; +} + +void xencomm_free(struct xencomm_handle *desc) +{ + if (desc && !((ulong)desc & XENCOMM_INLINE_FLAG)) { + struct xencomm_desc *desc__ = (struct xencomm_desc *)desc; + if (sizeof(*desc__) > sizeof(void *)) { + unsigned long size = sizeof(*desc__) + + sizeof(desc__->address[0]) * desc__->nr_addrs; + unsigned long order = get_order(size); + free_pages((unsigned long)__va(desc), order); + } else + kfree(__va(desc)); + } +} + +static int xencomm_create(void *buffer, unsigned long bytes, + struct xencomm_desc **ret, gfp_t gfp_mask) +{ + struct xencomm_desc *desc; + int rc; + + pr_debug("%s: %p[%ld]\n", __func__, buffer, bytes); + + if (bytes == 0) { + /* don't create a descriptor; Xen recognizes NULL. */ + BUG_ON(buffer != NULL); + *ret = NULL; + return 0; + } + + BUG_ON(buffer == NULL); /* 'bytes' is non-zero */ + + desc = xencomm_alloc(gfp_mask, buffer, bytes); + if (!desc) { + printk(KERN_DEBUG "%s failure\n", "xencomm_alloc"); + return -ENOMEM; + } + + rc = xencomm_init(desc, buffer, bytes); + if (rc) { + printk(KERN_DEBUG "%s failure: %d\n", "xencomm_init", rc); + xencomm_free((struct xencomm_handle *)__pa(desc)); + return rc; + } + + *ret = desc; + return 0; +} + +/* check if memory address is within VMALLOC region */ +static int is_phys_contiguous(unsigned long addr) +{ + if (!is_kernel_addr(addr)) + return 0; + + return (addr < VMALLOC_START) || (addr >= VMALLOC_END); +} + +static struct xencomm_handle *xencomm_create_inline(void *ptr) +{ + unsigned long paddr; + + BUG_ON(!is_phys_contiguous((unsigned long)ptr)); + + paddr = (unsigned long)xencomm_pa(ptr); + BUG_ON(paddr & XENCOMM_INLINE_FLAG); + return (struct xencomm_handle *)(paddr | XENCOMM_INLINE_FLAG); +} + +/* "mini" routine, for stack-based communications: */ +static int xencomm_create_mini(void *buffer, + unsigned long bytes, struct xencomm_mini *xc_desc, + struct xencomm_desc **ret) +{ + int rc = 0; + struct xencomm_desc *desc; + BUG_ON(((unsigned long)xc_desc) % sizeof(*xc_desc) != 0); + + desc = (void *)xc_desc; + + desc->nr_addrs = XENCOMM_MINI_ADDRS; + + rc = xencomm_init(desc, buffer, bytes); + if (!rc) + *ret = desc; + + return rc; +} + +struct xencomm_handle *xencomm_map(void *ptr, unsigned long bytes) +{ + int rc; + struct xencomm_desc *desc; + + if (is_phys_contiguous((unsigned long)ptr)) + return xencomm_create_inline(ptr); + + rc = xencomm_create(ptr, bytes, &desc, GFP_KERNEL); + + if (rc || desc == NULL) + return NULL; + + return xencomm_pa(desc); +} + +struct xencomm_handle *__xencomm_map_no_alloc(void *ptr, unsigned long bytes, + struct xencomm_mini *xc_desc) +{ + int rc; + struct xencomm_desc *desc = NULL; + + if (is_phys_contiguous((unsigned long)ptr)) + return xencomm_create_inline(ptr); + + rc = xencomm_create_mini(ptr, bytes, xc_desc, + &desc); + + if (rc) + return NULL; + + return xencomm_pa(desc); +} diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index d14d5a4dc5ac..3ea36554107f 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -14,7 +14,7 @@ be fairly close. alloc_sem --------- -The alloc_sem is a per-filesystem semaphore, used primarily to ensure +The alloc_sem is a per-filesystem mutex, used primarily to ensure contiguous allocation of space on the medium. It is automatically obtained during space allocations (jffs2_reserve_space()) and freed upon write completion (jffs2_complete_reservation()). Note that @@ -41,10 +41,10 @@ if the wbuf is currently holding any data is permitted, though. Ordering constraints: See f->sem. - File Semaphore f->sem + File Mutex f->sem --------------------- -This is the JFFS2-internal equivalent of the inode semaphore i->i_sem. +This is the JFFS2-internal equivalent of the inode mutex i->i_sem. It protects the contents of the jffs2_inode_info private inode data, including the linked list of node fragments (but see the notes below on erase_completion_lock), etc. @@ -60,14 +60,14 @@ lead to deadlock, unless we played games with unlocking the i_sem before calling the space allocation functions. Instead of playing such games, we just have an extra internal -semaphore, which is obtained by the garbage collection code and also +mutex, which is obtained by the garbage collection code and also by the normal file system code _after_ allocation of space. Ordering constraints: 1. Never attempt to allocate space or lock alloc_sem with any f->sem held. - 2. Never attempt to lock two file semaphores in one thread. + 2. Never attempt to lock two file mutexes in one thread. No ordering rules have been made for doing so. @@ -86,8 +86,8 @@ a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in -the list are protected by the file semaphore f->sem. But the erase -code may remove _obsolete_ nodes from the list while holding only the +the list are protected by the file mutex f->sem. But the erase code +may remove _obsolete_ nodes from the list while holding only the erase_completion_lock. So you can walk the list only while holding the erase_completion_lock, and can drop the lock temporarily mid-walk as long as the pointer you're holding is to a _valid_ node, not an @@ -124,10 +124,10 @@ Ordering constraints: erase_free_sem -------------- -This semaphore is only used by the erase code which frees obsolete -node references and the jffs2_garbage_collect_deletion_dirent() -function. The latter function on NAND flash must read _obsolete_ nodes -to determine whether the 'deletion dirent' under consideration can be +This mutex is only used by the erase code which frees obsolete node +references and the jffs2_garbage_collect_deletion_dirent() function. +The latter function on NAND flash must read _obsolete_ nodes to +determine whether the 'deletion dirent' under consideration can be discarded or whether it is still required to show that an inode has been unlinked. Because reading from the flash may sleep, the erase_completion_lock cannot be held, so an alternative, more diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 722a6b682951..d58f845ccb85 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -345,6 +345,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) INIT_LIST_HEAD(&c->dirty_list); INIT_LIST_HEAD(&c->erasable_list); INIT_LIST_HEAD(&c->erasing_list); + INIT_LIST_HEAD(&c->erase_checking_list); INIT_LIST_HEAD(&c->erase_pending_list); INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); INIT_LIST_HEAD(&c->erase_complete_list); diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 3a32c64ed497..5544d31c066b 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -62,9 +62,9 @@ __jffs2_dbg_acct_sanity_check(struct jffs2_sb_info *c, void __jffs2_dbg_fragtree_paranoia_check(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); __jffs2_dbg_fragtree_paranoia_check_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void @@ -153,6 +153,139 @@ __jffs2_dbg_prewrite_paranoia_check(struct jffs2_sb_info *c, kfree(buf); } +void __jffs2_dbg_superblock_counts(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb; + uint32_t free = 0, dirty = 0, used = 0, wasted = 0, + erasing = 0, bad = 0, unchecked = 0; + int nr_counted = 0; + int dump = 0; + + if (c->gcblock) { + nr_counted++; + free += c->gcblock->free_size; + dirty += c->gcblock->dirty_size; + used += c->gcblock->used_size; + wasted += c->gcblock->wasted_size; + unchecked += c->gcblock->unchecked_size; + } + if (c->nextblock) { + nr_counted++; + free += c->nextblock->free_size; + dirty += c->nextblock->dirty_size; + used += c->nextblock->used_size; + wasted += c->nextblock->wasted_size; + unchecked += c->nextblock->unchecked_size; + } + list_for_each_entry(jeb, &c->clean_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->very_dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->dirty_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erasable_pending_wbuf_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->erase_pending_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->free_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + list_for_each_entry(jeb, &c->bad_used_list, list) { + nr_counted++; + free += jeb->free_size; + dirty += jeb->dirty_size; + used += jeb->used_size; + wasted += jeb->wasted_size; + unchecked += jeb->unchecked_size; + } + + list_for_each_entry(jeb, &c->erasing_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_checking_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->erase_complete_list, list) { + nr_counted++; + erasing += c->sector_size; + } + list_for_each_entry(jeb, &c->bad_list, list) { + nr_counted++; + bad += c->sector_size; + } + +#define check(sz) \ + if (sz != c->sz##_size) { \ + printk(KERN_WARNING #sz "_size mismatch counted 0x%x, c->" #sz "_size 0x%x\n", \ + sz, c->sz##_size); \ + dump = 1; \ + } + check(free); + check(dirty); + check(used); + check(wasted); + check(unchecked); + check(bad); + check(erasing); +#undef check + + if (nr_counted != c->nr_blocks) { + printk(KERN_WARNING "%s counted only 0x%x blocks of 0x%x. Where are the others?\n", + __func__, nr_counted, c->nr_blocks); + dump = 1; + } + + if (dump) { + __jffs2_dbg_dump_block_lists_nolock(c); + BUG(); + } +} + /* * Check the space accounting and node_ref list correctness for the JFFS2 erasable block 'jeb'. */ @@ -229,6 +362,9 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c, } #endif + if (!(c->flags & (JFFS2_SB_FLAG_BUILDING|JFFS2_SB_FLAG_SCANNING))) + __jffs2_dbg_superblock_counts(c); + return; error: @@ -268,7 +404,10 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c, printk(JFFS2_DBG); for (ref = jeb->first_node; ; ref = ref_next(ref)) { - printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); + printk("%#08x", ref_offset(ref)); +#ifdef TEST_TOTLEN + printk("(%x)", ref->__totlen); +#endif if (ref_next(ref)) printk("->"); else @@ -447,6 +586,21 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) } } } + if (list_empty(&c->erase_checking_list)) { + printk(JFFS2_DBG "erase_checking_list: empty\n"); + } else { + struct list_head *this; + + list_for_each(this, &c->erase_checking_list) { + struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); + + if (!(jeb->used_size == 0 && jeb->dirty_size == 0 && jeb->wasted_size == 0)) { + printk(JFFS2_DBG "erase_checking_list: %#08x (used %#08x, dirty %#08x, wasted %#08x, unchecked %#08x, free %#08x)\n", + jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, + jeb->unchecked_size, jeb->free_size); + } + } + } if (list_empty(&c->erase_pending_list)) { printk(JFFS2_DBG "erase_pending_list: empty\n"); @@ -532,9 +686,9 @@ __jffs2_dbg_dump_block_lists_nolock(struct jffs2_sb_info *c) void __jffs2_dbg_dump_fragtree(struct jffs2_inode_info *f) { - down(&f->sem); + mutex_lock(&f->sem); jffs2_dbg_dump_fragtree_nolock(f); - up(&f->sem); + mutex_unlock(&f->sem); } void diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 4130adabd76e..9645275023e6 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -38,6 +38,7 @@ #if CONFIG_JFFS2_FS_DEBUG > 1 #define JFFS2_DBG_FRAGTREE2_MESSAGES +#define JFFS2_DBG_READINODE2_MESSAGES #define JFFS2_DBG_MEMALLOC_MESSAGES #endif @@ -115,6 +116,11 @@ #else #define dbg_readinode(fmt, ...) #endif +#ifdef JFFS2_DBG_READINODE2_MESSAGES +#define dbg_readinode2(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_readinode2(fmt, ...) +#endif /* Fragtree build debugging messages */ #ifdef JFFS2_DBG_FRAGTREE_MESSAGES diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index f948f7e6ec82..c63e7a96af0d 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -86,7 +86,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { @@ -99,7 +99,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, } if (fd) ino = fd->ino; - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); if (ino) { inode = jffs2_iget(dir_i->i_sb, ino); if (IS_ERR(inode)) { @@ -146,7 +146,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) } curofs=1; - down(&f->sem); + mutex_lock(&f->sem); for (fd = f->dents; fd; fd = fd->next) { curofs++; @@ -166,7 +166,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) break; offset++; } - up(&f->sem); + mutex_unlock(&f->sem); out: filp->f_pos = offset; return 0; @@ -275,9 +275,9 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len, now); if (!ret) { - down(&f->sem); + mutex_lock(&f->sem); old_dentry->d_inode->i_nlink = ++f->inocache->nlink; - up(&f->sem); + mutex_unlock(&f->sem); d_instantiate(dentry, old_dentry->d_inode); dir_i->i_mtime = dir_i->i_ctime = ITIME(now); atomic_inc(&old_dentry->d_inode->i_count); @@ -351,7 +351,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -361,7 +361,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char f->target = kmalloc(targetlen + 1, GFP_KERNEL); if (!f->target) { printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return -ENOMEM; @@ -374,7 +374,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -406,7 +406,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -429,7 +429,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -442,7 +442,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -507,7 +507,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -516,7 +516,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -548,7 +548,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -571,7 +571,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -585,7 +585,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -673,7 +673,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); return PTR_ERR(fn); @@ -682,7 +682,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de obsoleted by the first data write */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); @@ -714,7 +714,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de } dir_f = JFFS2_INODE_INFO(dir_i); - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -740,7 +740,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_clear_inode(inode); return PTR_ERR(fd); } @@ -753,7 +753,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); @@ -780,14 +780,14 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (S_ISDIR(new_dentry->d_inode->i_mode)) { struct jffs2_full_dirent *fd; - down(&victim_f->sem); + mutex_lock(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); return -ENOTEMPTY; } } - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -816,9 +816,9 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { - down(&victim_f->sem); + mutex_lock(&victim_f->sem); victim_f->inocache->nlink--; - up(&victim_f->sem); + mutex_unlock(&victim_f->sem); } } @@ -836,11 +836,11 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); - down(&f->sem); + mutex_lock(&f->sem); inc_nlink(old_dentry->d_inode); if (f->inocache) f->inocache->nlink++; - up(&f->sem); + mutex_unlock(&f->sem); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); /* Might as well let the VFS know */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index a1db9180633f..25a640e566d3 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -50,14 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -84,14 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, if (ret == -ENOMEM || ret == -EAGAIN) { /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } @@ -107,7 +107,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); @@ -116,9 +116,9 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) if (!list_empty(&c->erase_complete_list)) { jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); - list_del(&jeb->list); + list_move(&jeb->list, &c->erase_checking_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); if (!--count) { @@ -139,7 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_block(c, jeb); @@ -149,12 +149,12 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) /* Be nice */ yield(); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); } spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); } @@ -162,11 +162,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); } @@ -180,26 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock failed too many times. */ if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } } - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_move(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); } @@ -350,9 +350,11 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl break; } while(--retlen); c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); - if (retlen) + if (retlen) { printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n", *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf)); + return -EIO; + } return 0; } do_flash_read: @@ -373,10 +375,12 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + ret = -EIO; goto fail; } if (retlen != readlen) { printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + ret = -EIO; goto fail; } for (i=0; i<readlen; i += sizeof(unsigned long)) { @@ -385,6 +389,7 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl if (*datum + 1) { *bad_offset += i; printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", *datum, *bad_offset); + ret = -EIO; goto fail; } } @@ -419,9 +424,6 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb if (jffs2_write_nand_cleanmarker(c, jeb)) goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; } else { struct kvec vecs[1]; @@ -449,48 +451,50 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb goto filebad; } - - /* Everything else got zeroed before the erase */ - jeb->free_size = c->sector_size; - /* FIXME Special case for cleanmarker in empty block */ - jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } + /* Everything else got zeroed before the erase */ + jeb->free_size = c->sector_size; - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; - c->free_size += jeb->free_size; - c->used_size += jeb->used_size; + c->free_size += c->sector_size; - jffs2_dbg_acct_sanity_check_nolock(c,jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + /* Account for cleanmarker now, if it's in-band */ + if (c->cleanmarker_size && !jffs2_cleanmarker_oob(c)) + jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); - list_add_tail(&jeb->list, &c->free_list); + list_move_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; + + jffs2_dbg_acct_sanity_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); wake_up(&c->erase_wait); return; filebad: - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); /* Stick it on a list (any list) so erase_failed can take it right off again. Silly, but shouldn't happen often. */ - list_add(&jeb->list, &c->erasing_list); + list_move(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); jffs2_erase_failed(c, jeb, bad_offset); return; refile: /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); - list_add(&jeb->list, &c->erase_complete_list); + list_move(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); return; } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index dcc2734e0b5d..5e920343b2c5 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -115,9 +115,9 @@ static int jffs2_readpage (struct file *filp, struct page *pg) struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host); int ret; - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_unlock(pg->mapping->host, pg); - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -154,7 +154,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (ret) goto out_page; - down(&f->sem); + mutex_lock(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -181,7 +181,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); @@ -195,12 +195,12 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); - up(&f->sem); + mutex_unlock(&f->sem); goto out_page; } jffs2_complete_reservation(c); inode->i_size = pageofs; - up(&f->sem); + mutex_unlock(&f->sem); } /* @@ -209,9 +209,9 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, * case of a short-copy. */ if (!PageUptodate(pg)) { - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); - up(&f->sem); + mutex_unlock(&f->sem); if (ret) goto out_page; } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index e26ea78c7892..3eb1c84b0a33 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -36,6 +36,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) unsigned int ivalid; uint32_t alloclen; int ret; + int alloc_type = ALLOC_NORMAL; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); @@ -50,20 +51,20 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mdata = (char *)&dev; D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { - down(&f->sem); + mutex_lock(&f->sem); mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) { - up(&f->sem); + mutex_unlock(&f->sem); return -ENOMEM; } ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); kfree(mdata); return ret; } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } @@ -82,7 +83,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) kfree(mdata); return ret; } - down(&f->sem); + mutex_lock(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -115,6 +116,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); + } else if (ivalid & ATTR_SIZE && !iattr->ia_size) { + /* For truncate-to-zero, treat it as deletion because + it'll always be obsoleting all previous nodes */ + alloc_type = ALLOC_DELETION; } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) @@ -122,14 +127,14 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) else ri->data_crc = cpu_to_je32(0); - new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL); + new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, alloc_type); if (S_ISLNK(inode->i_mode)) kfree(mdata); if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ @@ -149,6 +154,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); inode->i_size = iattr->ia_size; + inode->i_blocks = (inode->i_size + 511) >> 9; f->metadata = NULL; } else { f->metadata = new_metadata; @@ -159,7 +165,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) } jffs2_free_raw_inode(ri); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); /* We have to do the vmtruncate() without f->sem held, since @@ -167,8 +173,10 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ - if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) - vmtruncate(inode, iattr->ia_size); + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { + vmtruncate(inode, iattr->ia_size); + inode->i_blocks = (inode->i_size + 511) >> 9; + } return 0; } @@ -248,12 +256,12 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) c = JFFS2_SB_INFO(inode->i_sb); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); iget_failed(inode); return ERR_PTR(ret); } @@ -330,7 +338,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); } - up(&f->sem); + mutex_unlock(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); unlock_new_inode(inode); @@ -339,7 +347,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) error_io: ret = -EIO; error: - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); iget_failed(inode); return ERR_PTR(ret); @@ -380,9 +388,9 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) Flush the writebuffer, if neccecary, else we loose it */ if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } if (!(*flags & MS_RDONLY)) @@ -429,7 +437,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); - down(&f->sem); + mutex_lock(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 32ff0373aa04..bad005664e30 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -126,7 +126,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) int ret = 0, inum, nlink; int xattr = 0; - if (down_interruptible(&c->alloc_sem)) + if (mutex_lock_interruptible(&c->alloc_sem)) return -EINTR; for (;;) { @@ -143,7 +143,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -190,7 +190,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) made no progress in this case, but that should be OK */ c->checked_ino--; - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; @@ -210,7 +210,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -221,9 +221,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb = jffs2_find_gc_block(c); if (!jeb) { - D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); + /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */ + if (!list_empty(&c->erase_pending_list)) { + spin_unlock(&c->erase_completion_lock); + mutex_unlock(&c->alloc_sem); + return -EAGAIN; + } + D1(printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -EIO; } @@ -232,7 +238,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); if (!jeb->used_size) { - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit; } @@ -248,7 +254,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); BUG(); } } @@ -266,7 +272,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) /* Just mark it obsolete */ jffs2_mark_node_obsolete(c, raw); } - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); goto eraseit_lock; } @@ -334,7 +340,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) */ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ic->ino, ic->state); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); spin_unlock(&c->inocache_lock); BUG(); @@ -345,7 +351,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) the alloc_sem() (for marking nodes invalid) so we must drop the alloc_sem before sleeping. */ - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); @@ -416,7 +422,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ret = -ENOSPC; } release_sem: - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); eraseit_lock: /* If we've finished this block, start it erasing */ @@ -445,7 +451,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era uint32_t start = 0, end = 0, nrfrags = 0; int ret = 0; - down(&f->sem); + mutex_lock(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ @@ -525,7 +531,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } } upnout: - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -846,7 +852,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but can't see any alternative. Suggestions on a postcard to... */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { @@ -899,7 +905,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); @@ -908,7 +914,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct return jffs2_garbage_collect_dirent(c, jeb, f, fd); } - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); kfree(rd); } @@ -1081,7 +1087,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras return 0; } -static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end) { diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index f4d525b0ea53..e2177210f621 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -10,6 +10,7 @@ */ #include <linux/fs.h> +#include "nodelist.h" int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index a841f4973a74..31559f45fdde 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -15,7 +15,7 @@ #include <linux/version.h> #include <linux/rbtree.h> #include <linux/posix_acl.h> -#include <linux/semaphore.h> +#include <linux/mutex.h> struct jffs2_inode_info { /* We need an internal mutex similar to inode->i_mutex. @@ -24,7 +24,7 @@ struct jffs2_inode_info { before letting GC proceed. Or we'd have to put ugliness into the GC code so it didn't attempt to obtain the i_mutex for the inode(s) which are already locked */ - struct semaphore sem; + struct mutex sem; /* The highest (datanode) version number used for this ino */ uint32_t highest_version; diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 18fca2b9e531..85ef6dbb1be7 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -16,7 +16,7 @@ #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/completion.h> -#include <linux/semaphore.h> +#include <linux/mutex.h> #include <linux/timer.h> #include <linux/wait.h> #include <linux/list.h> @@ -44,7 +44,7 @@ struct jffs2_sb_info { struct completion gc_thread_start; /* GC thread start completion */ struct completion gc_thread_exit; /* GC thread exit completion port */ - struct semaphore alloc_sem; /* Used to protect all the following + struct mutex alloc_sem; /* Used to protect all the following fields, and also to protect against out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER @@ -87,6 +87,7 @@ struct jffs2_sb_info { struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_checking_list; /* Blocks which are being checked and marked */ struct list_head erase_pending_list; /* Blocks which need erasing now */ struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ struct list_head free_list; /* Blocks which are free and ready to be used */ @@ -104,7 +105,7 @@ struct jffs2_sb_info { /* Sem to allow jffs2_garbage_collect_deletion_dirent to drop the erase_completion_lock while it's holding a pointer to an obsoleted node. I don't like this. Alternatives welcomed. */ - struct semaphore erase_free_sem; + struct mutex erase_free_sem; uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index ec1aae9e695e..8219df6eb6d8 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -87,7 +87,7 @@ struct jffs2_raw_node_ref xattr_ref or xattr_datum instead. The common part of those structures has NULL in the first word. See jffs2_raw_ref_to_ic() below */ uint32_t flash_offset; -#define TEST_TOTLEN +#undef TEST_TOTLEN #ifdef TEST_TOTLEN uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ #endif diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index a0313fa8748e..9df8f3ef20df 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -48,7 +48,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); @@ -57,7 +57,6 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { - int ret; uint32_t dirty, avail; /* calculate real dirty size @@ -82,7 +81,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } @@ -105,11 +104,11 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return -ENOSPC; } - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, @@ -117,7 +116,10 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); - if (ret) + + if (ret == -EAGAIN) + jffs2_erase_pending_blocks(c, 1); + else if (ret) return ret; cond_resched(); @@ -125,7 +127,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (signal_pending(current)) return -EINTR; - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } @@ -138,7 +140,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); if (ret) - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -463,7 +465,7 @@ void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); jffs2_garbage_collect_trigger(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); } static inline int on_list(struct list_head *obj, struct list_head *head) @@ -512,7 +514,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref any jffs2_raw_node_refs. So we don't need to stop erases from happening, or protect against people holding an obsolete jffs2_raw_node_ref without the erase_completion_lock. */ - down(&c->erase_free_sem); + mutex_lock(&c->erase_free_sem); } spin_lock(&c->erase_completion_lock); @@ -715,7 +717,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } out_erase_sem: - up(&c->erase_free_sem); + mutex_unlock(&c->erase_free_sem); } int jffs2_thread_should_wake(struct jffs2_sb_info *c) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index e512a93d6249..4cb4d76de07f 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -825,8 +825,9 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref else // normal case... tn->fn->size = je32_to_cpu(rd->dsize); - dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", - ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); + dbg_readinode2("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n", + ref_offset(ref), je32_to_cpu(rd->version), + je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize); ret = jffs2_add_tn_to_tree(c, rii, tn); @@ -836,13 +837,13 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref jffs2_free_tmp_dnode_info(tn); return ret; } -#ifdef JFFS2_DBG_READINODE_MESSAGES - dbg_readinode("After adding ver %d:\n", je32_to_cpu(rd->version)); +#ifdef JFFS2_DBG_READINODE2_MESSAGES + dbg_readinode2("After adding ver %d:\n", je32_to_cpu(rd->version)); tn = tn_first(&rii->tn_root); while (tn) { - dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n", - tn, tn->version, tn->fn->ofs, - tn->fn->ofs+tn->fn->size, tn->overlapped); + dbg_readinode2("%p: v %d r 0x%x-0x%x ov %d\n", + tn, tn->version, tn->fn->ofs, + tn->fn->ofs+tn->fn->size, tn->overlapped); tn = tn_next(tn); } #endif @@ -1193,7 +1194,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } @@ -1202,7 +1203,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(rii.latest_ref)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1242,7 +1243,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } @@ -1255,7 +1256,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ret = -EIO; kfree(f->target); f->target = NULL; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } @@ -1273,14 +1274,14 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1289,7 +1290,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } @@ -1379,12 +1380,13 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i if (!f) return -ENOMEM; - init_MUTEX_LOCKED(&f->sem); + mutex_init(&f->sem); + mutex_lock(&f->sem); f->inocache = ic; ret = jffs2_do_read_inode_internal(c, f, &n); if (!ret) { - up(&f->sem); + mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); } kfree (f); @@ -1398,7 +1400,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_clear_acl(f); jffs2_xattr_delete_inode(c, f->inocache); - down(&f->sem); + mutex_lock(&f->sem); deleted = f->inocache && !f->inocache->nlink; if (f->inocache && f->inocache->state != INO_STATE_CHECKING) @@ -1430,5 +1432,5 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_del_ino_cache(c, f->inocache); } - up(&f->sem); + mutex_unlock(&f->sem); } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 4677355996cc..f3353df178e7 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -47,7 +47,7 @@ static void jffs2_i_init_once(struct kmem_cache *cachep, void *foo) { struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; - init_MUTEX(&ei->sem); + mutex_init(&ei->sem); inode_init_once(&ei->vfs_inode); } @@ -55,9 +55,9 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return 0; } @@ -95,8 +95,8 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) /* Initialize JFFS2 superblock locks, the further initialization will * be done later */ - init_MUTEX(&c->alloc_sem); - init_MUTEX(&c->erase_free_sem); + mutex_init(&c->alloc_sem); + mutex_init(&c->erase_free_sem); init_waitqueue_head(&c->erase_wait); init_waitqueue_head(&c->inocache_wq); spin_lock_init(&c->erase_completion_lock); @@ -125,9 +125,9 @@ static void jffs2_put_super (struct super_block *sb) D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); jffs2_sum_exit(c); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index d1d4f27464ba..8de52b607678 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -578,8 +578,8 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) if (!jffs2_is_writebuffered(c)) return 0; - if (!down_trylock(&c->alloc_sem)) { - up(&c->alloc_sem); + if (mutex_trylock(&c->alloc_sem)) { + mutex_unlock(&c->alloc_sem); printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); BUG(); } @@ -702,10 +702,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) if (!c->wbuf) return 0; - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); if (!jffs2_wbuf_pending_for_ino(c, ino)) { D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return 0; } @@ -725,14 +725,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ret = jffs2_garbage_collect_pass(c); if (ret) { /* GC failed. Flush it with padding instead */ - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); down_write(&c->wbuf_sem); ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); /* retry flushing wbuf in case jffs2_wbuf_recover @@ -742,12 +742,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) up_write(&c->wbuf_sem); break; } - down(&c->alloc_sem); + mutex_lock(&c->alloc_sem); } D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); - up(&c->alloc_sem); + mutex_unlock(&c->alloc_sem); return ret; } @@ -1236,12 +1236,24 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) { if (!c->wbuf) return -ENOMEM; +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf_verify) { + kfree(c->oobbuf); + kfree(c->wbuf); + return -ENOMEM; + } +#endif + printk(KERN_INFO "JFFS2 write-buffering enabled buffer (%d) erasesize (%d)\n", c->wbuf_pagesize, c->sector_size); return 0; } void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) { +#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY + kfree(c->wbuf_verify); +#endif kfree(c->wbuf); } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 776f13cbf2b5..665fce9797d3 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -137,12 +137,12 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -285,12 +285,12 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - down(&f->sem); + mutex_lock(&f->sem); } if (!ret) { @@ -353,7 +353,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); break; } - down(&f->sem); + mutex_lock(&f->sem); datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); @@ -381,7 +381,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, if (IS_ERR(fn)) { ret = PTR_ERR(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!retried) { /* Write error to be retried */ @@ -403,11 +403,11 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); break; } - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); if (!datalen) { printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); @@ -439,7 +439,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { - up(&f->sem); + mutex_unlock(&f->sem); return ret; } @@ -454,7 +454,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); /* Eeek. Wave bye bye */ - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); return PTR_ERR(fn); } @@ -463,7 +463,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str */ f->metadata = fn; - up(&f->sem); + mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode); @@ -489,7 +489,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str return -ENOMEM; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); @@ -513,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -522,7 +522,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } @@ -551,7 +551,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -574,21 +574,21 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } else { - struct jffs2_full_dirent *fd = dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); + fd = dir_f->dents; /* We don't actually want to reserve any space, but we do want to be holding the alloc_sem when we write to flash */ - down(&c->alloc_sem); - down(&dir_f->sem); + mutex_lock(&c->alloc_sem); + mutex_lock(&dir_f->sem); for (fd = dir_f->dents; fd; fd = fd->next) { if (fd->nhash == nhash && @@ -607,7 +607,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, break; } } - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); } /* dead_f is NULL if this was a rename not a real unlink */ @@ -615,7 +615,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, pointing to an inode which didn't exist. */ if (dead_f && dead_f->inocache) { - down(&dead_f->sem); + mutex_lock(&dead_f->sem); if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) { while (dead_f->dents) { @@ -639,7 +639,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ - up(&dead_f->sem); + mutex_unlock(&dead_f->sem); } jffs2_complete_reservation(c); @@ -666,7 +666,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint return ret; } - down(&dir_f->sem); + mutex_lock(&dir_f->sem); /* Build a deletion node */ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -691,7 +691,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint if (IS_ERR(fd)) { jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return PTR_ERR(fd); } @@ -699,7 +699,7 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint jffs2_add_fd_to_list(c, fd, &dir_f->dents); jffs2_complete_reservation(c); - up(&dir_f->sem); + mutex_unlock(&dir_f->sem); return 0; } diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 1f122c1940af..4d81553d2948 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -632,7 +632,7 @@ nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf, block->b_flags |= B_TIMED_OUT; if (conf) { if (block->b_fl) - locks_copy_lock(block->b_fl, conf); + __locks_copy_lock(block->b_fl, conf); } } diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index dbbefbcd6712..d1c48b539df8 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -18,6 +18,8 @@ #include <linux/lockd/lockd.h> #include <linux/lockd/share.h> #include <linux/lockd/sm_inter.h> +#include <linux/module.h> +#include <linux/mount.h> #define NLMDBG_FACILITY NLMDBG_SVCSUBS @@ -194,6 +196,12 @@ again: return 0; } +static int +nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2) +{ + return 1; +} + /* * Inspect a single file */ @@ -230,7 +238,8 @@ nlm_file_inuse(struct nlm_file *file) * Loop over all files in the file table. */ static int -nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) +nlm_traverse_files(void *data, nlm_host_match_fn_t match, + int (*is_failover_file)(void *data, struct nlm_file *file)) { struct hlist_node *pos, *next; struct nlm_file *file; @@ -239,12 +248,14 @@ nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) { + if (is_failover_file && !is_failover_file(data, file)) + continue; file->f_count++; mutex_unlock(&nlm_file_mutex); /* Traverse locks, blocks and shares of this file * and update file->f_locks count */ - if (nlm_inspect_file(host, file, match)) + if (nlm_inspect_file(data, file, match)) ret = 1; mutex_lock(&nlm_file_mutex); @@ -303,21 +314,27 @@ nlm_release_file(struct nlm_file *file) * Used by nlmsvc_invalidate_all */ static int -nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy) +nlmsvc_mark_host(void *data, struct nlm_host *dummy) { + struct nlm_host *host = data; + host->h_inuse = 1; return 0; } static int -nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other) +nlmsvc_same_host(void *data, struct nlm_host *other) { + struct nlm_host *host = data; + return host == other; } static int -nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy) +nlmsvc_is_client(void *data, struct nlm_host *dummy) { + struct nlm_host *host = data; + if (host->h_server) { /* we are destroying locks even though the client * hasn't asked us too, so don't unmonitor the @@ -337,7 +354,7 @@ void nlmsvc_mark_resources(void) { dprintk("lockd: nlmsvc_mark_resources\n"); - nlm_traverse_files(NULL, nlmsvc_mark_host); + nlm_traverse_files(NULL, nlmsvc_mark_host, NULL); } /* @@ -348,7 +365,7 @@ nlmsvc_free_host_resources(struct nlm_host *host) { dprintk("lockd: nlmsvc_free_host_resources\n"); - if (nlm_traverse_files(host, nlmsvc_same_host)) { + if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) { printk(KERN_WARNING "lockd: couldn't remove all locks held by %s\n", host->h_name); @@ -368,5 +385,41 @@ nlmsvc_invalidate_all(void) * turn, which is about as inefficient as it gets. * Now we just do it once in nlm_traverse_files. */ - nlm_traverse_files(NULL, nlmsvc_is_client); + nlm_traverse_files(NULL, nlmsvc_is_client, NULL); +} + +static int +nlmsvc_match_sb(void *datap, struct nlm_file *file) +{ + struct super_block *sb = datap; + + return sb == file->f_file->f_path.mnt->mnt_sb; +} + +int +nlmsvc_unlock_all_by_sb(struct super_block *sb) +{ + int ret; + + ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb); + return ret ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); + +static int +nlmsvc_match_ip(void *datap, struct nlm_host *host) +{ + __be32 *server_addr = datap; + + return host->h_saddr.sin_addr.s_addr == *server_addr; +} + +int +nlmsvc_unlock_all_by_ip(__be32 server_addr) +{ + int ret; + ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip, NULL); + return ret ? -EIO : 0; + } +EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip); diff --git a/fs/locks.c b/fs/locks.c index 592faadbcec1..e1ea2fe03681 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -224,7 +224,7 @@ static void locks_copy_private(struct file_lock *new, struct file_lock *fl) /* * Initialize a new lock from an existing file_lock structure. */ -static void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl) +void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl) { new->fl_owner = fl->fl_owner; new->fl_pid = fl->fl_pid; @@ -833,7 +833,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str if (!posix_locks_conflict(request, fl)) continue; if (conflock) - locks_copy_lock(conflock, fl); + __locks_copy_lock(conflock, fl); error = -EAGAIN; if (!(request->fl_flags & FL_SLEEP)) goto out; @@ -1367,18 +1367,20 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) lease = *flp; - error = -EAGAIN; - if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) - goto out; - if ((arg == F_WRLCK) - && ((atomic_read(&dentry->d_count) > 1) - || (atomic_read(&inode->i_count) > 1))) - goto out; + if (arg != F_UNLCK) { + error = -ENOMEM; + new_fl = locks_alloc_lock(); + if (new_fl == NULL) + goto out; - error = -ENOMEM; - new_fl = locks_alloc_lock(); - if (new_fl == NULL) - goto out; + error = -EAGAIN; + if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) + goto out; + if ((arg == F_WRLCK) + && ((atomic_read(&dentry->d_count) > 1) + || (atomic_read(&inode->i_count) > 1))) + goto out; + } /* * At this point, we know that if there is an exclusive @@ -1404,6 +1406,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) rdlease_count++; } + error = -EAGAIN; if ((arg == F_RDLCK && (wrlease_count > 0)) || (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) goto out; @@ -1490,8 +1493,7 @@ EXPORT_SYMBOL_GPL(vfs_setlease); int fcntl_setlease(unsigned int fd, struct file *filp, long arg) { struct file_lock fl, *flp = &fl; - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = filp->f_path.dentry->d_inode; int error; locks_init_lock(&fl); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 55dfdd71f1b0..8799b8708188 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2712,9 +2712,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Note: locks.c uses the BKL to protect the inode's lock list. */ - /* XXX?: Just to divert the locks_release_private at the start of - * locks_copy_lock: */ - locks_init_lock(&conflock); err = vfs_lock_file(filp, cmd, &file_lock, &conflock); switch (-err) { case 0: /* success! */ diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 1ba7ad981935..c513bbdf2d36 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -376,20 +376,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia goto xdr_error; } } - if (bmval[1] & FATTR4_WORD1_TIME_METADATA) { - /* We require the high 32 bits of 'seconds' to be 0, and we ignore - all 32 bits of 'nseconds'. */ - READ_BUF(12); - len += 12; - READ32(dummy32); - if (dummy32) - return nfserr_inval; - READ32(iattr->ia_ctime.tv_sec); - READ32(iattr->ia_ctime.tv_nsec); - if (iattr->ia_ctime.tv_nsec >= (u32)1000000000) - return nfserr_inval; - iattr->ia_valid |= ATTR_CTIME; - } if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { READ_BUF(4); len += 4; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 613bcb8171a5..42f3820ee8f5 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -22,6 +22,7 @@ #include <linux/seq_file.h> #include <linux/pagemap.h> #include <linux/init.h> +#include <linux/inet.h> #include <linux/string.h> #include <linux/smp_lock.h> #include <linux/ctype.h> @@ -35,6 +36,7 @@ #include <linux/nfsd/cache.h> #include <linux/nfsd/xdr.h> #include <linux/nfsd/syscall.h> +#include <linux/lockd/lockd.h> #include <asm/uaccess.h> #include <net/ipv6.h> @@ -53,6 +55,8 @@ enum { NFSD_Getfs, NFSD_List, NFSD_Fh, + NFSD_FO_UnlockIP, + NFSD_FO_UnlockFS, NFSD_Threads, NFSD_Pool_Threads, NFSD_Versions, @@ -89,6 +93,9 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); #endif +static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size); +static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size); + static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Svc] = write_svc, [NFSD_Add] = write_add, @@ -98,6 +105,8 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Getfd] = write_getfd, [NFSD_Getfs] = write_getfs, [NFSD_Fh] = write_filehandle, + [NFSD_FO_UnlockIP] = failover_unlock_ip, + [NFSD_FO_UnlockFS] = failover_unlock_fs, [NFSD_Threads] = write_threads, [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, @@ -298,6 +307,58 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) return err; } +static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) +{ + __be32 server_ip; + char *fo_path, c; + int b1, b2, b3, b4; + + /* sanity check */ + if (size == 0) + return -EINVAL; + + if (buf[size-1] != '\n') + return -EINVAL; + + fo_path = buf; + if (qword_get(&buf, fo_path, size) < 0) + return -EINVAL; + + /* get ipv4 address */ + if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4) + return -EINVAL; + server_ip = htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); + + return nlmsvc_unlock_all_by_ip(server_ip); +} + +static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) +{ + struct nameidata nd; + char *fo_path; + int error; + + /* sanity check */ + if (size == 0) + return -EINVAL; + + if (buf[size-1] != '\n') + return -EINVAL; + + fo_path = buf; + if (qword_get(&buf, fo_path, size) < 0) + return -EINVAL; + + error = path_lookup(fo_path, 0, &nd); + if (error) + return error; + + error = nlmsvc_unlock_all_by_sb(nd.path.mnt->mnt_sb); + + path_put(&nd.path); + return error; +} + static ssize_t write_filehandle(struct file *file, char *buf, size_t size) { /* request is: @@ -700,6 +761,10 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, + [NFSD_FO_UnlockIP] = {"unlock_ip", + &transaction_ops, S_IWUSR|S_IRUSR}, + [NFSD_FO_UnlockFS] = {"unlock_filesystem", + &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, diff --git a/include/asm-arm/arch-pxa/pxa3xx_nand.h b/include/asm-arm/arch-pxa/pxa3xx_nand.h new file mode 100644 index 000000000000..81a8937486cb --- /dev/null +++ b/include/asm-arm/arch-pxa/pxa3xx_nand.h @@ -0,0 +1,18 @@ +#ifndef __ASM_ARCH_PXA3XX_NAND_H +#define __ASM_ARCH_PXA3XX_NAND_H + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +struct pxa3xx_nand_platform_data { + + /* the data flash bus is shared between the Static Memory + * Controller and the Data Flash Controller, the arbiter + * controls the ownership of the bus + */ + int enable_arbiter; + + struct mtd_partition *parts; + unsigned int nr_parts; +}; +#endif /* __ASM_ARCH_PXA3XX_NAND_H */ diff --git a/include/asm-arm/plat-s3c/nand.h b/include/asm-arm/plat-s3c/nand.h index 8816f7f9cee1..ad6bbe90616e 100644 --- a/include/asm-arm/plat-s3c/nand.h +++ b/include/asm-arm/plat-s3c/nand.h @@ -22,11 +22,14 @@ */ struct s3c2410_nand_set { + unsigned int disable_ecc : 1; + int nr_chips; int nr_partitions; char *name; int *nr_map; struct mtd_partition *partitions; + struct nand_ecclayout *ecc_layout; }; struct s3c2410_platform_nand { @@ -36,6 +39,8 @@ struct s3c2410_platform_nand { int twrph0; /* active time for nWE/nOE */ int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ + unsigned int ignore_unset_ecc : 1; + int nr_sets; struct s3c2410_nand_set *sets; diff --git a/include/asm-x86/fixmap.h b/include/asm-x86/fixmap.h index 382eb271a892..5bd206973dca 100644 --- a/include/asm-x86/fixmap.h +++ b/include/asm-x86/fixmap.h @@ -1,5 +1,13 @@ +#ifndef _ASM_FIXMAP_H +#define _ASM_FIXMAP_H + #ifdef CONFIG_X86_32 # include "fixmap_32.h" #else # include "fixmap_64.h" #endif + +#define clear_fixmap(idx) \ + __set_fixmap(idx, 0, __pgprot(0)) + +#endif diff --git a/include/asm-x86/fixmap_32.h b/include/asm-x86/fixmap_32.h index eb1665125c44..4b96148e90c1 100644 --- a/include/asm-x86/fixmap_32.h +++ b/include/asm-x86/fixmap_32.h @@ -10,8 +10,8 @@ * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 */ -#ifndef _ASM_FIXMAP_H -#define _ASM_FIXMAP_H +#ifndef _ASM_FIXMAP_32_H +#define _ASM_FIXMAP_32_H /* used by vmalloc.c, vsyscall.lds.S. @@ -121,9 +121,6 @@ extern void reserve_top_address(unsigned long reserve); #define set_fixmap_nocache(idx, phys) \ __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE) -#define clear_fixmap(idx) \ - __set_fixmap(idx, 0, __pgprot(0)) - #define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP) #define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) diff --git a/include/asm-x86/fixmap_64.h b/include/asm-x86/fixmap_64.h index f3d76858c0e6..355d26a75a82 100644 --- a/include/asm-x86/fixmap_64.h +++ b/include/asm-x86/fixmap_64.h @@ -8,8 +8,8 @@ * Copyright (C) 1998 Ingo Molnar */ -#ifndef _ASM_FIXMAP_H -#define _ASM_FIXMAP_H +#ifndef _ASM_FIXMAP_64_H +#define _ASM_FIXMAP_64_H #include <linux/kernel.h> #include <asm/apicdef.h> diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h index 3d419398499b..0f13b945e240 100644 --- a/include/asm-x86/paravirt.h +++ b/include/asm-x86/paravirt.h @@ -220,11 +220,13 @@ struct pv_mmu_ops { unsigned long va); /* Hooks for allocating/releasing pagetable pages */ - void (*alloc_pt)(struct mm_struct *mm, u32 pfn); - void (*alloc_pd)(struct mm_struct *mm, u32 pfn); - void (*alloc_pd_clone)(u32 pfn, u32 clonepfn, u32 start, u32 count); - void (*release_pt)(u32 pfn); - void (*release_pd)(u32 pfn); + void (*alloc_pte)(struct mm_struct *mm, u32 pfn); + void (*alloc_pmd)(struct mm_struct *mm, u32 pfn); + void (*alloc_pmd_clone)(u32 pfn, u32 clonepfn, u32 start, u32 count); + void (*alloc_pud)(struct mm_struct *mm, u32 pfn); + void (*release_pte)(u32 pfn); + void (*release_pmd)(u32 pfn); + void (*release_pud)(u32 pfn); /* Pagetable manipulation functions */ void (*set_pte)(pte_t *ptep, pte_t pteval); @@ -910,28 +912,37 @@ static inline void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, PVOP_VCALL3(pv_mmu_ops.flush_tlb_others, &cpumask, mm, va); } -static inline void paravirt_alloc_pt(struct mm_struct *mm, unsigned pfn) +static inline void paravirt_alloc_pte(struct mm_struct *mm, unsigned pfn) { - PVOP_VCALL2(pv_mmu_ops.alloc_pt, mm, pfn); + PVOP_VCALL2(pv_mmu_ops.alloc_pte, mm, pfn); } -static inline void paravirt_release_pt(unsigned pfn) +static inline void paravirt_release_pte(unsigned pfn) { - PVOP_VCALL1(pv_mmu_ops.release_pt, pfn); + PVOP_VCALL1(pv_mmu_ops.release_pte, pfn); } -static inline void paravirt_alloc_pd(struct mm_struct *mm, unsigned pfn) +static inline void paravirt_alloc_pmd(struct mm_struct *mm, unsigned pfn) { - PVOP_VCALL2(pv_mmu_ops.alloc_pd, mm, pfn); + PVOP_VCALL2(pv_mmu_ops.alloc_pmd, mm, pfn); } -static inline void paravirt_alloc_pd_clone(unsigned pfn, unsigned clonepfn, - unsigned start, unsigned count) +static inline void paravirt_alloc_pmd_clone(unsigned pfn, unsigned clonepfn, + unsigned start, unsigned count) { - PVOP_VCALL4(pv_mmu_ops.alloc_pd_clone, pfn, clonepfn, start, count); + PVOP_VCALL4(pv_mmu_ops.alloc_pmd_clone, pfn, clonepfn, start, count); } -static inline void paravirt_release_pd(unsigned pfn) +static inline void paravirt_release_pmd(unsigned pfn) { - PVOP_VCALL1(pv_mmu_ops.release_pd, pfn); + PVOP_VCALL1(pv_mmu_ops.release_pmd, pfn); +} + +static inline void paravirt_alloc_pud(struct mm_struct *mm, unsigned pfn) +{ + PVOP_VCALL2(pv_mmu_ops.alloc_pud, mm, pfn); +} +static inline void paravirt_release_pud(unsigned pfn) +{ + PVOP_VCALL1(pv_mmu_ops.release_pud, pfn); } #ifdef CONFIG_HIGHPTE diff --git a/include/asm-x86/pgalloc.h b/include/asm-x86/pgalloc.h index 5886eed05886..91e4641f3f31 100644 --- a/include/asm-x86/pgalloc.h +++ b/include/asm-x86/pgalloc.h @@ -1,5 +1,110 @@ -#ifdef CONFIG_X86_32 -# include "pgalloc_32.h" +#ifndef _ASM_X86_PGALLOC_H +#define _ASM_X86_PGALLOC_H + +#include <linux/threads.h> +#include <linux/mm.h> /* for struct page */ +#include <linux/pagemap.h> + +#ifdef CONFIG_PARAVIRT +#include <asm/paravirt.h> #else -# include "pgalloc_64.h" +static inline void paravirt_alloc_pte(struct mm_struct *mm, unsigned long pfn) {} +static inline void paravirt_alloc_pmd(struct mm_struct *mm, unsigned long pfn) {} +static inline void paravirt_alloc_pmd_clone(unsigned long pfn, unsigned long clonepfn, + unsigned long start, unsigned long count) {} +static inline void paravirt_alloc_pud(struct mm_struct *mm, unsigned long pfn) {} +static inline void paravirt_release_pte(unsigned long pfn) {} +static inline void paravirt_release_pmd(unsigned long pfn) {} +static inline void paravirt_release_pud(unsigned long pfn) {} #endif + +/* + * Allocate and free page tables. + */ +extern pgd_t *pgd_alloc(struct mm_struct *); +extern void pgd_free(struct mm_struct *mm, pgd_t *pgd); + +extern pte_t *pte_alloc_one_kernel(struct mm_struct *, unsigned long); +extern pgtable_t pte_alloc_one(struct mm_struct *, unsigned long); + +/* Should really implement gc for free page table pages. This could be + done with a reference count in struct page. */ + +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ + BUG_ON((unsigned long)pte & (PAGE_SIZE-1)); + free_page((unsigned long)pte); +} + +static inline void pte_free(struct mm_struct *mm, struct page *pte) +{ + __free_page(pte); +} + +extern void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte); + +static inline void pmd_populate_kernel(struct mm_struct *mm, + pmd_t *pmd, pte_t *pte) +{ + paravirt_alloc_pte(mm, __pa(pte) >> PAGE_SHIFT); + set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE)); +} + +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, + struct page *pte) +{ + unsigned long pfn = page_to_pfn(pte); + + paravirt_alloc_pte(mm, pfn); + set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE)); +} + +#define pmd_pgtable(pmd) pmd_page(pmd) + +#if PAGETABLE_LEVELS > 2 +static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + return (pmd_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); +} + +static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) +{ + BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); + free_page((unsigned long)pmd); +} + +extern void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd); + +#ifdef CONFIG_X86_PAE +extern void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd); +#else /* !CONFIG_X86_PAE */ +static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) +{ + paravirt_alloc_pmd(mm, __pa(pmd) >> PAGE_SHIFT); + set_pud(pud, __pud(_PAGE_TABLE | __pa(pmd))); +} +#endif /* CONFIG_X86_PAE */ + +#if PAGETABLE_LEVELS > 3 +static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) +{ + paravirt_alloc_pud(mm, __pa(pud) >> PAGE_SHIFT); + set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pud))); +} + +static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) +{ + return (pud_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); +} + +static inline void pud_free(struct mm_struct *mm, pud_t *pud) +{ + BUG_ON((unsigned long)pud & (PAGE_SIZE-1)); + free_page((unsigned long)pud); +} + +extern void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pud); +#endif /* PAGETABLE_LEVELS > 3 */ +#endif /* PAGETABLE_LEVELS > 2 */ + +#endif /* _ASM_X86_PGALLOC_H */ diff --git a/include/asm-x86/pgalloc_32.h b/include/asm-x86/pgalloc_32.h deleted file mode 100644 index 6bea6e5b5ee5..000000000000 --- a/include/asm-x86/pgalloc_32.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef _I386_PGALLOC_H -#define _I386_PGALLOC_H - -#include <linux/threads.h> -#include <linux/mm.h> /* for struct page */ -#include <linux/pagemap.h> -#include <asm/tlb.h> -#include <asm-generic/tlb.h> - -#ifdef CONFIG_PARAVIRT -#include <asm/paravirt.h> -#else -#define paravirt_alloc_pt(mm, pfn) do { } while (0) -#define paravirt_alloc_pd(mm, pfn) do { } while (0) -#define paravirt_alloc_pd_clone(pfn, clonepfn, start, count) do { } while (0) -#define paravirt_release_pt(pfn) do { } while (0) -#define paravirt_release_pd(pfn) do { } while (0) -#endif - -static inline void pmd_populate_kernel(struct mm_struct *mm, - pmd_t *pmd, pte_t *pte) -{ - paravirt_alloc_pt(mm, __pa(pte) >> PAGE_SHIFT); - set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE)); -} - -static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) -{ - unsigned long pfn = page_to_pfn(pte); - - paravirt_alloc_pt(mm, pfn); - set_pmd(pmd, __pmd(((pteval_t)pfn << PAGE_SHIFT) | _PAGE_TABLE)); -} -#define pmd_pgtable(pmd) pmd_page(pmd) - -/* - * Allocate and free page tables. - */ -extern pgd_t *pgd_alloc(struct mm_struct *); -extern void pgd_free(struct mm_struct *mm, pgd_t *pgd); - -extern pte_t *pte_alloc_one_kernel(struct mm_struct *, unsigned long); -extern pgtable_t pte_alloc_one(struct mm_struct *, unsigned long); - -static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ - free_page((unsigned long)pte); -} - -static inline void pte_free(struct mm_struct *mm, pgtable_t pte) -{ - pgtable_page_dtor(pte); - __free_page(pte); -} - - -extern void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte); - -#ifdef CONFIG_X86_PAE -/* - * In the PAE case we free the pmds as part of the pgd. - */ -static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pmd_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); - free_page((unsigned long)pmd); -} - -extern void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd); - -static inline void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd) -{ - paravirt_alloc_pd(mm, __pa(pmd) >> PAGE_SHIFT); - - /* Note: almost everything apart from _PAGE_PRESENT is - reserved at the pmd (PDPT) level. */ - set_pud(pudp, __pud(__pa(pmd) | _PAGE_PRESENT)); - - /* - * According to Intel App note "TLBs, Paging-Structure Caches, - * and Their Invalidation", April 2007, document 317080-001, - * section 8.1: in PAE mode we explicitly have to flush the - * TLB via cr3 if the top-level pgd is changed... - */ - if (mm == current->active_mm) - write_cr3(read_cr3()); -} -#endif /* CONFIG_X86_PAE */ - -#endif /* _I386_PGALLOC_H */ diff --git a/include/asm-x86/pgalloc_64.h b/include/asm-x86/pgalloc_64.h deleted file mode 100644 index 8d6722320dcc..000000000000 --- a/include/asm-x86/pgalloc_64.h +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef _X86_64_PGALLOC_H -#define _X86_64_PGALLOC_H - -#include <asm/pda.h> -#include <linux/threads.h> -#include <linux/mm.h> - -#define pmd_populate_kernel(mm, pmd, pte) \ - set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte))) -#define pud_populate(mm, pud, pmd) \ - set_pud(pud, __pud(_PAGE_TABLE | __pa(pmd))) -#define pgd_populate(mm, pgd, pud) \ - set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pud))) - -#define pmd_pgtable(pmd) pmd_page(pmd) - -static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) -{ - set_pmd(pmd, __pmd(_PAGE_TABLE | (page_to_pfn(pte) << PAGE_SHIFT))); -} - -static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) -{ - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); - free_page((unsigned long)pmd); -} - -static inline pmd_t *pmd_alloc_one (struct mm_struct *mm, unsigned long addr) -{ - return (pmd_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); -} - -static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr) -{ - return (pud_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); -} - -static inline void pud_free(struct mm_struct *mm, pud_t *pud) -{ - BUG_ON((unsigned long)pud & (PAGE_SIZE-1)); - free_page((unsigned long)pud); -} - -static inline void pgd_list_add(pgd_t *pgd) -{ - struct page *page = virt_to_page(pgd); - unsigned long flags; - - spin_lock_irqsave(&pgd_lock, flags); - list_add(&page->lru, &pgd_list); - spin_unlock_irqrestore(&pgd_lock, flags); -} - -static inline void pgd_list_del(pgd_t *pgd) -{ - struct page *page = virt_to_page(pgd); - unsigned long flags; - - spin_lock_irqsave(&pgd_lock, flags); - list_del(&page->lru); - spin_unlock_irqrestore(&pgd_lock, flags); -} - -static inline pgd_t *pgd_alloc(struct mm_struct *mm) -{ - unsigned boundary; - pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); - if (!pgd) - return NULL; - pgd_list_add(pgd); - /* - * Copy kernel pointers in from init. - * Could keep a freelist or slab cache of those because the kernel - * part never changes. - */ - boundary = pgd_index(__PAGE_OFFSET); - memset(pgd, 0, boundary * sizeof(pgd_t)); - memcpy(pgd + boundary, - init_level4_pgt + boundary, - (PTRS_PER_PGD - boundary) * sizeof(pgd_t)); - return pgd; -} - -static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) -{ - BUG_ON((unsigned long)pgd & (PAGE_SIZE-1)); - pgd_list_del(pgd); - free_page((unsigned long)pgd); -} - -static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) -{ - return (pte_t *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); -} - -static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) -{ - struct page *page; - void *p; - - p = (void *)get_zeroed_page(GFP_KERNEL|__GFP_REPEAT); - if (!p) - return NULL; - page = virt_to_page(p); - pgtable_page_ctor(page); - return page; -} - -/* Should really implement gc for free page table pages. This could be - done with a reference count in struct page. */ - -static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) -{ - BUG_ON((unsigned long)pte & (PAGE_SIZE-1)); - free_page((unsigned long)pte); -} - -static inline void pte_free(struct mm_struct *mm, pgtable_t pte) -{ - pgtable_page_dtor(pte); - __free_page(pte); -} - -#define __pte_free_tlb(tlb,pte) \ -do { \ - pgtable_page_dtor((pte)); \ - tlb_remove_page((tlb), (pte)); \ -} while (0) - -#define __pmd_free_tlb(tlb,x) tlb_remove_page((tlb),virt_to_page(x)) -#define __pud_free_tlb(tlb,x) tlb_remove_page((tlb),virt_to_page(x)) - -#endif /* _X86_64_PGALLOC_H */ diff --git a/include/asm-x86/pgtable.h b/include/asm-x86/pgtable.h index f1d9f4a03f6f..b8a08bd7bd48 100644 --- a/include/asm-x86/pgtable.h +++ b/include/asm-x86/pgtable.h @@ -1,7 +1,6 @@ #ifndef _ASM_X86_PGTABLE_H #define _ASM_X86_PGTABLE_H -#define USER_PTRS_PER_PGD ((TASK_SIZE-1)/PGDIR_SIZE+1) #define FIRST_USER_ADDRESS 0 #define _PAGE_BIT_PRESENT 0 /* is present */ @@ -330,6 +329,9 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) # include "pgtable_64.h" #endif +#define KERNEL_PGD_BOUNDARY pgd_index(PAGE_OFFSET) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD - KERNEL_PGD_BOUNDARY) + #ifndef __ASSEMBLY__ enum { @@ -389,37 +391,17 @@ static inline void native_set_pte_at(struct mm_struct *mm, unsigned long addr, * bit at the same time. */ #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS -#define ptep_set_access_flags(vma, address, ptep, entry, dirty) \ -({ \ - int __changed = !pte_same(*(ptep), entry); \ - if (__changed && dirty) { \ - *ptep = entry; \ - pte_update_defer((vma)->vm_mm, (address), (ptep)); \ - flush_tlb_page(vma, address); \ - } \ - __changed; \ -}) +extern int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty); #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG -#define ptep_test_and_clear_young(vma, addr, ptep) ({ \ - int __ret = 0; \ - if (pte_young(*(ptep))) \ - __ret = test_and_clear_bit(_PAGE_BIT_ACCESSED, \ - &(ptep)->pte); \ - if (__ret) \ - pte_update((vma)->vm_mm, addr, ptep); \ - __ret; \ -}) +extern int ptep_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep); #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH -#define ptep_clear_flush_young(vma, address, ptep) \ -({ \ - int __young; \ - __young = ptep_test_and_clear_young((vma), (address), (ptep)); \ - if (__young) \ - flush_tlb_page(vma, address); \ - __young; \ -}) +extern int ptep_clear_flush_young(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep); #define __HAVE_ARCH_PTEP_GET_AND_CLEAR static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, @@ -456,6 +438,22 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, pte_update(mm, addr, ptep); } +/* + * clone_pgd_range(pgd_t *dst, pgd_t *src, int count); + * + * dst - pointer to pgd range anwhere on a pgd page + * src - "" + * count - the number of pgds to copy. + * + * dst and src can be on the same page, but the range must not overlap, + * and must not cross a page boundary. + */ +static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count) +{ + memcpy(dst, src, count * sizeof(pgd_t)); +} + + #include <asm-generic/pgtable.h> #endif /* __ASSEMBLY__ */ diff --git a/include/asm-x86/pgtable_32.h b/include/asm-x86/pgtable_32.h index c4a643674458..168b6447cf18 100644 --- a/include/asm-x86/pgtable_32.h +++ b/include/asm-x86/pgtable_32.h @@ -48,9 +48,6 @@ void paging_init(void); #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE - 1)) -#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) -#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) - /* Just any arbitrary offset to the start of the vmalloc VM area: the * current 8MB value just means that there will be a 8MB "hole" after the * physical memory until the kernel virtual memory starts. That means that @@ -109,21 +106,6 @@ extern int pmd_bad(pmd_t pmd); #endif /* - * clone_pgd_range(pgd_t *dst, pgd_t *src, int count); - * - * dst - pointer to pgd range anwhere on a pgd page - * src - "" - * count - the number of pgds to copy. - * - * dst and src can be on the same page, but the range must not overlap, - * and must not cross a page boundary. - */ -static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count) -{ - memcpy(dst, src, count * sizeof(pgd_t)); -} - -/* * Macro to mark a page protection value as "uncacheable". * On processors which do not support it, this is a no-op. */ diff --git a/include/asm-x86/pgtable_64.h b/include/asm-x86/pgtable_64.h index 9fd87d0b6477..a3bbf8766c1d 100644 --- a/include/asm-x86/pgtable_64.h +++ b/include/asm-x86/pgtable_64.h @@ -24,7 +24,7 @@ extern void paging_init(void); #endif /* !__ASSEMBLY__ */ -#define SHARED_KERNEL_PMD 1 +#define SHARED_KERNEL_PMD 0 /* * PGDIR_SHIFT determines what a top-level page table entry can map diff --git a/include/asm-x86/xen/events.h b/include/asm-x86/xen/events.h new file mode 100644 index 000000000000..596312a7bfc9 --- /dev/null +++ b/include/asm-x86/xen/events.h @@ -0,0 +1,22 @@ +#ifndef __XEN_EVENTS_H +#define __XEN_EVENTS_H + +enum ipi_vector { + XEN_RESCHEDULE_VECTOR, + XEN_CALL_FUNCTION_VECTOR, + + XEN_NR_IPIS, +}; + +static inline int xen_irqs_disabled(struct pt_regs *regs) +{ + return raw_irqs_disabled_flags(regs->flags); +} + +static inline void xen_do_IRQ(int irq, struct pt_regs *regs) +{ + regs->orig_ax = ~irq; + do_IRQ(regs); +} + +#endif /* __XEN_EVENTS_H */ diff --git a/include/asm-x86/xen/grant_table.h b/include/asm-x86/xen/grant_table.h new file mode 100644 index 000000000000..2444d4593a3b --- /dev/null +++ b/include/asm-x86/xen/grant_table.h @@ -0,0 +1,7 @@ +#ifndef __XEN_GRANT_TABLE_H +#define __XEN_GRANT_TABLE_H + +#define xen_alloc_vm_area(size) alloc_vm_area(size) +#define xen_free_vm_area(area) free_vm_area(area) + +#endif /* __XEN_GRANT_TABLE_H */ diff --git a/include/asm-x86/xen/hypercall.h b/include/asm-x86/xen/hypercall.h index bc0ee7d961ca..c2ccd997ed35 100644 --- a/include/asm-x86/xen/hypercall.h +++ b/include/asm-x86/xen/hypercall.h @@ -164,6 +164,12 @@ HYPERVISOR_set_callbacks(unsigned long event_selector, } static inline int +HYPERVISOR_callback_op(int cmd, void *arg) +{ + return _hypercall2(int, callback_op, cmd, arg); +} + +static inline int HYPERVISOR_fpu_taskswitch(int set) { return _hypercall1(int, fpu_taskswitch, set); diff --git a/include/asm-x86/xen/interface.h b/include/asm-x86/xen/interface.h index 165c3968e138..6227000a1e84 100644 --- a/include/asm-x86/xen/interface.h +++ b/include/asm-x86/xen/interface.h @@ -22,6 +22,30 @@ #define DEFINE_GUEST_HANDLE(name) __DEFINE_GUEST_HANDLE(name, name) #define GUEST_HANDLE(name) __guest_handle_ ## name +#ifdef __XEN__ +#if defined(__i386__) +#define set_xen_guest_handle(hnd, val) \ + do { \ + if (sizeof(hnd) == 8) \ + *(uint64_t *)&(hnd) = 0; \ + (hnd).p = val; \ + } while (0) +#elif defined(__x86_64__) +#define set_xen_guest_handle(hnd, val) do { (hnd).p = val; } while (0) +#endif +#else +#if defined(__i386__) +#define set_xen_guest_handle(hnd, val) \ + do { \ + if (sizeof(hnd) == 8) \ + *(uint64_t *)&(hnd) = 0; \ + (hnd) = val; \ + } while (0) +#elif defined(__x86_64__) +#define set_xen_guest_handle(hnd, val) do { (hnd) = val; } while (0) +#endif +#endif + #ifndef __ASSEMBLY__ /* Guest handles for primitive C types. */ __DEFINE_GUEST_HANDLE(uchar, unsigned char); @@ -171,6 +195,10 @@ struct arch_vcpu_info { unsigned long pad[5]; /* sizeof(struct vcpu_info) == 64 */ }; +struct xen_callback { + unsigned long cs; + unsigned long eip; +}; #endif /* !__ASSEMBLY__ */ /* diff --git a/include/asm-x86/xen/page.h b/include/asm-x86/xen/page.h new file mode 100644 index 000000000000..01799305f02a --- /dev/null +++ b/include/asm-x86/xen/page.h @@ -0,0 +1,168 @@ +#ifndef __XEN_PAGE_H +#define __XEN_PAGE_H + +#include <linux/pfn.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +#include <xen/features.h> + +/* Xen machine address */ +typedef struct xmaddr { + phys_addr_t maddr; +} xmaddr_t; + +/* Xen pseudo-physical address */ +typedef struct xpaddr { + phys_addr_t paddr; +} xpaddr_t; + +#define XMADDR(x) ((xmaddr_t) { .maddr = (x) }) +#define XPADDR(x) ((xpaddr_t) { .paddr = (x) }) + +/**** MACHINE <-> PHYSICAL CONVERSION MACROS ****/ +#define INVALID_P2M_ENTRY (~0UL) +#define FOREIGN_FRAME_BIT (1UL<<31) +#define FOREIGN_FRAME(m) ((m) | FOREIGN_FRAME_BIT) + +extern unsigned long *phys_to_machine_mapping; + +static inline unsigned long pfn_to_mfn(unsigned long pfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) + return pfn; + + return phys_to_machine_mapping[(unsigned int)(pfn)] & + ~FOREIGN_FRAME_BIT; +} + +static inline int phys_to_machine_mapping_valid(unsigned long pfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) + return 1; + + return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY); +} + +static inline unsigned long mfn_to_pfn(unsigned long mfn) +{ + unsigned long pfn; + + if (xen_feature(XENFEAT_auto_translated_physmap)) + return mfn; + +#if 0 + if (unlikely((mfn >> machine_to_phys_order) != 0)) + return max_mapnr; +#endif + + pfn = 0; + /* + * The array access can fail (e.g., device space beyond end of RAM). + * In such cases it doesn't matter what we return (we return garbage), + * but we must handle the fault without crashing! + */ + __get_user(pfn, &machine_to_phys_mapping[mfn]); + + return pfn; +} + +static inline xmaddr_t phys_to_machine(xpaddr_t phys) +{ + unsigned offset = phys.paddr & ~PAGE_MASK; + return XMADDR(PFN_PHYS((u64)pfn_to_mfn(PFN_DOWN(phys.paddr))) | offset); +} + +static inline xpaddr_t machine_to_phys(xmaddr_t machine) +{ + unsigned offset = machine.maddr & ~PAGE_MASK; + return XPADDR(PFN_PHYS((u64)mfn_to_pfn(PFN_DOWN(machine.maddr))) | offset); +} + +/* + * We detect special mappings in one of two ways: + * 1. If the MFN is an I/O page then Xen will set the m2p entry + * to be outside our maximum possible pseudophys range. + * 2. If the MFN belongs to a different domain then we will certainly + * not have MFN in our p2m table. Conversely, if the page is ours, + * then we'll have p2m(m2p(MFN))==MFN. + * If we detect a special mapping then it doesn't have a 'struct page'. + * We force !pfn_valid() by returning an out-of-range pointer. + * + * NB. These checks require that, for any MFN that is not in our reservation, + * there is no PFN such that p2m(PFN) == MFN. Otherwise we can get confused if + * we are foreign-mapping the MFN, and the other domain as m2p(MFN) == PFN. + * Yikes! Various places must poke in INVALID_P2M_ENTRY for safety. + * + * NB2. When deliberately mapping foreign pages into the p2m table, you *must* + * use FOREIGN_FRAME(). This will cause pte_pfn() to choke on it, as we + * require. In all the cases we care about, the FOREIGN_FRAME bit is + * masked (e.g., pfn_to_mfn()) so behaviour there is correct. + */ +static inline unsigned long mfn_to_local_pfn(unsigned long mfn) +{ + extern unsigned long max_mapnr; + unsigned long pfn = mfn_to_pfn(mfn); + if ((pfn < max_mapnr) + && !xen_feature(XENFEAT_auto_translated_physmap) + && (phys_to_machine_mapping[pfn] != mfn)) + return max_mapnr; /* force !pfn_valid() */ + return pfn; +} + +static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) { + BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); + return; + } + phys_to_machine_mapping[pfn] = mfn; +} + +/* VIRT <-> MACHINE conversion */ +#define virt_to_machine(v) (phys_to_machine(XPADDR(__pa(v)))) +#define virt_to_mfn(v) (pfn_to_mfn(PFN_DOWN(__pa(v)))) +#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT)) + +static inline unsigned long pte_mfn(pte_t pte) +{ + return (pte.pte & ~_PAGE_NX) >> PAGE_SHIFT; +} + +static inline pte_t mfn_pte(unsigned long page_nr, pgprot_t pgprot) +{ + pte_t pte; + + pte.pte = ((phys_addr_t)page_nr << PAGE_SHIFT) | + (pgprot_val(pgprot) & __supported_pte_mask); + + return pte; +} + +static inline pteval_t pte_val_ma(pte_t pte) +{ + return pte.pte; +} + +static inline pte_t __pte_ma(pteval_t x) +{ + return (pte_t) { .pte = x }; +} + +#ifdef CONFIG_X86_PAE +#define pmd_val_ma(v) ((v).pmd) +#define pud_val_ma(v) ((v).pgd.pgd) +#define __pmd_ma(x) ((pmd_t) { (x) } ) +#else /* !X86_PAE */ +#define pmd_val_ma(v) ((v).pud.pgd.pgd) +#endif /* CONFIG_X86_PAE */ + +#define pgd_val_ma(x) ((x).pgd) + + +xmaddr_t arbitrary_virt_to_machine(unsigned long address); +void make_lowmem_page_readonly(void *vaddr); +void make_lowmem_page_readwrite(void *vaddr); + +#endif /* __XEN_PAGE_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index cc2be2cf7d41..6556f2f967e5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -973,6 +973,7 @@ extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); +extern void __locks_copy_lock(struct file_lock *, const struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); extern void posix_test_lock(struct file *, struct file_lock *); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 94649a8da014..102d928f7206 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -194,7 +194,7 @@ void nsm_release(struct nsm_handle *); * This is used in garbage collection and resource reclaim * A return value != 0 means destroy the lock/block/share */ -typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref); +typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); /* * Server-side lock handling @@ -220,6 +220,12 @@ void nlmsvc_mark_resources(void); void nlmsvc_free_host_resources(struct nlm_host *); void nlmsvc_invalidate_all(void); +/* + * Cluster failover support + */ +int nlmsvc_unlock_all_by_sb(struct super_block *sb); +int nlmsvc_unlock_all_by_ip(__be32 server_addr); + static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) { return file->f_file->f_path.dentry->d_inode; diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index 6977780e548f..85fd041d44ad 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -57,6 +57,11 @@ extern char inftlmountrev[]; void INFTL_dumptables(struct INFTLrecord *s); void INFTL_dumpVUchains(struct INFTLrecord *s); +int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + #endif /* __KERNEL__ */ #endif /* __MTD_INFTL_H__ */ diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index bcf2fb3fa4a7..001eec50cac6 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -43,6 +43,11 @@ struct NFTLrecord { int NFTL_mount(struct NFTLrecord *s); int NFTL_formatblock(struct NFTLrecord *s, int block); +int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); +int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, + size_t *retlen, uint8_t *buf); + #ifndef NFTL_MAJOR #define NFTL_MAJOR 93 #endif diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index fd0a260e070b..9aa2a9149b58 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -187,4 +187,7 @@ struct onenand_manufacturers { char *name; }; +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/plat-ram.h b/include/linux/mtd/plat-ram.h index 9667863bd7e3..0e37ad07bce2 100644 --- a/include/linux/mtd/plat-ram.h +++ b/include/linux/mtd/plat-ram.h @@ -21,8 +21,9 @@ #define PLATRAM_RW (1) struct platdata_mtd_ram { - char *mapname; - char **probes; + const char *mapname; + const char **map_probes; + const char **probes; struct mtd_partition *partitions; int nr_partitions; int bankwidth; diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 21ee440dd3e7..41d30c9c9de6 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -329,7 +329,7 @@ extern struct timeval nfssvc_boot; (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL ) #define NFSD_WRITEABLE_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ - | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET) + | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) #endif /* CONFIG_NFSD_V4 */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 779cbcd65f62..02df20f085fe 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -379,6 +379,18 @@ struct phy_driver { }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) +#define PHY_ANY_ID "MATCH ANY PHY" +#define PHY_ANY_UID 0xffffffff + +/* A Structure for boards to register fixups with the PHY Lib */ +struct phy_fixup { + struct list_head list; + char bus_id[BUS_ID_SIZE]; + u32 phy_uid; + u32 phy_uid_mask; + int (*run)(struct phy_device *phydev); +}; + int phy_read(struct phy_device *phydev, u16 regnum); int phy_write(struct phy_device *phydev, u16 regnum, u16 val); int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id); @@ -386,8 +398,8 @@ struct phy_device* get_phy_device(struct mii_bus *bus, int addr); int phy_clear_interrupt(struct phy_device *phydev); int phy_config_interrupt(struct phy_device *phydev, u32 interrupts); struct phy_device * phy_attach(struct net_device *dev, - const char *phy_id, u32 flags, phy_interface_t interface); -struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, + const char *bus_id, u32 flags, phy_interface_t interface); +struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, void (*handler)(struct net_device *), u32 flags, phy_interface_t interface); void phy_disconnect(struct phy_device *phydev); @@ -427,5 +439,13 @@ void phy_print_status(struct phy_device *phydev); struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id); void phy_device_free(struct phy_device *phydev); +int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); +int phy_register_fixup_for_id(const char *bus_id, + int (*run)(struct phy_device *)); +int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); +int phy_scan_fixups(struct phy_device *phydev); + extern struct bus_type mdio_bus_type; #endif /* __PHY_H */ diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 0c82c80b277f..2ca6bae88721 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -97,10 +97,10 @@ struct xfrm_algo { }; struct xfrm_algo_aead { - char alg_name[64]; - int alg_key_len; /* in bits */ - int alg_icv_len; /* in bits */ - char alg_key[0]; + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + unsigned int alg_icv_len; /* in bits */ + char alg_key[0]; }; struct xfrm_stats { diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index 4d46b3bdebd8..8eb018f96002 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -3,5 +3,4 @@ header-y += jffs2-user.h header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h -header-y += ubi-header.h header-y += ubi-user.h diff --git a/include/xen/balloon.h b/include/xen/balloon.h new file mode 100644 index 000000000000..fe43b0f3c86a --- /dev/null +++ b/include/xen/balloon.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * balloon.h + * + * Xen balloon driver - enables returning/claiming memory to/from Xen. + * + * Copyright (c) 2003, B Dragovic + * Copyright (c) 2003-2004, M Williamson, K Fraser + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __XEN_BALLOON_H__ +#define __XEN_BALLOON_H__ + +#include <linux/spinlock.h> + +#if 0 +/* + * Inform the balloon driver that it should allow some slop for device-driver + * memory activities. + */ +void balloon_update_driver_allowance(long delta); + +/* Allocate/free a set of empty pages in low memory (i.e., no RAM mapped). */ +struct page **alloc_empty_pages_and_pagevec(int nr_pages); +void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages); + +void balloon_release_driver_page(struct page *page); + +/* + * Prevent the balloon driver from changing the memory reservation during + * a driver critical region. + */ +extern spinlock_t balloon_lock; +#define balloon_lock(__flags) spin_lock_irqsave(&balloon_lock, __flags) +#define balloon_unlock(__flags) spin_unlock_irqrestore(&balloon_lock, __flags) +#endif + +#endif /* __XEN_BALLOON_H__ */ diff --git a/include/xen/events.h b/include/xen/events.h index 2bde54d29be5..acd8e062c85f 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -5,13 +5,7 @@ #include <xen/interface/event_channel.h> #include <asm/xen/hypercall.h> - -enum ipi_vector { - XEN_RESCHEDULE_VECTOR, - XEN_CALL_FUNCTION_VECTOR, - - XEN_NR_IPIS, -}; +#include <asm/xen/events.h> int bind_evtchn_to_irq(unsigned int evtchn); int bind_evtchn_to_irqhandler(unsigned int evtchn, @@ -37,6 +31,7 @@ int bind_ipi_to_irqhandler(enum ipi_vector ipi, void unbind_from_irqhandler(unsigned int irq, void *dev_id); void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector); +int resend_irq_on_evtchn(unsigned int irq); static inline void notify_remote_via_evtchn(int port) { diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 761c83498e03..466204846121 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -39,6 +39,7 @@ #include <asm/xen/hypervisor.h> #include <xen/interface/grant_table.h> +#include <asm/xen/grant_table.h> /* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */ #define NR_GRANT_FRAMES 4 @@ -102,6 +103,12 @@ void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, void gnttab_grant_foreign_transfer_ref(grant_ref_t, domid_t domid, unsigned long pfn); +int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + struct grant_entry **__shared); +void arch_gnttab_unmap_shared(struct grant_entry *shared, + unsigned long nr_gframes); + #define gnttab_map_vaddr(map) ((void *)(map.host_virt_addr)) #endif /* __ASM_GNTTAB_H__ */ diff --git a/include/xen/interface/callback.h b/include/xen/interface/callback.h new file mode 100644 index 000000000000..4aadcba31af9 --- /dev/null +++ b/include/xen/interface/callback.h @@ -0,0 +1,102 @@ +/****************************************************************************** + * callback.h + * + * Register guest OS callbacks with Xen. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2006, Ian Campbell + */ + +#ifndef __XEN_PUBLIC_CALLBACK_H__ +#define __XEN_PUBLIC_CALLBACK_H__ + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * long callback_op(int cmd, void *extra_args) + * @cmd == CALLBACKOP_??? (callback operation). + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +/* ia64, x86: Callback for event delivery. */ +#define CALLBACKTYPE_event 0 + +/* x86: Failsafe callback when guest state cannot be restored by Xen. */ +#define CALLBACKTYPE_failsafe 1 + +/* x86/64 hypervisor: Syscall by 64-bit guest app ('64-on-64-on-64'). */ +#define CALLBACKTYPE_syscall 2 + +/* + * x86/32 hypervisor: Only available on x86/32 when supervisor_mode_kernel + * feature is enabled. Do not use this callback type in new code. + */ +#define CALLBACKTYPE_sysenter_deprecated 3 + +/* x86: Callback for NMI delivery. */ +#define CALLBACKTYPE_nmi 4 + +/* + * x86: sysenter is only available as follows: + * - 32-bit hypervisor: with the supervisor_mode_kernel feature enabled + * - 64-bit hypervisor: 32-bit guest applications on Intel CPUs + * ('32-on-32-on-64', '32-on-64-on-64') + * [nb. also 64-bit guest applications on Intel CPUs + * ('64-on-64-on-64'), but syscall is preferred] + */ +#define CALLBACKTYPE_sysenter 5 + +/* + * x86/64 hypervisor: Syscall by 32-bit guest app on AMD CPUs + * ('32-on-32-on-64', '32-on-64-on-64') + */ +#define CALLBACKTYPE_syscall32 7 + +/* + * Disable event deliver during callback? This flag is ignored for event and + * NMI callbacks: event delivery is unconditionally disabled. + */ +#define _CALLBACKF_mask_events 0 +#define CALLBACKF_mask_events (1U << _CALLBACKF_mask_events) + +/* + * Register a callback. + */ +#define CALLBACKOP_register 0 +struct callback_register { + uint16_t type; + uint16_t flags; + struct xen_callback address; +}; + +/* + * Unregister a callback. + * + * Not all callbacks can be unregistered. -EINVAL will be returned if + * you attempt to unregister such a callback. + */ +#define CALLBACKOP_unregister 1 +struct callback_unregister { + uint16_t type; + uint16_t _unused; +}; + +#endif /* __XEN_PUBLIC_CALLBACK_H__ */ diff --git a/include/xen/interface/grant_table.h b/include/xen/interface/grant_table.h index 219049802cf2..39da93c21de0 100644 --- a/include/xen/interface/grant_table.h +++ b/include/xen/interface/grant_table.h @@ -185,6 +185,7 @@ struct gnttab_map_grant_ref { grant_handle_t handle; uint64_t dev_bus_addr; }; +DEFINE_GUEST_HANDLE_STRUCT(gnttab_map_grant_ref); /* * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings @@ -206,6 +207,7 @@ struct gnttab_unmap_grant_ref { /* OUT parameters. */ int16_t status; /* GNTST_* */ }; +DEFINE_GUEST_HANDLE_STRUCT(gnttab_unmap_grant_ref); /* * GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least @@ -223,8 +225,9 @@ struct gnttab_setup_table { uint32_t nr_frames; /* OUT parameters. */ int16_t status; /* GNTST_* */ - ulong *frame_list; + GUEST_HANDLE(ulong) frame_list; }; +DEFINE_GUEST_HANDLE_STRUCT(gnttab_setup_table); /* * GNTTABOP_dump_table: Dump the contents of the grant table to the @@ -237,6 +240,7 @@ struct gnttab_dump_table { /* OUT parameters. */ int16_t status; /* GNTST_* */ }; +DEFINE_GUEST_HANDLE_STRUCT(gnttab_dump_table); /* * GNTTABOP_transfer_grant_ref: Transfer <frame> to a foreign domain. The @@ -255,7 +259,7 @@ struct gnttab_transfer { /* OUT parameters. */ int16_t status; }; - +DEFINE_GUEST_HANDLE_STRUCT(gnttab_transfer); /* * GNTTABOP_copy: Hypervisor based copy @@ -296,6 +300,7 @@ struct gnttab_copy { /* OUT parameters. */ int16_t status; }; +DEFINE_GUEST_HANDLE_STRUCT(gnttab_copy); /* * GNTTABOP_query_size: Query the current and maximum sizes of the shared @@ -313,7 +318,7 @@ struct gnttab_query_size { uint32_t max_nr_frames; int16_t status; /* GNTST_* */ }; - +DEFINE_GUEST_HANDLE_STRUCT(gnttab_query_size); /* * Bitfield values for update_pin_status.flags. diff --git a/include/xen/interface/io/fbif.h b/include/xen/interface/io/fbif.h new file mode 100644 index 000000000000..5a934dd7796d --- /dev/null +++ b/include/xen/interface/io/fbif.h @@ -0,0 +1,124 @@ +/* + * fbif.h -- Xen virtual frame buffer device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> + */ + +#ifndef __XEN_PUBLIC_IO_FBIF_H__ +#define __XEN_PUBLIC_IO_FBIF_H__ + +/* Out events (frontend -> backend) */ + +/* + * Out events may be sent only when requested by backend, and receipt + * of an unknown out event is an error. + */ + +/* Event type 1 currently not used */ +/* + * Framebuffer update notification event + * Capable frontend sets feature-update in xenstore. + * Backend requests it by setting request-update in xenstore. + */ +#define XENFB_TYPE_UPDATE 2 + +struct xenfb_update { + uint8_t type; /* XENFB_TYPE_UPDATE */ + int32_t x; /* source x */ + int32_t y; /* source y */ + int32_t width; /* rect width */ + int32_t height; /* rect height */ +}; + +#define XENFB_OUT_EVENT_SIZE 40 + +union xenfb_out_event { + uint8_t type; + struct xenfb_update update; + char pad[XENFB_OUT_EVENT_SIZE]; +}; + +/* In events (backend -> frontend) */ + +/* + * Frontends should ignore unknown in events. + * No in events currently defined. + */ + +#define XENFB_IN_EVENT_SIZE 40 + +union xenfb_in_event { + uint8_t type; + char pad[XENFB_IN_EVENT_SIZE]; +}; + +/* shared page */ + +#define XENFB_IN_RING_SIZE 1024 +#define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE) +#define XENFB_IN_RING_OFFS 1024 +#define XENFB_IN_RING(page) \ + ((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS)) +#define XENFB_IN_RING_REF(page, idx) \ + (XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN]) + +#define XENFB_OUT_RING_SIZE 2048 +#define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE) +#define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE) +#define XENFB_OUT_RING(page) \ + ((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS)) +#define XENFB_OUT_RING_REF(page, idx) \ + (XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN]) + +struct xenfb_page { + uint32_t in_cons, in_prod; + uint32_t out_cons, out_prod; + + int32_t width; /* width of the framebuffer (in pixels) */ + int32_t height; /* height of the framebuffer (in pixels) */ + uint32_t line_length; /* length of a row of pixels (in bytes) */ + uint32_t mem_length; /* length of the framebuffer (in bytes) */ + uint8_t depth; /* depth of a pixel (in bits) */ + + /* + * Framebuffer page directory + * + * Each directory page holds PAGE_SIZE / sizeof(*pd) + * framebuffer pages, and can thus map up to PAGE_SIZE * + * PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and + * sizeof(unsigned long) == 4, that's 4 Megs. Two directory + * pages should be enough for a while. + */ + unsigned long pd[2]; +}; + +/* + * Wart: xenkbd needs to know resolution. Put it here until a better + * solution is found, but don't leak it to the backend. + */ +#ifdef __KERNEL__ +#define XENFB_WIDTH 800 +#define XENFB_HEIGHT 600 +#define XENFB_DEPTH 32 +#endif + +#endif diff --git a/include/xen/interface/io/kbdif.h b/include/xen/interface/io/kbdif.h new file mode 100644 index 000000000000..fb97f4284ffd --- /dev/null +++ b/include/xen/interface/io/kbdif.h @@ -0,0 +1,114 @@ +/* + * kbdif.h -- Xen virtual keyboard/mouse + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> + */ + +#ifndef __XEN_PUBLIC_IO_KBDIF_H__ +#define __XEN_PUBLIC_IO_KBDIF_H__ + +/* In events (backend -> frontend) */ + +/* + * Frontends should ignore unknown in events. + */ + +/* Pointer movement event */ +#define XENKBD_TYPE_MOTION 1 +/* Event type 2 currently not used */ +/* Key event (includes pointer buttons) */ +#define XENKBD_TYPE_KEY 3 +/* + * Pointer position event + * Capable backend sets feature-abs-pointer in xenstore. + * Frontend requests ot instead of XENKBD_TYPE_MOTION by setting + * request-abs-update in xenstore. + */ +#define XENKBD_TYPE_POS 4 + +struct xenkbd_motion { + uint8_t type; /* XENKBD_TYPE_MOTION */ + int32_t rel_x; /* relative X motion */ + int32_t rel_y; /* relative Y motion */ +}; + +struct xenkbd_key { + uint8_t type; /* XENKBD_TYPE_KEY */ + uint8_t pressed; /* 1 if pressed; 0 otherwise */ + uint32_t keycode; /* KEY_* from linux/input.h */ +}; + +struct xenkbd_position { + uint8_t type; /* XENKBD_TYPE_POS */ + int32_t abs_x; /* absolute X position (in FB pixels) */ + int32_t abs_y; /* absolute Y position (in FB pixels) */ +}; + +#define XENKBD_IN_EVENT_SIZE 40 + +union xenkbd_in_event { + uint8_t type; + struct xenkbd_motion motion; + struct xenkbd_key key; + struct xenkbd_position pos; + char pad[XENKBD_IN_EVENT_SIZE]; +}; + +/* Out events (frontend -> backend) */ + +/* + * Out events may be sent only when requested by backend, and receipt + * of an unknown out event is an error. + * No out events currently defined. + */ + +#define XENKBD_OUT_EVENT_SIZE 40 + +union xenkbd_out_event { + uint8_t type; + char pad[XENKBD_OUT_EVENT_SIZE]; +}; + +/* shared page */ + +#define XENKBD_IN_RING_SIZE 2048 +#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE) +#define XENKBD_IN_RING_OFFS 1024 +#define XENKBD_IN_RING(page) \ + ((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS)) +#define XENKBD_IN_RING_REF(page, idx) \ + (XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN]) + +#define XENKBD_OUT_RING_SIZE 1024 +#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE) +#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE) +#define XENKBD_OUT_RING(page) \ + ((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS)) +#define XENKBD_OUT_RING_REF(page, idx) \ + (XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN]) + +struct xenkbd_page { + uint32_t in_cons, in_prod; + uint32_t out_cons, out_prod; +}; + +#endif diff --git a/include/xen/interface/io/protocols.h b/include/xen/interface/io/protocols.h new file mode 100644 index 000000000000..01fc8ae5f0b0 --- /dev/null +++ b/include/xen/interface/io/protocols.h @@ -0,0 +1,21 @@ +#ifndef __XEN_PROTOCOLS_H__ +#define __XEN_PROTOCOLS_H__ + +#define XEN_IO_PROTO_ABI_X86_32 "x86_32-abi" +#define XEN_IO_PROTO_ABI_X86_64 "x86_64-abi" +#define XEN_IO_PROTO_ABI_IA64 "ia64-abi" +#define XEN_IO_PROTO_ABI_POWERPC64 "powerpc64-abi" + +#if defined(__i386__) +# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_X86_32 +#elif defined(__x86_64__) +# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_X86_64 +#elif defined(__ia64__) +# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_IA64 +#elif defined(__powerpc64__) +# define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_POWERPC64 +#else +# error arch fixup needed here +#endif + +#endif diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h index af36ead16817..da768469aa92 100644 --- a/include/xen/interface/memory.h +++ b/include/xen/interface/memory.h @@ -29,7 +29,7 @@ struct xen_memory_reservation { * OUT: GMFN bases of extents that were allocated * (NB. This command also updates the mach_to_phys translation table) */ - GUEST_HANDLE(ulong) extent_start; + ulong extent_start; /* Number of extents, and size/alignment of each (2^extent_order pages). */ unsigned long nr_extents; @@ -50,7 +50,6 @@ struct xen_memory_reservation { domid_t domid; }; -DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation); /* * Returns the maximum machine frame number of mapped RAM in this system. @@ -86,7 +85,7 @@ struct xen_machphys_mfn_list { * any large discontiguities in the machine address space, 2MB gaps in * the machphys table will be represented by an MFN base of zero. */ - GUEST_HANDLE(ulong) extent_start; + ulong extent_start; /* * Number of extents written to the above array. This will be smaller @@ -94,7 +93,6 @@ struct xen_machphys_mfn_list { */ unsigned int nr_extents; }; -DEFINE_GUEST_HANDLE_STRUCT(xen_machphys_mfn_list); /* * Sets the GPFN at which a particular page appears in the specified guest's @@ -117,7 +115,6 @@ struct xen_add_to_physmap { /* GPFN where the source mapping page should appear. */ unsigned long gpfn; }; -DEFINE_GUEST_HANDLE_STRUCT(xen_add_to_physmap); /* * Translates a list of domain-specific GPFNs into MFNs. Returns a -ve error @@ -132,14 +129,13 @@ struct xen_translate_gpfn_list { unsigned long nr_gpfns; /* List of GPFNs to translate. */ - GUEST_HANDLE(ulong) gpfn_list; + ulong gpfn_list; /* * Output list to contain MFN translations. May be the same as the input * list (in which case each input GPFN is overwritten with the output MFN). */ - GUEST_HANDLE(ulong) mfn_list; + ulong mfn_list; }; -DEFINE_GUEST_HANDLE_STRUCT(xen_translate_gpfn_list); #endif /* __XEN_PUBLIC_MEMORY_H__ */ diff --git a/include/xen/interface/vcpu.h b/include/xen/interface/vcpu.h index b05d8a6d9143..87e6f8a48661 100644 --- a/include/xen/interface/vcpu.h +++ b/include/xen/interface/vcpu.h @@ -85,6 +85,7 @@ struct vcpu_runstate_info { */ uint64_t time[4]; }; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_runstate_info); /* VCPU is currently running on a physical CPU. */ #define RUNSTATE_running 0 @@ -119,6 +120,7 @@ struct vcpu_runstate_info { #define VCPUOP_register_runstate_memory_area 5 struct vcpu_register_runstate_memory_area { union { + GUEST_HANDLE(vcpu_runstate_info) h; struct vcpu_runstate_info *v; uint64_t p; } addr; @@ -134,6 +136,7 @@ struct vcpu_register_runstate_memory_area { struct vcpu_set_periodic_timer { uint64_t period_ns; }; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_set_periodic_timer); /* * Set or stop a VCPU's single-shot timer. Every VCPU has one single-shot @@ -145,6 +148,7 @@ struct vcpu_set_singleshot_timer { uint64_t timeout_abs_ns; uint32_t flags; /* VCPU_SSHOTTMR_??? */ }; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_set_singleshot_timer); /* Flags to VCPUOP_set_singleshot_timer. */ /* Require the timeout to be in the future (return -ETIME if it's passed). */ @@ -164,5 +168,6 @@ struct vcpu_register_vcpu_info { uint32_t offset; /* offset within page */ uint32_t rsvd; /* unused */ }; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_register_vcpu_info); #endif /* __XEN_PUBLIC_VCPU_H__ */ diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 518a5bf79ed3..9b018da48cf3 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -58,6 +58,16 @@ #define __HYPERVISOR_physdev_op 33 #define __HYPERVISOR_hvm_op 34 +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + /* * VIRTUAL INTERRUPTS * @@ -68,8 +78,18 @@ #define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */ #define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */ #define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */ -#define NR_VIRQS 8 +/* Architecture-specific VIRQ definitions. */ +#define VIRQ_ARCH_0 16 +#define VIRQ_ARCH_1 17 +#define VIRQ_ARCH_2 18 +#define VIRQ_ARCH_3 19 +#define VIRQ_ARCH_4 20 +#define VIRQ_ARCH_5 21 +#define VIRQ_ARCH_6 22 +#define VIRQ_ARCH_7 23 + +#define NR_VIRQS 24 /* * MMU-UPDATE REQUESTS * diff --git a/include/xen/interface/xencomm.h b/include/xen/interface/xencomm.h new file mode 100644 index 000000000000..ac45e0712afa --- /dev/null +++ b/include/xen/interface/xencomm.h @@ -0,0 +1,41 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (C) IBM Corp. 2006 + */ + +#ifndef _XEN_XENCOMM_H_ +#define _XEN_XENCOMM_H_ + +/* A xencomm descriptor is a scatter/gather list containing physical + * addresses corresponding to a virtually contiguous memory area. The + * hypervisor translates these physical addresses to machine addresses to copy + * to and from the virtually contiguous area. + */ + +#define XENCOMM_MAGIC 0x58434F4D /* 'XCOM' */ +#define XENCOMM_INVALID (~0UL) + +struct xencomm_desc { + uint32_t magic; + uint32_t nr_addrs; /* the number of entries in address[] */ + uint64_t address[0]; +}; + +#endif /* _XEN_XENCOMM_H_ */ diff --git a/include/xen/page.h b/include/xen/page.h index 031ef22a971e..eaf85fab1263 100644 --- a/include/xen/page.h +++ b/include/xen/page.h @@ -1,180 +1 @@ -#ifndef __XEN_PAGE_H -#define __XEN_PAGE_H - -#include <linux/pfn.h> - -#include <asm/uaccess.h> -#include <asm/pgtable.h> - -#include <xen/features.h> - -#ifdef CONFIG_X86_PAE -/* Xen machine address */ -typedef struct xmaddr { - unsigned long long maddr; -} xmaddr_t; - -/* Xen pseudo-physical address */ -typedef struct xpaddr { - unsigned long long paddr; -} xpaddr_t; -#else -/* Xen machine address */ -typedef struct xmaddr { - unsigned long maddr; -} xmaddr_t; - -/* Xen pseudo-physical address */ -typedef struct xpaddr { - unsigned long paddr; -} xpaddr_t; -#endif - -#define XMADDR(x) ((xmaddr_t) { .maddr = (x) }) -#define XPADDR(x) ((xpaddr_t) { .paddr = (x) }) - -/**** MACHINE <-> PHYSICAL CONVERSION MACROS ****/ -#define INVALID_P2M_ENTRY (~0UL) -#define FOREIGN_FRAME_BIT (1UL<<31) -#define FOREIGN_FRAME(m) ((m) | FOREIGN_FRAME_BIT) - -extern unsigned long *phys_to_machine_mapping; - -static inline unsigned long pfn_to_mfn(unsigned long pfn) -{ - if (xen_feature(XENFEAT_auto_translated_physmap)) - return pfn; - - return phys_to_machine_mapping[(unsigned int)(pfn)] & - ~FOREIGN_FRAME_BIT; -} - -static inline int phys_to_machine_mapping_valid(unsigned long pfn) -{ - if (xen_feature(XENFEAT_auto_translated_physmap)) - return 1; - - return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY); -} - -static inline unsigned long mfn_to_pfn(unsigned long mfn) -{ - unsigned long pfn; - - if (xen_feature(XENFEAT_auto_translated_physmap)) - return mfn; - -#if 0 - if (unlikely((mfn >> machine_to_phys_order) != 0)) - return max_mapnr; -#endif - - pfn = 0; - /* - * The array access can fail (e.g., device space beyond end of RAM). - * In such cases it doesn't matter what we return (we return garbage), - * but we must handle the fault without crashing! - */ - __get_user(pfn, &machine_to_phys_mapping[mfn]); - - return pfn; -} - -static inline xmaddr_t phys_to_machine(xpaddr_t phys) -{ - unsigned offset = phys.paddr & ~PAGE_MASK; - return XMADDR(PFN_PHYS((u64)pfn_to_mfn(PFN_DOWN(phys.paddr))) | offset); -} - -static inline xpaddr_t machine_to_phys(xmaddr_t machine) -{ - unsigned offset = machine.maddr & ~PAGE_MASK; - return XPADDR(PFN_PHYS((u64)mfn_to_pfn(PFN_DOWN(machine.maddr))) | offset); -} - -/* - * We detect special mappings in one of two ways: - * 1. If the MFN is an I/O page then Xen will set the m2p entry - * to be outside our maximum possible pseudophys range. - * 2. If the MFN belongs to a different domain then we will certainly - * not have MFN in our p2m table. Conversely, if the page is ours, - * then we'll have p2m(m2p(MFN))==MFN. - * If we detect a special mapping then it doesn't have a 'struct page'. - * We force !pfn_valid() by returning an out-of-range pointer. - * - * NB. These checks require that, for any MFN that is not in our reservation, - * there is no PFN such that p2m(PFN) == MFN. Otherwise we can get confused if - * we are foreign-mapping the MFN, and the other domain as m2p(MFN) == PFN. - * Yikes! Various places must poke in INVALID_P2M_ENTRY for safety. - * - * NB2. When deliberately mapping foreign pages into the p2m table, you *must* - * use FOREIGN_FRAME(). This will cause pte_pfn() to choke on it, as we - * require. In all the cases we care about, the FOREIGN_FRAME bit is - * masked (e.g., pfn_to_mfn()) so behaviour there is correct. - */ -static inline unsigned long mfn_to_local_pfn(unsigned long mfn) -{ - extern unsigned long max_mapnr; - unsigned long pfn = mfn_to_pfn(mfn); - if ((pfn < max_mapnr) - && !xen_feature(XENFEAT_auto_translated_physmap) - && (phys_to_machine_mapping[pfn] != mfn)) - return max_mapnr; /* force !pfn_valid() */ - return pfn; -} - -static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn) -{ - if (xen_feature(XENFEAT_auto_translated_physmap)) { - BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); - return; - } - phys_to_machine_mapping[pfn] = mfn; -} - -/* VIRT <-> MACHINE conversion */ -#define virt_to_machine(v) (phys_to_machine(XPADDR(__pa(v)))) -#define virt_to_mfn(v) (pfn_to_mfn(PFN_DOWN(__pa(v)))) -#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT)) - -#ifdef CONFIG_X86_PAE -#define pte_mfn(_pte) (((_pte).pte_low >> PAGE_SHIFT) | \ - (((_pte).pte_high & 0xfff) << (32-PAGE_SHIFT))) - -static inline pte_t mfn_pte(unsigned long page_nr, pgprot_t pgprot) -{ - pte_t pte; - - pte.pte_high = (page_nr >> (32 - PAGE_SHIFT)) | - (pgprot_val(pgprot) >> 32); - pte.pte_high &= (__supported_pte_mask >> 32); - pte.pte_low = ((page_nr << PAGE_SHIFT) | pgprot_val(pgprot)); - pte.pte_low &= __supported_pte_mask; - - return pte; -} - -static inline unsigned long long pte_val_ma(pte_t x) -{ - return x.pte; -} -#define pmd_val_ma(v) ((v).pmd) -#define pud_val_ma(v) ((v).pgd.pgd) -#define __pte_ma(x) ((pte_t) { .pte = (x) }) -#define __pmd_ma(x) ((pmd_t) { (x) } ) -#else /* !X86_PAE */ -#define pte_mfn(_pte) ((_pte).pte_low >> PAGE_SHIFT) -#define mfn_pte(pfn, prot) __pte_ma(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) -#define pte_val_ma(x) ((x).pte) -#define pmd_val_ma(v) ((v).pud.pgd.pgd) -#define __pte_ma(x) ((pte_t) { (x) } ) -#endif /* CONFIG_X86_PAE */ - -#define pgd_val_ma(x) ((x).pgd) - - -xmaddr_t arbitrary_virt_to_machine(unsigned long address); -void make_lowmem_page_readonly(void *vaddr); -void make_lowmem_page_readwrite(void *vaddr); - -#endif /* __XEN_PAGE_H */ +#include <asm/xen/page.h> diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h new file mode 100644 index 000000000000..10ddfe0142d0 --- /dev/null +++ b/include/xen/xen-ops.h @@ -0,0 +1,8 @@ +#ifndef INCLUDE_XEN_OPS_H +#define INCLUDE_XEN_OPS_H + +#include <linux/percpu.h> + +DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); + +#endif /* INCLUDE_XEN_OPS_H */ diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index 6f7c290651ae..6369d89c25d5 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -97,6 +97,7 @@ struct xenbus_driver { int (*uevent)(struct xenbus_device *, char **, int, char *, int); struct device_driver driver; int (*read_otherend_details)(struct xenbus_device *dev); + int (*is_ready)(struct xenbus_device *dev); }; static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv) diff --git a/include/xen/xencomm.h b/include/xen/xencomm.h new file mode 100644 index 000000000000..e43b039be112 --- /dev/null +++ b/include/xen/xencomm.h @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corp. 2006 + * + * Authors: Hollis Blanchard <hollisb@us.ibm.com> + * Jerone Young <jyoung5@us.ibm.com> + */ + +#ifndef _LINUX_XENCOMM_H_ +#define _LINUX_XENCOMM_H_ + +#include <xen/interface/xencomm.h> + +#define XENCOMM_MINI_ADDRS 3 +struct xencomm_mini { + struct xencomm_desc _desc; + uint64_t address[XENCOMM_MINI_ADDRS]; +}; + +/* To avoid additionnal virt to phys conversion, an opaque structure is + presented. */ +struct xencomm_handle; + +extern void xencomm_free(struct xencomm_handle *desc); +extern struct xencomm_handle *xencomm_map(void *ptr, unsigned long bytes); +extern struct xencomm_handle *__xencomm_map_no_alloc(void *ptr, + unsigned long bytes, struct xencomm_mini *xc_area); + +#if 0 +#define XENCOMM_MINI_ALIGNED(xc_desc, n) \ + struct xencomm_mini xc_desc ## _base[(n)] \ + __attribute__((__aligned__(sizeof(struct xencomm_mini)))); \ + struct xencomm_mini *xc_desc = &xc_desc ## _base[0]; +#else +/* + * gcc bug workaround: + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660 + * gcc doesn't handle properly stack variable with + * __attribute__((__align__(sizeof(struct xencomm_mini)))) + */ +#define XENCOMM_MINI_ALIGNED(xc_desc, n) \ + unsigned char xc_desc ## _base[((n) + 1 ) * \ + sizeof(struct xencomm_mini)]; \ + struct xencomm_mini *xc_desc = (struct xencomm_mini *) \ + ((unsigned long)xc_desc ## _base + \ + (sizeof(struct xencomm_mini) - \ + ((unsigned long)xc_desc ## _base) % \ + sizeof(struct xencomm_mini))); +#endif +#define xencomm_map_no_alloc(ptr, bytes) \ + ({ XENCOMM_MINI_ALIGNED(xc_desc, 1); \ + __xencomm_map_no_alloc(ptr, bytes, xc_desc); }) + +/* provided by architecture code: */ +extern unsigned long xencomm_vtop(unsigned long vaddr); + +static inline void *xencomm_pa(void *ptr) +{ + return (void *)xencomm_vtop((unsigned long)ptr); +} + +#define xen_guest_handle(hnd) ((hnd).p) + +#endif /* _LINUX_XENCOMM_H_ */ diff --git a/kernel/sched.c b/kernel/sched.c index 0014b03adaca..09ca69b2c17d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -8128,7 +8128,7 @@ void __init sched_init(void) * we use alloc_bootmem(). */ if (alloc_size) { - ptr = (unsigned long)alloc_bootmem_low(alloc_size); + ptr = (unsigned long)alloc_bootmem(alloc_size); #ifdef CONFIG_FAIR_GROUP_SCHED init_task_group.se = (struct sched_entity **)ptr; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 623ef24c2381..754cc0027f2a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -25,6 +25,17 @@ config ENABLE_MUST_CHECK suppress the "warning: ignoring return value of 'foo', declared with attribute warn_unused_result" messages. +config FRAME_WARN + int "Warn for stack frames larger than (needs gcc 4.4)" + range 0 8192 + default 1024 if !64BIT + default 2048 if 64BIT + help + Tell gcc to warn at build time for stack frames larger than this. + Setting this too low will cause a lot of warnings. + Setting it to 0 disables the warning. + Requires gcc 4.4 + config MAGIC_SYSRQ bool "Magic SysRq key" depends on !UML diff --git a/net/can/raw.c b/net/can/raw.c index ead50c7c0d40..201cbfc6b9ec 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -573,7 +573,8 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, int fsize = ro->count * sizeof(struct can_filter); if (len > fsize) len = fsize; - err = copy_to_user(optval, ro->filter, len); + if (copy_to_user(optval, ro->filter, len)) + err = -EFAULT; } else len = 0; release_sock(sk); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index a29b43d0b450..0133b5ebd545 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -323,6 +323,11 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) bytes_remaining -= eeprom.len; } + eeprom.len = userbuf - (useraddr + sizeof(eeprom)); + eeprom.offset -= eeprom.len; + if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) + ret = -EFAULT; + kfree(data); return ret; } diff --git a/net/dccp/probe.c b/net/dccp/probe.c index 6e1df62bd7c9..0bcdc9250279 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -140,7 +140,7 @@ static ssize_t dccpprobe_read(struct file *file, char __user *buf, goto out_free; cnt = kfifo_get(dccpw.fifo, tbuf, len); - error = copy_to_user(buf, tbuf, cnt); + error = copy_to_user(buf, tbuf, cnt) ? -EFAULT : 0; out_free: vfree(tbuf); diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 1c509592574a..5ff0ce6e9d39 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -190,19 +190,18 @@ static ssize_t tcpprobe_read(struct file *file, char __user *buf, width = tcpprobe_sprint(tbuf, sizeof(tbuf)); - if (width < len) + if (cnt + width < len) tcp_probe.tail = (tcp_probe.tail + 1) % bufsize; spin_unlock_bh(&tcp_probe.lock); /* if record greater than space available return partial buffer (so far) */ - if (width >= len) + if (cnt + width >= len) break; - error = copy_to_user(buf + cnt, tbuf, width); - if (error) - break; + if (copy_to_user(buf + cnt, tbuf, width)) + return -EFAULT; cnt += width; } diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 42814a2ec9d7..b2c9becc02e8 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -167,7 +167,7 @@ config IPV6_SIT Tunneling means encapsulating data of one protocol type within another protocol and sending it over a channel that understands the encapsulating protocol. This driver implements encapsulation of IPv6 - into IPv4 packets. This is useful if you want to connect two IPv6 + into IPv4 packets. This is useful if you want to connect to IPv6 networks over an IPv4-only path. Saying M here will produce a module called sit.ko. If unsure, say Y. diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 6193b124cbc7..396f0ea11090 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -971,6 +971,19 @@ static int do_rawv6_setsockopt(struct sock *sk, int level, int optname, switch (optname) { case IPV6_CHECKSUM: + if (inet_sk(sk)->num == IPPROTO_ICMPV6 && + level == IPPROTO_IPV6) { + /* + * RFC3542 tells that IPV6_CHECKSUM socket + * option in the IPPROTO_IPV6 level is not + * allowed on ICMPv6 sockets. + * If you want to set it, use IPPROTO_RAW + * level IPV6_CHECKSUM socket option + * (Linux extension). + */ + return -EINVAL; + } + /* You may get strange result with a positive odd offset; RFC2292bis agrees with me. */ if (val > 0 && (val&1)) @@ -1046,6 +1059,11 @@ static int do_rawv6_getsockopt(struct sock *sk, int level, int optname, switch (optname) { case IPV6_CHECKSUM: + /* + * We allow getsockopt() for IPPROTO_IPV6-level + * IPV6_CHECKSUM socket option on ICMPv6 sockets + * since RFC3542 is silent about it. + */ if (rp->checksum == 0) val = -1; else diff --git a/net/key/af_key.c b/net/key/af_key.c index 81a8e5297ad1..2403a31fe0f6 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2356,7 +2356,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg struct xfrm_selector sel; struct km_event c; struct sadb_x_sec_ctx *sec_ctx; - struct xfrm_sec_ctx *pol_ctx; + struct xfrm_sec_ctx *pol_ctx = NULL; if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1], ext_hdrs[SADB_EXT_ADDRESS_DST-1]) || @@ -2396,8 +2396,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg kfree(uctx); if (err) return err; - } else - pol_ctx = NULL; + } xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN, pol->sadb_x_policy_dir - 1, &sel, pol_ctx, diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 05853159536a..230f9ca2ad6b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1756,8 +1756,8 @@ static int getsockopt(struct socket *sock, else if (len < sizeof(value)) { res = -EINVAL; } - else if ((res = copy_to_user(ov, &value, sizeof(value)))) { - /* couldn't return value */ + else if (copy_to_user(ov, &value, sizeof(value))) { + res = -EFAULT; } else { res = put_user(sizeof(value), ol); diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 67fb4530a6ff..277cfe0b7100 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -27,12 +27,12 @@ ccflags-y := cppflags-y := ldflags-y := -# Read .config if it exist, otherwise ignore +# Read auto.conf if it exists, otherwise ignore -include include/config/auto.conf include scripts/Kbuild.include -# For backward compatibility check that these variables does not change +# For backward compatibility check that these variables do not change save-cflags := $(CFLAGS) # The filename Kbuild has precedence over Makefile @@ -55,7 +55,7 @@ hostprogs-y += $(host-progs) endif endif -# Do not include host rules unles needed +# Do not include host rules unless needed ifneq ($(hostprogs-y)$(hostprogs-m),) include scripts/Makefile.host endif diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean index 2c647107c9cc..6f89fbb56256 100644 --- a/scripts/Makefile.clean +++ b/scripts/Makefile.clean @@ -37,7 +37,7 @@ subdir-ymn := $(sort $(subdir-ym) $(subdir-n) $(subdir-)) subdir-ymn := $(addprefix $(obj)/,$(subdir-ymn)) -# build a list of files to remove, usually releative to the current +# build a list of files to remove, usually relative to the current # directory __clean-files := $(extra-y) $(always) \ diff --git a/scripts/Makefile.host b/scripts/Makefile.host index 6943a7a5bb98..1ac414fd5030 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -3,9 +3,9 @@ # Binaries are used during the compilation of the kernel, for example # to preprocess a data file. # -# Both C and C++ is supported, but preferred language is C for such utilities. +# Both C and C++ are supported, but preferred language is C for such utilities. # -# Samle syntax (see Documentation/kbuild/makefile.txt for reference) +# Sample syntax (see Documentation/kbuild/makefiles.txt for reference) # hostprogs-y := bin2hex # Will compile bin2hex.c and create an executable named bin2hex # @@ -23,10 +23,10 @@ # hostprogs-y := conf # conf-objs := conf.o libkconfig.so # libkconfig-objs := expr.o type.o -# Will create a shared library named libkconfig.so that consist of -# expr.o and type.o (they are both compiled as C code and the object file +# Will create a shared library named libkconfig.so that consists of +# expr.o and type.o (they are both compiled as C code and the object files # are made as position independent code). -# conf.c is compiled as a c program, and conf.o is linked together with +# conf.c is compiled as a C program, and conf.o is linked together with # libkconfig.so as the executable conf. # Note: Shared libraries consisting of C++ files are not supported @@ -61,7 +61,7 @@ host-cobjs := $(filter-out %.so,$(host-cobjs)) host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) # output directory for programs/.o files -# hostprogs-y := tools/build may have been specified. Retreive directory +# hostprogs-y := tools/build may have been specified. Retrieve directory host-objdirs := $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f)))) # directory of .o files from prog-objs notation host-objdirs += $(foreach f,$(host-cmulti), \ diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 2d20640854b7..24b3c8fe6bca 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -42,6 +42,13 @@ _modpost: __modpost include include/config/auto.conf include scripts/Kbuild.include + +ifneq ($(KBUILD_EXTMOD),) +# Include the module's Makefile to find KBUILD_EXTRA_SYMBOLS +include $(if $(wildcard $(KBUILD_EXTMOD)/Kbuild), \ + $(KBUILD_EXTMOD)/Kbuild, $(KBUILD_EXTMOD)/Makefile) +endif + include scripts/Makefile.lib kernelsymfile := $(objtree)/Module.symvers @@ -69,6 +76,7 @@ modpost = scripts/mod/modpost \ $(if $(CONFIG_MODULE_SRCVERSION_ALL),-a,) \ $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \ $(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \ + $(if $(iKBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(EXTRA_SYMBOLS))) \ $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ $(if $(CONFIG_MARKERS),-K $(kernelmarkersfile)) \ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 110cf243fa4e..f8b42ab0724b 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1552,6 +1552,10 @@ static void read_symbols(char *modname) } license = get_modinfo(info.modinfo, info.modinfo_len, "license"); + if (!license && !is_vmlinux(modname)) + fatal("modpost: missing MODULE_LICENSE() in %s\n" + "see include/linux/module.h for " + "more information\n", modname); while (license) { if (license_is_gpl_compatible(license)) mod->gpl_compatible = 1; @@ -2015,6 +2019,11 @@ static void write_markers(const char *fname) write_if_changed(&buf, fname); } +struct ext_sym_list { + struct ext_sym_list *next; + const char *file; +}; + int main(int argc, char **argv) { struct module *mod; @@ -2025,8 +2034,10 @@ int main(int argc, char **argv) char *markers_write = NULL; int opt; int err; + struct ext_sym_list *extsym_iter; + struct ext_sym_list *extsym_start = NULL; - while ((opt = getopt(argc, argv, "i:I:cmsSo:awM:K:")) != -1) { + while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) { switch (opt) { case 'i': kernel_read = optarg; @@ -2038,6 +2049,14 @@ int main(int argc, char **argv) case 'c': cross_build = 1; break; + case 'e': + external_module = 1; + extsym_iter = + NOFAIL(malloc(sizeof(*extsym_iter))); + extsym_iter->next = extsym_start; + extsym_iter->file = optarg; + extsym_start = extsym_iter; + break; case 'm': modversions = 1; break; @@ -2071,6 +2090,12 @@ int main(int argc, char **argv) read_dump(kernel_read, 1); if (module_read) read_dump(module_read, 0); + while (extsym_start) { + read_dump(extsym_start->file, 0); + extsym_iter = extsym_start->next; + free(extsym_start); + extsym_start = extsym_iter; + } while (optind < argc) read_symbols(argv[optind++]); |