diff --git a/Documentation/filesystems/Exporting b/Documentation/filesystems/Exporting new file mode 100644 index 0000000000000000000000000000000000000000..e16492d7c720ed2994889884738a54d5b04b4505 --- /dev/null +++ b/Documentation/filesystems/Exporting @@ -0,0 +1,176 @@ + +Making Filesystems Exportable +============================= + +Most filesystem operations require a dentry (or two) as a starting +point. Local applications have a reference-counted hold on suitable +dentrys via open file descriptors or cwd/root. However remote +applications that access a filesystem via a remote filesystem protocol +such as NFS may not be able to hold such a reference, and so need a +different way to refer to a particular dentry. As the alternative +form of reference needs to be stable across renames, truncates, and +server-reboot (among other things, though these tend to be the most +problematic), there is no simple answer like 'filename'. + +The mechanism discussed here allows each filesystem implementation to +specify how to generate an opaque (out side of the filesystem) byte +string for any dentry, and how to find an appropriate dentry for any +given opaque byte string. +This byte string will be called a "filehandle fragment" as it +corresponds to part of an NFS filehandle. + +A filesystem which supports the mapping between filehandle fragments +and dentrys will be termed "exportable". + + + +Dcache Issues +------------- + +The dcache normally contains a proper prefix of any given filesystem +tree. This means that if any filesystem object is in the dcache, then +all of the ancestors of that filesystem object are also in the dcache. +As normal access is by filename this prefix is created naturally and +maintained easily (by each object maintaining a reference count on +it's parent). + +However when objects are included into the dcache by interpreting a +filehandle fragment, there is no automatic creation of a path prefix +for the object. This leads to two related but distinct features of +the dcache that are not needed for normal filesystem access. + +1/ The dcache must sometimes contain objects that are not part of the + proper prefix. i.e that are not connected to the root. +2/ The dcache must be prepared for a newly found (via ->lookup) directory + to already have a (non-connected) dentry, and must be able to move + that dentry into place (based on the parent and name in the + ->lookup). This is particuarly needed for directories as + it is a dcache invarient that directories only have one dentry. + +To implement these features, the dcache has: + +a/ A dentry flag DCACHE_DISCONNECTED which is set on + and dentry that might not be part of the proper prefix. + This is set when anonymous dentries are created, and cleared when a + dentry is noticed to be a child on a dentry which is in the proper + prefix. + +b/ A per-superblock list "s_anon" of dentries which are the roots of + subtrees that are not in the proper prefix. These dentries, as + well as the proper prefix, need to be released at unmount time. As + these dentries will not be hashed, they are linked together on the + d_hash list_head. + +c/ Helper routines to allocate anonymous dentries, and to help attach + loose directory dentries at lookup time. They are: + d_alloc_anon(inode) will return a dentry for the given inode. + If the inode already has a dentry, one of those is returned. + If it doesn't, a new anonymous (IS_ROOT and + DCACHE_DISCONNECTED) dentry is allocated and attached. + In the case of a directory, care is taken that only one dentry + can ever be attached. + d_splice_alias(inode, dentry) will make sure that there is a + dentry with the same name and parent as the given dentry, and + which refers to the given inode. + If the inode is a directory and already has a dentry, then that + dentry is d_moved over the given dentry. + If the passed dentry gets attached, care is taken that this is + mutually exclusive to a d_alloc_anon operation. + If the passed dentry is used, NULL is returned, else the used + dentry is returned. This corresponds to the calling pattern of + ->lookup. + + +Filesystem Issues +----------------- + +For a filesystem to be exportable it must: + + 1/ provide the filehandle fragment routines described below. + 2/ make sure that d_splice_alias is used rather than d_add + when ->lookup finds an inode for a given parent and name. + Typically the ->lookup routine will end: + if (inode) + return d_splice(inode, dentry); + d_add(dentry, inode); + return NULL; + } + + + + A file system implementation declares that instances of the filesystem +are exportable by setting the s_export_op field in the struct +super_block. This field must point to a "struct export_operations" +struct which could potentially be full of NULLs, though normally at +least get_parent will be set. + + The primary operations are decode_fh and encode_fh. +decode_fh takes a filehandle fragment and tries to find or create a +dentry for the object referred to by the filehandle. +encode_fh takes a dentry and creates a filehandle fragment which can +later be used to find/create a dentry for the same object. + +decode_fh will probably make use of "find_exported_dentry". +This function lives in the "exportfs" module which a filesystem does +not need unless it is being exported. So rather that calling +find_exported_dentry directly, each filesystem should call it through +the find_exported_dentry pointer in it's export_operations table. +This field is set correctly by the exporting agent (e.g. nfsd) when a +filesystem is exported, and before any export operations are called. + +find_exported_dentry needs three support functions from the +filesystem: + get_name. When given a parent dentry and a child dentry, this + should find a name in the directory identified by the parent + dentry, which leads to the object identified by the child dentry. + If no get_name function is supplied, a default implementation + which used vfs_readdir to find potential names, and matches inode + numbers to find the correct match. + + get_parent. When given a dentry for a directory, this should return + a dentry for the parent. Quite possibly the parent dentry will + have been allocated by d_alloc_anon. + The default get_parent function just returns an error so any + filehandle lookup that requires finding a parent will fail. + ->lookup("..") is *not* used as a default as it can leave ".." + entries in the dcache which are too messy to work with. + + get_dentry. When given a opaque datum, this should find the + implied object and create a dentry for it (possibly with + d_alloc_anon). + The opaque datum is whatever is passed down by the decode_fh + function, and is often simply a fragment of the filehandle + fragment. + decode_fh passes two datums through find_exported_dentry. One that + should be used to identify the target object, and one that can be + used to identify the objects parent, should that be necessary. + The default get_dentry function assumes that the datum contains an + inode number and a generation number, and it attempts to get the + inode using "iget" and check it's validity by matching the + generation number. A filesystem should only depend on the default + if iget can safely be used this way. + +If decode_fh and/or encode_fh are left as NULL, then default +implementations are used. These defaults are suitable for ext2 and +extremely similar filesystems (like ext3). + +The default encode_fh creates a filehandle fragment from the inode +number and generation number of the target together with the inode +number and generation number of the parent (if the parent is +required). + +The default decode_fh extract the target and parent datums from the +filehandle assuming the format used by the default encode_fh and +passed them to find_exported_dentry. + + +A filehandle fragment consists of an array of 1 or more 4byte words. +Together with a one byte "type". +The decode_fh routine should not depend on the stated size that is +passed to it. This size may be larger than the original filehandle +generated by encode_fh, in which case it will have been padded with +nuls. Rather, the encode_fh routine should choose a "type" which +indicates the decode_fh how much of the filehandle is valid, and how +it should be interpreted. + + diff --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog index 435a612320c627637ddd384a7ecb3b2d9f292d31..4c4ddd9d2758cd9844c1bcb6bc7e1d86757dbc84 100644 --- a/Documentation/filesystems/devfs/ChangeLog +++ b/Documentation/filesystems/devfs/ChangeLog @@ -1905,3 +1905,10 @@ Changes for patch v209 - Fixed bitfield data type for <devfs_*alloc_devnum> - Made major bitfield type and initialiser 64 bit safe +=============================================================================== +Changes for patch v210 + +- Updated fs/devfs/util.c to fix shift warning on 64 bit machines + Thanks to Anton Blanchard <anton@samba.org> + +- Updated README from master HTML file diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README index 062d2b2aea5f08ca6b4da537efffe39dc2e001ed..3c9d29861e281b3c5d194642fc099da9243d0837 100644 --- a/Documentation/filesystems/devfs/README +++ b/Documentation/filesystems/devfs/README @@ -3,7 +3,7 @@ Devfs (Device File System) FAQ Linux Devfs (Device File System) FAQ Richard Gooch -4-APR-2002 +7-APR-2002 Document languages: @@ -1591,6 +1591,20 @@ mount -t none devfs /dev +Mount by volume LABEL=<label> doesn't work with +devfs + +Most probably you are not mounting devfs onto /dev. What +happens is that if your kernel config has CONFIG_DEVFS_FS=y +then the contents of /proc/partitions will have the devfs +names (such as scsi/host0/bus0/target0/lun0/part1). The +contents of /proc/partitions are used by mount(8) when +mounting by volume label. If devfs is not mounted on /dev, +then mount(8) will fail to find devices. The solution is to +make sure that devfs is mounted on /dev. See above for how to +do that. + + diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 1ac5d3af530e86979018efeb9a69670f150b164e..dd28c5c12e868cf776d3c7e33d042607c02ccb12 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -1,7 +1,7 @@ Changes since 2.5.0: --- -[recommeneded] +[recommended] New helpers: sb_bread(), sb_getblk(), sb_get_hash_table(), set_bh(), sb_set_blocksize() and sb_min_blocksize(). @@ -9,7 +9,7 @@ New helpers: sb_bread(), sb_getblk(), sb_get_hash_table(), set_bh(), Use them. --- -[recommeneded] +[recommended] New methods: ->alloc_inode() and ->destroy_inode(). @@ -123,3 +123,19 @@ went in - and hadn't been documented ;-/). Just remove it from fs_flags ->setattr() is called without BKL now. Caller _always_ holds ->i_sem, so watch for ->i_sem-grabbing code that might be used by your ->setattr(). Callers of notify_change() need ->i_sem now. + +--- +[recommended] + +New super_block field "struct export_operations *s_export_op" for +explicit support for exporting, e.g. via NFS. The structure is fully +documented at its declaration in include/linux/fs.h, and in +Documentation/filesystems/Exporting. + +Briefly it allows for the definition of decode_fh and encode_fh operations +to encode and decode filehandles, and allows the filesystem to use +a standard helper function for decode_fh, and provide file-system specific +support for this helper, particularly get_parent. + +It is planned that this will be required for exporting once the code +settles down a bit. diff --git a/Documentation/usb/dc2xx.txt b/Documentation/usb/dc2xx.txt deleted file mode 100644 index af73377dc585c0e9a5f259d4599112ea137f03bf..0000000000000000000000000000000000000000 --- a/Documentation/usb/dc2xx.txt +++ /dev/null @@ -1,111 +0,0 @@ -14 April 2000 -david-b@pacbell.net - -This is an overview of how to use the "dc2xx" USB driver with certain -digital still cameras from Kodak and other vendors. - - -CAMERAS - -This driver will mostly be used with Kodak DC-2xx series digital still -cameras, but it should be trivial to tell it about several non-Kodak -USB-enabled cameras. - -You'll most likely want to hook it up to recent versions of "gPhoto" -(www.gphoto.org), since version 0.4 and later know how to use it to talk -to Kodak DC-240 and DC-280 cameras over USB. - -In addition the DC-220, DC-260, DC-265, and DC-290 are also recognized. -However, like other cameras using the "Digita OS" (from www.flashpoint.com) -there is no gPhoto support for this camera. There is a python script -for accessing these cameras (see archives of the linux-usb mailing list) -and a "Digita Services" library that can also use this driver. - -The HP PhotoSmart C500 should also work, since it's another Digita camera -with USB support. - - -USB HARDWARE - -Recent kernels have had no particular problems using this driver with -either OHCI or UHCI chipsets, and have worked on the PowerMac platform. - -Note that in some cases changes in BIOS settings may be needed before -your USB works. At least one user has reported a need for SMP-related -settings as well, and some old hardware may not handle USB correctly. - - -SETUP - -Configure in the DC2XX USB driver, and have it in your kernel. It works -as a module, or compiled in directly. - -Create at least one device, perhaps like this (both read and write): - - # mknod -m 0660 /dev/usb/dc2xx0 c 180 80 - # mknod -m 0660 /dev/usb/dc2xx1 c 180 81 - ... - -NOTE: you would normally configure PAM so that the user logged in at -the console is granted ownership of these devices. console.perms(5) -explains how to do this. - -The driver supports multiple device nodes. The USB framework supports -a maximum of sixteen device nodes (up to minor device number 96). - -When you plug in one camera, it will use the first device node (dc2xx0 -in the example above). A second camera will use the second device node, -and so on. - - -SANITY TESTING - -First: if you've got /proc support, make sure that the driver has hooked -itself up correctly. - - - You should see an entry in /proc/bus/usb/drivers for "dc2xx", - if you enabled USB /proc support and correctly mounted the - usbdevfs on /proc/bus/usb. - -Second: when you connect your camera to the computer, does it get recognized -by the driver? (Make sure the camera is powered on!) - - - if you've got /proc/bus/usb/devices, you should see an entry - something like this. The "ProdID" may be different if you didn't - plug in a DC-240, as may the strings presented, but "Driver=dc2xx" - had better be there. - - T: Lev=01 Prnt=00 Port=00 Cnt=01 Dev#= 1 Spd=12 MxCh= 0 - D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 - P: Vendor=040a ProdID=0120 Rev= 1.08 - S: Manufacturer=Eastman Kodak Company - S: Product=KODAK DC240 Zoom Digital Camera - C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA - I: If#= 0 Alt= 0 #EPs= 2 Cls=00(>ifc ) Sub=00 Prot=00 Driver=dc2xx - E: Ad=01(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms - E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms - - - see if "dmesg" output tells you that you plugged in your camera. - - Manufacturer: Eastman Kodak Company - Product: KODAK DC240 Zoom Digital Camera - dc2xx.c: USB Camera #0 connected - -Third: (optional) can you use gPhoto to talk to the camera? - - - When you configure your camera, tell it to use "/dev/usb/dc2xx0" - (or whatever name you used). Right now, gPhoto emits a diagnostic - message (non-GUI) saying that it since it didn't act like a TTY, - it's assuming it's got a USB connection. - - - With the camera turned on, get the "camera summary". It'll - talk to the camera -- and tell you you're using USB. - -If you got that far, you should be able to use everything fine. - - -ADDITIONAL INFORMATION - -You may find that you need more driver-specific information, which is -currently accessible through a link from http://www.linux-usb.org/ -along with other Linux USB resources. diff --git a/Makefile b/Makefile index 8c6a31c5aed4bf3eeb8ca85a952f244077c425ab..15094513154378c842ce1c3975240dff59b2f0c7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 8 +SUBLEVEL = 9 EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/Rules.make b/Rules.make index 05858896c186197f7f01c7313419f31270e5fd53..763dbfe29a4cd888e5a590984e3eb02e3073ffd0 100644 --- a/Rules.make +++ b/Rules.make @@ -126,8 +126,10 @@ endif # for make >= 3.78 the following is cleaner: # multi-used := $(foreach m,$(obj-y) $(obj-m), $(if $($(basename $(m))-objs), $(m))) -multi-used-y := $(sort $(foreach m,$(obj-y),$(patsubst %,$(m),$($(basename $(m))-objs)))) -multi-used-m := $(sort $(foreach m,$(obj-m),$(patsubst %,$(m),$($(basename $(m))-objs)))) +__obj-y = $(filter-out export.o,$(obj-y)) +__obj-m = $(filter-out export.o,$(obj-m)) +multi-used-y := $(sort $(foreach m,$(__obj-y),$(patsubst %,$(m),$($(basename $(m))-objs)))) +multi-used-m := $(sort $(foreach m,$(__obj-m),$(patsubst %,$(m),$($(basename $(m))-objs)))) ld-multi-used-y := $(filter-out $(list-multi),$(multi-used-y)) ld-multi-used-m := $(filter-out $(list-multi),$(multi-used-m)) ld-multi-objs-y := $(foreach m, $(ld-multi-used-y), $($(basename $(m))-objs)) diff --git a/arch/alpha/math-emu/Makefile b/arch/alpha/math-emu/Makefile index 4486b79e338d5cbae1b3e26f329343e877a33885..3f451cbbf9464bfec6162906f59876caa2707867 100644 --- a/arch/alpha/math-emu/Makefile +++ b/arch/alpha/math-emu/Makefile @@ -11,11 +11,8 @@ obj-y := math.o qrnnd.o else -list-multi := math-emu.o math-emu-objs := math.o qrnnd.o obj-m := math-emu.o -math-emu.o: $(math-emu-objs) - $(LD) -r -o $@ $(math-emu-objs) endif diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index 66e786486cca15d488545103d61f4289e42e8c85..e3c46966b3d8432ccde6b6c298ae33b036d53e9a 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -1,9 +1,6 @@ # # Makefile for the linux kernel. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). USE_STANDARD_AS_RULE := true @@ -115,7 +112,6 @@ export-objs += yopy.o obj-$(CONFIG_LEDS) += $(led-y) # SA1110 USB client support -list-multi += sa1100usb_core.o sa1100usb_core-objs := usb_ctl.o usb_ep0.o usb_recv.o usb_send.o obj-$(CONFIG_SA1100_USB) += sa1100usb_core.o obj-$(CONFIG_SA1100_USB_NETLINK) += usb-eth.o @@ -125,7 +121,3 @@ obj-$(CONFIG_SA1100_USB_CHAR) += usb-char.o obj-$(CONFIG_PM) += pm.o sleep.o include $(TOPDIR)/Rules.make - -sa1100usb_core.o: $(sa1100usb_core-objs) - $(LD) -r -o $@ $(sa1100usb_core-objs) - diff --git a/arch/arm/nwfpe/Makefile b/arch/arm/nwfpe/Makefile index bcac0cf0e1ab580b0b9f934fcc10939d5209b935..4eb26eb03cd12c1be32a6c056b21185e4890709e 100644 --- a/arch/arm/nwfpe/Makefile +++ b/arch/arm/nwfpe/Makefile @@ -1,6 +1,4 @@ # -# linux/arch/arm/nwfpe/Makefile -# # Copyright (C) 1998, 1999, 2001 Philip Blundell # @@ -12,8 +10,6 @@ obj-y := obj-m := obj-n := -list-multi := nwfpe.o - obj-$(CONFIG_FPE_NWFPE) += nwfpe.o nwfpe-objs := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \ @@ -27,6 +23,3 @@ nwfpe-objs += entry.o endif include $(TOPDIR)/Rules.make - -nwfpe.o: $(nwfpe-objs) - $(LD) -r -o $@ $(nwfpe-objs) diff --git a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c index 79a617d3890b4062cc8c2628464b27e239c950fb..1aa748ef4f38c0ce3a99b3f1846c919479481182 100644 --- a/arch/cris/drivers/ide.c +++ b/arch/cris/drivers/ide.c @@ -272,13 +272,17 @@ init_e100_ide (void) printk("ide: ETRAX 100LX built-in ATA DMA controller\n"); /* first initialize the channel interface data */ - + for(h = 0; h < MAX_HWIFS; h++) { struct ata_channel *hwif = &ide_hwifs[h]; + hwif->chipset = ide_etrax100; hwif->tuneproc = &tune_e100_ide; hwif->dmaproc = &e100_dmaproc; - hwif->ideproc = &e100_ideproc; + hwif->ata_read = e100_ide_input_data; + hwif->ata_write = e100_ide_input_data; + hwif->atapi_read = e100_atapi_read; + hwif->atapi_write = e100_atapi_write; } /* actually reset and configure the etrax100 ide/ata interface */ @@ -375,12 +379,12 @@ static etrax_dma_descr mydescr; * so if an odd bytecount is specified, be sure that there's at least one * extra byte allocated for the buffer. */ -static void -e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +static void +e100_atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + D(printk("atapi_read, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); if(bytecount & 1) { @@ -454,12 +458,12 @@ e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount #endif } -static void -e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +static void +e100_atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + D(printk("atapi_write, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); if(bytecount & 1) { @@ -544,7 +548,7 @@ e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecoun static void e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { - e100_atapi_input_bytes(drive, buffer, wcount << 2); + e100_atapi_read(drive, buffer, wcount << 2); } /* @@ -553,7 +557,7 @@ e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) static void e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { - e100_atapi_output_bytes(drive, buffer, wcount << 2); + e100_atapi_write(drive, buffer, wcount << 2); } /* @@ -570,11 +574,11 @@ e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, case ideproc_ide_output_data: e100_ide_input_data(drive, buffer, length); break; - case ideproc_atapi_input_bytes: - e100_atapi_input_bytes(drive, buffer, length); + case ideproc_atapi_read: + e100_atapi_read(drive, buffer, length); break; - case ideproc_atapi_output_bytes: - e100_atapi_output_bytes(drive, buffer, length); + case ideproc_atapi_write: + e100_atapi_write(drive, buffer, length); break; default: printk("e100_ideproc: unsupported func %d!\n", func); diff --git a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c index c1ab98c51944a8c0f3da7d1f13aff2b7fd4d2240..086693ebbb8dff2c21d9fb7656b35d8175a2499e 100644 --- a/arch/i386/kernel/bluesmoke.c +++ b/arch/i386/kernel/bluesmoke.c @@ -76,11 +76,11 @@ static void __init intel_init_thermal(struct cpuinfo_x86 *c) unsigned int cpu = smp_processor_id(); /* Thermal monitoring */ - if (!test_bit(X86_FEATURE_ACPI, &c->x86_capability)) + if (!test_bit(X86_FEATURE_ACPI, c->x86_capability)) return; /* -ENODEV */ /* Clock modulation */ - if (!test_bit(X86_FEATURE_ACC, &c->x86_capability)) + if (!test_bit(X86_FEATURE_ACC, c->x86_capability)) return; /* -ENODEV */ rdmsr(MSR_IA32_MISC_ENABLE, l, h); diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 40da258a929d3fc0e06512d0147f182744ac3578..61d4fadedc7ad96af0b9f971987e2f4e763711ac 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -338,7 +338,7 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l return -EINVAL; } if ((len >> PAGE_SHIFT) > num_physpages) { - printk(KERN_ERR "microcode: too much data (max %d pages)\n", num_physpages); + printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); return -EINVAL; } down_write(µcode_rwsem); diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 12b0f33d4d8f966f5d87290c4a1d877bb18c1397..5ad6cca0e23b89004ad460f1a94bb539236b5d12 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -38,6 +38,8 @@ int smp_found_config; int apic_version [MAX_APICS]; int mp_bus_id_to_type [MAX_MP_BUSSES]; int mp_bus_id_to_node [MAX_MP_BUSSES]; +int mp_bus_id_to_local [MAX_MP_BUSSES]; +int quad_local_to_mp_bus_id [NR_CPUS/4][4]; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; int mp_current_pci_id; @@ -237,13 +239,17 @@ void __init MP_processor_info (struct mpc_config_processor *m) static void __init MP_bus_info (struct mpc_config_bus *m) { char str[7]; + int quad; memcpy(str, m->mpc_bustype, 6); str[6] = 0; if (clustered_apic_mode) { - mp_bus_id_to_node[m->mpc_busid] = translation_table[mpc_record]->trans_quad; - printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, mp_bus_id_to_node[m->mpc_busid]); + quad = translation_table[mpc_record]->trans_quad; + mp_bus_id_to_node[m->mpc_busid] = quad; + mp_bus_id_to_local[m->mpc_busid] = translation_table[mpc_record]->trans_local; + quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid; + printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, quad); } else { Dprintk("Bus #%d is %s\n", m->mpc_busid, str); } @@ -320,13 +326,14 @@ static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m) static void __init MP_translation_info (struct mpc_config_translation *m) { - printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, - m->trans_quad, m->trans_global, m->trans_local); + printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, m->trans_quad, m->trans_global, m->trans_local); if (mpc_record >= MAX_MPC_ENTRY) printk("MAX_MPC_ENTRY exceeded!\n"); else translation_table[mpc_record] = m; /* stash this for later */ + if (m->trans_quad+1 > numnodes) + numnodes = m->trans_quad+1; } /* @@ -492,10 +499,6 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) } ++mpc_record; } - if (clustered_apic_mode && nr_ioapics > 2) { - /* don't initialise IO apics on secondary quads */ - nr_ioapics = 2; - } if (!num_processors) printk(KERN_ERR "SMP mptable: no processors registered!\n"); return num_processors; diff --git a/arch/ia64/Config.help b/arch/ia64/Config.help index 740f05e04ce3e632387671b0082dd0158d8f343f..cefa2e3edb843b3a2088fd2cae67a02228c8ffa5 100644 --- a/arch/ia64/Config.help +++ b/arch/ia64/Config.help @@ -414,12 +414,18 @@ CONFIG_IA64_GENERIC HP-simulator For the HP simulator (<http://software.hp.com/ia64linux/>). + HP-zx1 For HP zx1 platforms. SN1-simulator For the SGI SN1 simulator. DIG-compliant For DIG ("Developer's Interface Guide") compliant - system. + systems. If you don't know what to do, choose "generic". +CONFIG_IA64_HP_ZX1 + Build a kernel that runs on HP zx1-based systems. This adds support + for the zx1 IOMMU and makes root bus bridges appear in PCI config space + (required for zx1 agpgart support). + CONFIG_IA64_PAGE_SIZE_4KB This lets you select the page size of the kernel. For best IA-64 performance, a page size of 8KB or 16KB is recommended. For best @@ -440,6 +446,32 @@ CONFIG_ITANIUM_BSTEP_SPECIFIC with a B-step CPU. You have a B-step CPU if the "revision" field in /proc/cpuinfo has a value in the range from 1 to 4. +CONFIG_IA64_SGI_AUTOTEST + Build a kernel used for hardware validation. If you include the + keyword "autotest" on the boot command line, the kernel does NOT boot. + Instead, it starts all cpus and runs cache coherency tests instead. + + If unsure, say N. + +CONFIG_IA64_SGI_SN_DEBUG + Turns on extra debugging code in the SGI SN (Scalable NUMA) platform + for IA64. Unless you are debugging problems on an SGI SN IA64 box, + say N. + +CONFIG_IA64_SGI_SN_SIM + If you are compiling a kernel that will run under SGI's IA64 + simulator (Medusa) then say Y, otherwise say N. + +CONFIG_SERIAL_SGI_L1_PROTOCOL + Uses protocol mode instead of raw mode for the level 1 console on the + SGI SN (Scalable NUMA) platform for IA64. If you are compiling for + an SGI SN box then Y is the recommended value, otherwise say N. + +CONFIG_PCIBA + IRIX PCIBA-inspired user mode PCI interface for the SGI SN (Scalable + NUMA) platform for IA64. Unless you are compiling a kernel for an + SGI SN IA64 box, say N. + CONFIG_IA64_MCA Say Y here to enable machine check support for IA-64. If you're unsure, answer Y. diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index 757b58a0f2bcac9ef2b77cae1906b7c6c8b80467..c8f96ce640357326487f8e3fb5b2cfe976b4cc59 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -22,7 +22,7 @@ CFLAGS := $(CFLAGS) -pipe $(EXTRA) -ffixed-r13 -mfixed-range=f10-f15,f32-f127 \ # -ffunction-sections CFLAGS_KERNEL := -mconstant-gp -GCC_VERSION=$(shell $(CROSS_COMPILE)$(HOSTCC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.') +GCC_VERSION=$(shell $(CC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.') ifneq ($(GCC_VERSION),2) CFLAGS += -frename-registers --param max-inline-insns=2000 @@ -33,16 +33,11 @@ ifeq ($(CONFIG_ITANIUM_BSTEP_SPECIFIC),y) endif ifdef CONFIG_IA64_GENERIC - CORE_FILES := arch/$(ARCH)/hp/hp.a \ - arch/$(ARCH)/sn/sn.o \ - arch/$(ARCH)/dig/dig.a \ - arch/$(ARCH)/sn/io/sgiio.o \ + CORE_FILES := arch/$(ARCH)/hp/hp.o \ + arch/$(ARCH)/dig/dig.a \ $(CORE_FILES) SUBDIRS := arch/$(ARCH)/hp \ - arch/$(ARCH)/sn/sn1 \ - arch/$(ARCH)/sn \ arch/$(ARCH)/dig \ - arch/$(ARCH)/sn/io \ $(SUBDIRS) else # !GENERIC @@ -50,7 +45,16 @@ else # !GENERIC ifdef CONFIG_IA64_HP_SIM SUBDIRS := arch/$(ARCH)/hp \ $(SUBDIRS) - CORE_FILES := arch/$(ARCH)/hp/hp.a \ + CORE_FILES := arch/$(ARCH)/hp/hp.o \ + $(CORE_FILES) +endif + +ifdef CONFIG_IA64_HP_ZX1 + SUBDIRS := arch/$(ARCH)/hp \ + arch/$(ARCH)/dig \ + $(SUBDIRS) + CORE_FILES := arch/$(ARCH)/hp/hp.o \ + arch/$(ARCH)/dig/dig.a \ $(CORE_FILES) endif diff --git a/arch/ia64/config.in b/arch/ia64/config.in index 51043891a3edd528d9bb09adc40bc472b94e6aee..071dddd4aaade0b34a61aef3ec81679614204b8a 100644 --- a/arch/ia64/config.in +++ b/arch/ia64/config.in @@ -22,6 +22,7 @@ choice 'IA-64 system type' \ "generic CONFIG_IA64_GENERIC \ DIG-compliant CONFIG_IA64_DIG \ HP-simulator CONFIG_IA64_HP_SIM \ + HP-zx1 CONFIG_IA64_HP_ZX1 \ SGI-SN1 CONFIG_IA64_SGI_SN1 \ SGI-SN2 CONFIG_IA64_SGI_SN2" generic @@ -56,7 +57,7 @@ if [ "$CONFIG_MCKINLEY" = "y" ]; then fi fi -if [ "$CONFIG_IA64_DIG" = "y" ]; then +if [ "$CONFIG_IA64_GENERIC" = "y" ] || [ "$CONFIG_IA64_DIG" = "y" ] || [ "$CONFIG_IA64_HP_ZX1" = "y" ]; then bool ' Enable IA-64 Machine Check Abort' CONFIG_IA64_MCA define_bool CONFIG_PM y fi diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig index 50a7d883cbe31eb7bfdcf4f0932250d46c9827bf..e55eb2f7a61f63a4fbac5d7d689e222a8d6b0011 100644 --- a/arch/ia64/defconfig +++ b/arch/ia64/defconfig @@ -23,7 +23,7 @@ CONFIG_MODVERSIONS=y # CONFIG_KMOD is not set # -# General setup +# Processor type and features # CONFIG_IA64=y # CONFIG_ISA is not set @@ -32,21 +32,22 @@ CONFIG_IA64=y # CONFIG_SBUS is not set CONFIG_RWSEM_GENERIC_SPINLOCK=y # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_ACPI=y -CONFIG_ACPI_EFI=y -CONFIG_ACPI_INTERPRETER=y -CONFIG_ACPI_KERNEL_CONFIG=y CONFIG_ITANIUM=y # CONFIG_MCKINLEY is not set # CONFIG_IA64_GENERIC is not set CONFIG_IA64_DIG=y # CONFIG_IA64_HP_SIM is not set +# CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN1 is not set # CONFIG_IA64_SGI_SN2 is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y # CONFIG_IA64_PAGE_SIZE_64KB is not set +CONFIG_ACPI=y +CONFIG_ACPI_EFI=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_KERNEL_CONFIG=y CONFIG_IA64_BRL_EMU=y # CONFIG_ITANIUM_BSTEP_SPECIFIC is not set CONFIG_IA64_L1_CACHE_SHIFT=6 @@ -60,15 +61,23 @@ CONFIG_IA64_PALINFO=y CONFIG_EFI_VARS=y CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set -# CONFIG_ACPI_DEBUG is not set -CONFIG_ACPI_BUSMGR=y -CONFIG_ACPI_SYS=y -CONFIG_ACPI_CPU=y + +# +# ACPI Support +# +CONFIG_ACPI=y +CONFIG_ACPI_EFI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_BUS=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_SYSTEM=y CONFIG_ACPI_BUTTON=y -CONFIG_ACPI_AC=y -CONFIG_ACPI_EC=y -CONFIG_ACPI_CMBATT=y -CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_FAN=m +CONFIG_ACPI_PROCESSOR=m +CONFIG_ACPI_THERMAL=m +# CONFIG_ACPI_DEBUG is not set CONFIG_PCI=y CONFIG_PCI_NAMES=y # CONFIG_HOTPLUG is not set @@ -231,15 +240,13 @@ CONFIG_BLK_DEV_IDEDMA=y # CONFIG_BLK_DEV_ALI15X3 is not set # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set -# CONFIG_AMD74XX_OVERRIDE is not set # CONFIG_BLK_DEV_CMD64X is not set # CONFIG_BLK_DEV_CY82C693 is not set # CONFIG_BLK_DEV_CS5530 is not set # CONFIG_BLK_DEV_HPT34X is not set # CONFIG_HPT34X_AUTODMA is not set # CONFIG_BLK_DEV_HPT366 is not set -CONFIG_BLK_DEV_PIIX=y -# CONFIG_PIIX_TUNING is not set +# CONFIG_BLK_DEV_PIIX is not set # CONFIG_BLK_DEV_NS87415 is not set # CONFIG_BLK_DEV_OPTI621 is not set # CONFIG_BLK_DEV_PDC_ADMA is not set @@ -254,7 +261,6 @@ CONFIG_BLK_DEV_PIIX=y # CONFIG_IDEDMA_IVB is not set # CONFIG_IDEDMA_AUTO is not set # CONFIG_DMA_NONPCI is not set -CONFIG_BLK_DEV_IDE_MODES=y # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set @@ -691,8 +697,6 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set -CONFIG_EFI_PARTITION=y -# CONFIG_DEVFS_GUID is not set # CONFIG_LDM_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set @@ -726,6 +730,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_CODEPAGE_949 is not set # CONFIG_NLS_CODEPAGE_874 is not set # CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set # CONFIG_NLS_ISO8859_1 is not set # CONFIG_NLS_ISO8859_2 is not set @@ -814,8 +819,9 @@ CONFIG_USB_UHCI=m # USB Device Class drivers # # CONFIG_USB_AUDIO is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set # CONFIG_USB_STORAGE is not set # CONFIG_USB_STORAGE_DEBUG is not set # CONFIG_USB_STORAGE_DATAFAB is not set @@ -825,13 +831,12 @@ CONFIG_USB_UHCI=m # CONFIG_USB_STORAGE_HP8200e is not set # CONFIG_USB_STORAGE_SDDR09 is not set # CONFIG_USB_STORAGE_JUMPSHOT is not set -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set # # USB Human Interface Devices (HID) # CONFIG_USB_HID=m +CONFIG_USB_HIDINPUT=y CONFIG_USB_HIDDEV=y CONFIG_USB_KBD=m CONFIG_USB_MOUSE=m @@ -849,23 +854,24 @@ CONFIG_USB_MOUSE=m # # USB Multimedia devices # +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_DSBR is not set # CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set # CONFIG_USB_OV511 is not set # CONFIG_USB_PWC is not set # CONFIG_USB_SE401 is not set # CONFIG_USB_STV680 is not set -# CONFIG_USB_VICAM is not set -# CONFIG_USB_DSBR is not set -# CONFIG_USB_DABUSB is not set -# CONFIG_USB_KONICAWC is not set # # USB Network adaptors # -# CONFIG_USB_PEGASUS is not set -# CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set # CONFIG_USB_CDCETHER is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set # CONFIG_USB_USBNET is not set # @@ -897,9 +903,11 @@ CONFIG_USB_MOUSE=m # CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set -# CONFIG_USB_SERIAL_MCT_U232 is not set # CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set # CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SAFE_PADDED is not set # CONFIG_USB_SERIAL_CYBERJACK is not set # CONFIG_USB_SERIAL_XIRCOM is not set # CONFIG_USB_SERIAL_OMNINET is not set @@ -907,8 +915,10 @@ CONFIG_USB_MOUSE=m # # USB Miscellaneous drivers # -# CONFIG_USB_RIO500 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set # CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set # # Library routines @@ -936,11 +946,3 @@ CONFIG_IA64_EARLY_PRINTK=y # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_IA64_DEBUG_CMPXCHG is not set # CONFIG_IA64_DEBUG_IRQ is not set -CONFIG_KDB=y -# CONFIG_KDB_MODULES is not set -# CONFIG_KDB_OFF is not set - -# -# Load all symbols for debugging is required for KDB -# -CONFIG_KALLSYMS=y diff --git a/arch/ia64/dig/setup.c b/arch/ia64/dig/setup.c index c75a357c5a84f34488e4df7d813eef3cb9a51cbd..6fdf2480a206f7955e9455c9fcdc443376baf9cd 100644 --- a/arch/ia64/dig/setup.c +++ b/arch/ia64/dig/setup.c @@ -33,9 +33,6 @@ * is sufficient (the IDE driver will autodetect the drive geometry). */ char drive_info[4*16]; -extern int pcat_compat; - -unsigned char aux_device_present = 0xaa; /* XXX remove this when legacy I/O is gone */ void __init dig_setup (char **cmdline_p) @@ -85,16 +82,4 @@ dig_setup (char **cmdline_p) void __init dig_irq_init (void) { - if (pcat_compat) { - /* - * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support - * enabled. - */ - printk("%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__); - outb(0xff, 0xA1); - outb(0xff, 0x21); - } else { - printk("%s: System doesn't have PC-AT compatible dual-8259 setup. " - "Nothing to be done\n", __FUNCTION__); - } } diff --git a/arch/ia64/hp/Config.in b/arch/ia64/hp/Config.in new file mode 100644 index 0000000000000000000000000000000000000000..e8cda10b0380539fe5a5596c459283bc545885e9 --- /dev/null +++ b/arch/ia64/hp/Config.in @@ -0,0 +1,9 @@ +mainmenu_option next_comment +comment 'HP Simulator drivers' + +bool 'Simulated Ethernet ' CONFIG_HP_SIMETH +bool 'Simulated serial driver support' CONFIG_HP_SIMSERIAL +if [ "$CONFIG_SCSI" != "n" ]; then + bool 'Simulated SCSI disk' CONFIG_HP_SIMSCSI +fi +endmenu diff --git a/arch/ia64/hp/Makefile b/arch/ia64/hp/Makefile index 642a03c2d488fb316c4a0c84cd70e56097a96524..2dd12a0e1f81c7d109215acd662fca2458c5ed57 100644 --- a/arch/ia64/hp/Makefile +++ b/arch/ia64/hp/Makefile @@ -1,23 +1,15 @@ -# -# ia64/platform/hp/Makefile -# -# Copyright (C) 2002 Hewlett-Packard Co. -# David Mosberger-Tang <davidm@hpl.hp.com> -# Copyright (C) 1999 Silicon Graphics, Inc. -# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) -# +# arch/ia64/hp/Makefile +# Copyright (c) 2002 Matthew Wilcox for Hewlett Packard -all: hp.a +ALL_SUB_DIRS := sim zx1 common -O_TARGET := hp.a +O_TARGET := hp.o -obj-y := hpsim_console.o hpsim_irq.o hpsim_setup.o -obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o +subdir-$(CONFIG_IA64_GENERIC) += $(ALL_SUB_DIRS) +subdir-$(CONFIG_IA64_HP_SIM) += sim +subdir-$(CONFIG_IA64_HP_ZX1) += zx1 common -obj-$(CONFIG_SIMETH) += simeth.o -obj-$(CONFIG_SIM_SERIAL) += simserial.o -obj-$(CONFIG_SCSI_SIM) += simscsi.o - -clean:: +SUB_DIRS := $(subdir-y) +obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) include $(TOPDIR)/Rules.make diff --git a/arch/ia64/hp/common/Makefile b/arch/ia64/hp/common/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..678bbf3d09b88471138c1ddf2d52c4da1b85135b --- /dev/null +++ b/arch/ia64/hp/common/Makefile @@ -0,0 +1,28 @@ +# +# ia64/platform/hp/common/Makefile +# +# Copyright (C) 2002 Hewlett Packard +# Copyright (C) Alex Williamson (alex_williamson@hp.com) +# + +O_TARGET := common.o + +export-objs := sba_iommu.o + +obj-y := sba_iommu.o + +include $(TOPDIR)/Rules.make +# +# ia64/platform/hp/common/Makefile +# +# Copyright (C) 2002 Hewlett Packard +# Copyright (C) Alex Williamson (alex_williamson@hp.com) +# + +O_TARGET := common.o + +export-objs := sba_iommu.o + +obj-y := sba_iommu.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c new file mode 100644 index 0000000000000000000000000000000000000000..5a0ec8bd925ea56c68327d838b151c4856c19d44 --- /dev/null +++ b/arch/ia64/hp/common/sba_iommu.c @@ -0,0 +1,3700 @@ +/* +** IA64 System Bus Adapter (SBA) I/O MMU manager +** +** (c) Copyright 2002 Alex Williamson +** (c) Copyright 2002 Hewlett-Packard Company +** +** Portions (c) 2000 Grant Grundler (from parisc I/O MMU code) +** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code) +** +** 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 module initializes the IOC (I/O Controller) found on HP +** McKinley machines and their successors. +** +*/ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> + +#include <asm/delay.h> /* ia64_get_itc() */ +#include <asm/io.h> +#include <asm/page.h> /* PAGE_OFFSET */ +#include <asm/efi.h> + + +#define DRIVER_NAME "SBA" + +#ifndef CONFIG_IA64_HP_PROTO +#define ALLOW_IOV_BYPASS +#endif +#define ENABLE_MARK_CLEAN +/* +** The number of debug flags is a clue - this code is fragile. +*/ +#undef DEBUG_SBA_INIT +#undef DEBUG_SBA_RUN +#undef DEBUG_SBA_RUN_SG +#undef DEBUG_SBA_RESOURCE +#undef ASSERT_PDIR_SANITY +#undef DEBUG_LARGE_SG_ENTRIES +#undef DEBUG_BYPASS + +#define SBA_INLINE __inline__ +/* #define SBA_INLINE */ + +#ifdef DEBUG_SBA_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_SBA_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_SBA_RUN_SG +#define DBG_RUN_SG(x...) printk(x) +#else +#define DBG_RUN_SG(x...) +#endif + + +#ifdef DEBUG_SBA_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +#ifdef DEBUG_BYPASS +#define DBG_BYPASS(x...) printk(x) +#else +#define DBG_BYPASS(x...) +#endif + +#ifdef ASSERT_PDIR_SANITY +#define ASSERT(expr) \ + if(!(expr)) { \ + printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \ + panic(#expr); \ + } +#else +#define ASSERT(expr) +#endif + +#define KB(x) ((x) * 1024) +#define MB(x) (KB (KB (x))) +#define GB(x) (MB (KB (x))) + +/* +** The number of pdir entries to "free" before issueing +** a read to PCOM register to flush out PCOM writes. +** Interacts with allocation granularity (ie 4 or 8 entries +** allocated and free'd/purged at a time might make this +** less interesting). +*/ +#define DELAYED_RESOURCE_CNT 16 + +#define DEFAULT_DMA_HINT_REG 0 + +#define ZX1_FUNC_ID_VALUE ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP) +#define ZX1_MC_ID ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP) + +#define SBA_FUNC_ID 0x0000 /* function id */ +#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ + +#define SBA_FUNC_SIZE 0x10000 /* SBA configuration function reg set */ + +unsigned int __initdata zx1_func_offsets[] = {0x1000, 0x4000, 0x8000, + 0x9000, 0xa000, -1}; + +#define SBA_IOC_OFFSET 0x1000 + +#define MAX_IOC 1 /* we only have 1 for now*/ + +#define IOC_IBASE 0x300 /* IO TLB */ +#define IOC_IMASK 0x308 +#define IOC_PCOM 0x310 +#define IOC_TCNFG 0x318 +#define IOC_PDIR_BASE 0x320 + +#define IOC_IOVA_SPACE_BASE 0x40000000 /* IOVA ranges start at 1GB */ + +/* +** IOC supports 4/8/16/64KB page sizes (see TCNFG register) +** It's safer (avoid memory corruption) to keep DMA page mappings +** equivalently sized to VM PAGE_SIZE. +** +** We really can't avoid generating a new mapping for each +** page since the Virtual Coherence Index has to be generated +** and updated for each page. +** +** IOVP_SIZE could only be greater than PAGE_SIZE if we are +** confident the drivers really only touch the next physical +** page iff that driver instance owns it. +*/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +struct ioc { + unsigned long ioc_hpa; /* I/O MMU base address */ + char *res_map; /* resource map, bit == pdir entry */ + u64 *pdir_base; /* physical base address */ + unsigned long ibase; /* pdir IOV Space base */ + unsigned long imask; /* pdir IOV Space mask */ + + unsigned long *res_hint; /* next avail IOVP - circular search */ + spinlock_t res_lock; + unsigned long hint_mask_pdir; /* bits used for DMA hints */ + unsigned int res_bitshift; /* from the RIGHT! */ + unsigned int res_size; /* size of resource map in bytes */ + unsigned int hint_shift_pdir; + unsigned long dma_mask; +#if DELAYED_RESOURCE_CNT > 0 + int saved_cnt; + struct sba_dma_pair { + dma_addr_t iova; + size_t size; + } saved[DELAYED_RESOURCE_CNT]; +#endif + +#ifdef CONFIG_PROC_FS +#define SBA_SEARCH_SAMPLE 0x100 + unsigned long avg_search[SBA_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#ifdef ALLOW_IOV_BYPASS + unsigned long msingle_bypass; + unsigned long usingle_bypass; + unsigned long msg_bypass; +#endif +#endif + + /* STUFF We don't need in performance path */ + unsigned int pdir_size; /* in bytes, determined by IOV Space size */ +}; + +struct sba_device { + struct sba_device *next; /* list of SBA's in system */ + const char *name; + unsigned long sba_hpa; /* base address */ + spinlock_t sba_lock; + unsigned int flags; /* state/functionality enabled */ + unsigned int hw_rev; /* HW revision of chip */ + + unsigned int num_ioc; /* number of on-board IOC's */ + struct ioc ioc[MAX_IOC]; +}; + + +static struct sba_device *sba_list; +static int sba_count; +static int reserve_sba_gart = 1; + +#define sba_sg_iova(sg) (sg->address) +#define sba_sg_len(sg) (sg->length) +#define sba_sg_buffer(sg) (sg->orig_address) + +/* REVISIT - fix me for multiple SBAs/IOCs */ +#define GET_IOC(dev) (sba_list->ioc) +#define SBA_SET_AGP(sba_dev) (sba_dev->flags |= 0x1) +#define SBA_GET_AGP(sba_dev) (sba_dev->flags & 0x1) + +/* +** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up +** (or rather not merge) DMA's into managable chunks. +** On parisc, this is more of the software/tuning constraint +** rather than the HW. I/O MMU allocation alogorithms can be +** faster with smaller size is (to some degree). +*/ +#define DMA_CHUNK_SIZE (BITS_PER_LONG*PAGE_SIZE) + +/* Looks nice and keeps the compiler happy */ +#define SBA_DEV(d) ((struct sba_device *) (d)) + +#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) + +/************************************ +** SBA register read and write support +** +** BE WARNED: register writes are posted. +** (ie follow writes which must reach HW with a read) +** +*/ +#define READ_REG(addr) __raw_readq(addr) +#define WRITE_REG(val, addr) __raw_writeq(val, addr) + +#ifdef DEBUG_SBA_INIT + +/** + * sba_dump_tlb - debugging only - print IOMMU operating parameters + * @hpa: base address of the IOMMU + * + * Print the size/location of the IO MMU PDIR. + */ +static void +sba_dump_tlb(char *hpa) +{ + DBG_INIT("IO TLB at 0x%p\n", (void *)hpa); + DBG_INIT("IOC_IBASE : %016lx\n", READ_REG(hpa+IOC_IBASE)); + DBG_INIT("IOC_IMASK : %016lx\n", READ_REG(hpa+IOC_IMASK)); + DBG_INIT("IOC_TCNFG : %016lx\n", READ_REG(hpa+IOC_TCNFG)); + DBG_INIT("IOC_PDIR_BASE: %016lx\n", READ_REG(hpa+IOC_PDIR_BASE)); + DBG_INIT("\n"); +} +#endif + + +#ifdef ASSERT_PDIR_SANITY + +/** + * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * @pide: pdir index. + * + * Print one entry of the IO MMU PDIR in human readable form. + */ +static void +sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) +{ + /* start printing from lowest pde in rval */ + u64 *ptr = &(ioc->pdir_base[pide & ~(BITS_PER_LONG - 1)]); + unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); + uint rcnt; + + /* printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n", */ + printk("SBA: %s rp %p bit %d rval 0x%lx\n", + msg, rptr, pide & (BITS_PER_LONG - 1), *rptr); + + rcnt = 0; + while (rcnt < BITS_PER_LONG) { + printk("%s %2d %p %016Lx\n", + (rcnt == (pide & (BITS_PER_LONG - 1))) + ? " -->" : " ", + rcnt, ptr, *ptr ); + rcnt++; + ptr++; + } + printk("%s", msg); +} + + +/** + * sba_check_pdir - debugging only - consistency checker + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * + * Verify the resource map and pdir state is consistent + */ +static int +sba_check_pdir(struct ioc *ioc, char *msg) +{ + u64 *rptr_end = (u64 *) &(ioc->res_map[ioc->res_size]); + u64 *rptr = (u64 *) ioc->res_map; /* resource map ptr */ + u64 *pptr = ioc->pdir_base; /* pdir ptr */ + uint pide = 0; + + while (rptr < rptr_end) { + u64 rval; + int rcnt; /* number of bits we might check */ + + rval = *rptr; + rcnt = 64; + + while (rcnt) { + /* Get last byte and highest bit from that */ + u32 pde = ((u32)((*pptr >> (63)) & 0x1)); + if ((rval & 0x1) ^ pde) + { + /* + ** BUMMER! -- res_map != pdir -- + ** Dump rval and matching pdir entries + */ + sba_dump_pdir_entry(ioc, msg, pide); + return(1); + } + rcnt--; + rval >>= 1; /* try the next bit */ + pptr++; + pide++; + } + rptr++; /* look at next word of res_map */ + } + /* It'd be nice if we always got here :^) */ + return 0; +} + + +/** + * sba_dump_sg - debugging only - print Scatter-Gather list + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: head of the SG list + * @nents: number of entries in SG list + * + * print the SG list so we can verify it's correct by hand. + */ +static void +sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + while (nents-- > 0) { + printk(" %d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), + sba_sg_len(startsg), + sba_sg_buffer(startsg)); + startsg++; + } +} +static void +sba_check_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + struct scatterlist *the_sg = startsg; + int the_nents = nents; + + while (the_nents-- > 0) { + if (sba_sg_buffer(the_sg) == 0x0UL) + sba_dump_sg(NULL, startsg, nents); + the_sg++; + } +} + +#endif /* ASSERT_PDIR_SANITY */ + + + + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +***************************************************************/ +#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */ + +/* Convert from IOVP to IOVA and vice versa. */ +#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir))) +#define SBA_IOVP(ioc,iova) (((iova) & ioc->hint_mask_pdir) & ~(ioc->ibase)) + +/* FIXME : review these macros to verify correctness and usage */ +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) + +#define RESMAP_MASK(n) ~(~0UL << (n)) +#define RESMAP_IDX_MASK (sizeof(unsigned long) - 1) + + +/** + * sba_search_bitmap - find free space in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @bits_wanted: number of entries we need. + * + * Find consecutive free bits in resource bitmap. + * Each bit represents one entry in the IO Pdir. + * Cool perf optimization: search for log2(size) bits at a time. + */ +static SBA_INLINE unsigned long +sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted) +{ + unsigned long *res_ptr = ioc->res_hint; + unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]); + unsigned long pide = ~0UL; + + ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0); + ASSERT(res_ptr < res_end); + if (bits_wanted > (BITS_PER_LONG/2)) { + /* Search word at a time - no mask needed */ + for(; res_ptr < res_end; ++res_ptr) { + if (*res_ptr == 0) { + *res_ptr = RESMAP_MASK(bits_wanted); + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + break; + } + } + /* point to the next word on next pass */ + res_ptr++; + ioc->res_bitshift = 0; + } else { + /* + ** Search the resource bit map on well-aligned values. + ** "o" is the alignment. + ** We need the alignment to invalidate I/O TLB using + ** SBA HW features in the unmap path. + */ + unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT); + uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o); + unsigned long mask; + + if (bitshiftcnt >= BITS_PER_LONG) { + bitshiftcnt = 0; + res_ptr++; + } + mask = RESMAP_MASK(bits_wanted) << bitshiftcnt; + + DBG_RES("%s() o %ld %p", __FUNCTION__, o, res_ptr); + while(res_ptr < res_end) + { + DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr); + ASSERT(0 != mask); + if(0 == ((*res_ptr) & mask)) { + *res_ptr |= mask; /* mark resources busy! */ + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + pide += bitshiftcnt; + break; + } + mask <<= o; + bitshiftcnt += o; + if (0 == mask) { + mask = RESMAP_MASK(bits_wanted); + bitshiftcnt=0; + res_ptr++; + } + } + /* look in the same word on the next pass */ + ioc->res_bitshift = bitshiftcnt + bits_wanted; + } + + /* wrapped ? */ + if (res_end <= res_ptr) { + ioc->res_hint = (unsigned long *) ioc->res_map; + ioc->res_bitshift = 0; + } else { + ioc->res_hint = res_ptr; + } + return (pide); +} + + +/** + * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @size: number of bytes to create a mapping for + * + * Given a size, find consecutive unmarked and then mark those bits in the + * resource bit map. + */ +static int +sba_alloc_range(struct ioc *ioc, size_t size) +{ + unsigned int pages_needed = size >> IOVP_SHIFT; +#ifdef CONFIG_PROC_FS + unsigned long itc_start = ia64_get_itc(); +#endif + unsigned long pide; + + ASSERT(pages_needed); + ASSERT((pages_needed * IOVP_SIZE) <= DMA_CHUNK_SIZE); + ASSERT(pages_needed <= BITS_PER_LONG); + ASSERT(0 == (size & ~IOVP_MASK)); + + /* + ** "seek and ye shall find"...praying never hurts either... + */ + + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) { + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) + panic(__FILE__ ": I/O MMU @ %lx is out of mapping resources\n", ioc->ioc_hpa); + } + +#ifdef ASSERT_PDIR_SANITY + /* verify the first enable bit is clear */ + if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) { + sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide); + } +#endif + + DBG_RES("%s(%x) %d -> %lx hint %x/%x\n", + __FUNCTION__, size, pages_needed, pide, + (uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map), + ioc->res_bitshift ); + +#ifdef CONFIG_PROC_FS + { + unsigned long itc_end = ia64_get_itc(); + unsigned long tmp = itc_end - itc_start; + /* check for roll over */ + itc_start = (itc_end < itc_start) ? -(tmp) : (tmp); + } + ioc->avg_search[ioc->avg_idx++] = itc_start; + ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1; + + ioc->used_pages += pages_needed; +#endif + + return (pide); +} + + +/** + * sba_free_range - unmark bits in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO virtual address which was previously allocated. + * @size: number of bytes to create a mapping for + * + * clear bits in the ioc's resource map + */ +static SBA_INLINE void +sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size) +{ + unsigned long iovp = SBA_IOVP(ioc, iova); + unsigned int pide = PDIR_INDEX(iovp); + unsigned int ridx = pide >> 3; /* convert bit to byte address */ + unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]); + + int bits_not_wanted = size >> IOVP_SHIFT; + + /* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */ + unsigned long m = RESMAP_MASK(bits_not_wanted) << (pide & (BITS_PER_LONG - 1)); + + DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n", + __FUNCTION__, (uint) iova, size, + bits_not_wanted, m, pide, res_ptr, *res_ptr); + +#ifdef CONFIG_PROC_FS + ioc->used_pages -= bits_not_wanted; +#endif + + ASSERT(m != 0); + ASSERT(bits_not_wanted); + ASSERT((bits_not_wanted * IOVP_SIZE) <= DMA_CHUNK_SIZE); + ASSERT(bits_not_wanted <= BITS_PER_LONG); + ASSERT((*res_ptr & m) == m); /* verify same bits are set */ + *res_ptr &= ~m; +} + + +/************************************************************** +* +* "Dynamic DMA Mapping" support (aka "Coherent I/O") +* +***************************************************************/ + +#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir) + + +/** + * sba_io_pdir_entry - fill in one IO PDIR entry + * @pdir_ptr: pointer to IO PDIR entry + * @vba: Virtual CPU address of buffer to map + * + * SBA Mapping Routine + * + * Given a virtual address (vba, arg1) sba_io_pdir_entry() + * loads the I/O PDIR entry pointed to by pdir_ptr (arg0). + * Each IO Pdir entry consists of 8 bytes as shown below + * (LSB == bit 0): + * + * 63 40 11 7 0 + * +-+---------------------+----------------------------------+----+--------+ + * |V| U | PPN[39:12] | U | FF | + * +-+---------------------+----------------------------------+----+--------+ + * + * V == Valid Bit + * U == Unused + * PPN == Physical Page Number + * + * The physical address fields are filled with the results of virt_to_phys() + * on the vba. + */ + +#if 1 +#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL) +#else +void SBA_INLINE +sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba) +{ + *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL); +} +#endif + +#ifdef ENABLE_MARK_CLEAN +/** + * Since DMA is i-cache coherent, any (complete) pages that were written via + * DMA can be marked as "clean" so that update_mmu_cache() doesn't have to + * flush them when they get mapped into an executable vm-area. + */ +static void +mark_clean (void *addr, size_t size) +{ + unsigned long pg_addr, end; + + pg_addr = PAGE_ALIGN((unsigned long) addr); + end = (unsigned long) addr + size; + while (pg_addr + PAGE_SIZE <= end) { + struct page *page = virt_to_page(pg_addr); + set_bit(PG_arch_1, &page->flags); + pg_addr += PAGE_SIZE; + } +} +#endif + +/** + * sba_mark_invalid - invalidate one or more IO PDIR entries + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO Virtual Address mapped earlier + * @byte_cnt: number of bytes this mapping covers. + * + * Marking the IO PDIR entry(ies) as Invalid and invalidate + * corresponding IO TLB entry. The PCOM (Purge Command Register) + * is to purge stale entries in the IO TLB when unmapping entries. + * + * The PCOM register supports purging of multiple pages, with a minium + * of 1 page and a maximum of 2GB. Hardware requires the address be + * aligned to the size of the range being purged. The size of the range + * must be a power of 2. The "Cool perf optimization" in the + * allocation routine helps keep that true. + */ +static SBA_INLINE void +sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32) SBA_IOVP(ioc,iova); + + int off = PDIR_INDEX(iovp); + + /* Must be non-zero and rounded up */ + ASSERT(byte_cnt > 0); + ASSERT(0 == (byte_cnt & ~IOVP_MASK)); + +#ifdef ASSERT_PDIR_SANITY + /* Assert first pdir entry is set */ + if (!(ioc->pdir_base[off] >> 60)) { + sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp)); + } +#endif + + if (byte_cnt <= IOVP_SIZE) + { + ASSERT(off < ioc->pdir_size); + + iovp |= IOVP_SHIFT; /* set "size" field for PCOM */ + + /* + ** clear I/O PDIR entry "valid" bit + ** Do NOT clear the rest - save it for debugging. + ** We should only clear bits that have previously + ** been enabled. + */ + ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + } else { + u32 t = get_order(byte_cnt) + PAGE_SHIFT; + + iovp |= t; + ASSERT(t <= 31); /* 2GB! Max value of "size" field */ + + do { + /* verify this pdir entry is enabled */ + ASSERT(ioc->pdir_base[off] >> 63); + /* clear I/O Pdir entry "valid" bit first */ + ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + off++; + byte_cnt -= IOVP_SIZE; + } while (byte_cnt > 0); + } + + WRITE_REG(iovp, ioc->ioc_hpa+IOC_PCOM); +} + +/** + * sba_map_single - map one buffer and return IOVA for DMA + * @dev: instance of PCI owned by the driver that's asking. + * @addr: driver buffer to map. + * @size: number of bytes to map in driver buffer. + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +dma_addr_t +sba_map_single(struct pci_dev *dev, void *addr, size_t size, int direction) +{ + struct ioc *ioc; + unsigned long flags; + dma_addr_t iovp; + dma_addr_t offset; + u64 *pdir_start; + int pide; +#ifdef ALLOW_IOV_BYPASS + unsigned long pci_addr = virt_to_phys(addr); +#endif + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + /* + ** Check if the PCI device can DMA to ptr... if so, just return ptr + */ + if ((pci_addr & ~dev->dma_mask) == 0) { + /* + ** Device is bit capable of DMA'ing to the buffer... + ** just return the PCI address of ptr + */ +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->msingle_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n", + dev->dma_mask, pci_addr); + return pci_addr; + } +#endif + + ASSERT(size > 0); + ASSERT(size <= DMA_CHUNK_SIZE); + + /* save offset bits */ + offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK; + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + ~IOVP_MASK) & IOVP_MASK; + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_single()")) + panic("Sanity check failed"); +#endif + +#ifdef CONFIG_PROC_FS + ioc->msingle_calls++; + ioc->msingle_pages += size >> IOVP_SHIFT; +#endif + pide = sba_alloc_range(ioc, size); + iovp = (dma_addr_t) pide << IOVP_SHIFT; + + DBG_RUN("%s() 0x%p -> 0x%lx\n", + __FUNCTION__, addr, (long) iovp | offset); + + pdir_start = &(ioc->pdir_base[pide]); + + while (size > 0) { + ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */ + sba_io_pdir_entry(pdir_start, (unsigned long) addr); + + DBG_RUN(" pdir 0x%p %lx\n", pdir_start, *pdir_start); + + addr += IOVP_SIZE; + size -= IOVP_SIZE; + pdir_start++; + } + /* form complete address */ +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check after sba_map_single()"); +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG); +} + +/** + * sba_unmap_single - unmap one IOVA and free resources + * @dev: instance of PCI owned by the driver that's asking. + * @iova: IOVA of driver buffer previously mapped. + * @size: number of bytes mapped in driver buffer. + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +void sba_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size, + int direction) +{ + struct ioc *ioc; +#if DELAYED_RESOURCE_CNT > 0 + struct sba_dma_pair *d; +#endif + unsigned long flags; + dma_addr_t offset; + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + if ((iova & ioc->imask) != ioc->ibase) { + /* + ** Address does not fall w/in IOVA, must be bypassing + */ +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->usingle_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + DBG_BYPASS("sba_unmap_single() bypass addr: 0x%lx\n", iova); + +#ifdef ENABLE_MARK_CLEAN + if (direction == PCI_DMA_FROMDEVICE) { + mark_clean(phys_to_virt(iova), size); + } +#endif + return; + } +#endif + offset = iova & ~IOVP_MASK; + + DBG_RUN("%s() iovp 0x%lx/%x\n", + __FUNCTION__, (long) iova, size); + + iova ^= offset; /* clear offset bits */ + size += offset; + size = ROUNDUP(size, IOVP_SIZE); + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef CONFIG_PROC_FS + ioc->usingle_calls++; + ioc->usingle_pages += size >> IOVP_SHIFT; +#endif + +#if DELAYED_RESOURCE_CNT > 0 + d = &(ioc->saved[ioc->saved_cnt]); + d->iova = iova; + d->size = size; + if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) { + int cnt = ioc->saved_cnt; + while (cnt--) { + sba_mark_invalid(ioc, d->iova, d->size); + sba_free_range(ioc, d->iova, d->size); + d--; + } + ioc->saved_cnt = 0; + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ + } +#else /* DELAYED_RESOURCE_CNT == 0 */ + sba_mark_invalid(ioc, iova, size); + sba_free_range(ioc, iova, size); + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ +#endif /* DELAYED_RESOURCE_CNT == 0 */ +#ifdef ENABLE_MARK_CLEAN + if (direction == PCI_DMA_FROMDEVICE) { + u32 iovp = (u32) SBA_IOVP(ioc,iova); + int off = PDIR_INDEX(iovp); + void *addr; + + if (size <= IOVP_SIZE) { + addr = phys_to_virt(ioc->pdir_base[off] & + ~0xE000000000000FFFULL); + mark_clean(addr, size); + } else { + size_t byte_cnt = size; + + do { + addr = phys_to_virt(ioc->pdir_base[off] & + ~0xE000000000000FFFULL); + mark_clean(addr, min(byte_cnt, IOVP_SIZE)); + off++; + byte_cnt -= IOVP_SIZE; + + } while (byte_cnt > 0); + } + } +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + + /* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support. + ** For Astro based systems this isn't a big deal WRT performance. + ** As long as 2.4 kernels copyin/copyout data from/to userspace, + ** we don't need the syncdma. The issue here is I/O MMU cachelines + ** are *not* coherent in all cases. May be hwrev dependent. + ** Need to investigate more. + asm volatile("syncdma"); + */ +} + + +/** + * sba_alloc_consistent - allocate/map shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @dma_handle: IOVA of new buffer. + * + * See Documentation/DMA-mapping.txt + */ +void * +sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ret; + + if (!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return 0; + } + + ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = sba_map_single(hwdev, ret, size, 0); + } + + return ret; +} + + +/** + * sba_free_consistent - free/unmap shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @vaddr: virtual address IOVA of "consistent" buffer. + * @dma_handler: IO virtual address of "consistent" buffer. + * + * See Documentation/DMA-mapping.txt + */ +void sba_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + sba_unmap_single(hwdev, dma_handle, size, 0); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +/* +** Since 0 is a valid pdir_base index value, can't use that +** to determine if a value is valid or not. Use a flag to indicate +** the SG list entry contains a valid pdir index. +*/ +#define PIDE_FLAG 0x1UL + +#ifdef DEBUG_LARGE_SG_ENTRIES +int dump_run_sg = 0; +#endif + + +/** + * sba_fill_pdir - write allocated SG entries into IO PDIR + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: list of IOVA/size pairs + * @nents: number of entries in startsg list + * + * Take preprocessed SG list and write corresponding entries + * in the IO PDIR. + */ + +static SBA_INLINE int +sba_fill_pdir( + struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ + int n_mappings = 0; + u64 *pdirp = 0; + unsigned long dma_offset = 0; + + dma_sg--; + while (nents-- > 0) { + int cnt = sba_sg_len(startsg); + sba_sg_len(startsg) = 0; + +#ifdef DEBUG_LARGE_SG_ENTRIES + if (dump_run_sg) + printk(" %2d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), cnt, + sba_sg_buffer(startsg) + ); +#else + DBG_RUN_SG(" %d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), cnt, + sba_sg_buffer(startsg) + ); +#endif + /* + ** Look for the start of a new DMA stream + */ + if ((u64)sba_sg_iova(startsg) & PIDE_FLAG) { + u32 pide = (u64)sba_sg_iova(startsg) & ~PIDE_FLAG; + dma_offset = (unsigned long) pide & ~IOVP_MASK; + sba_sg_iova(startsg) = 0; + dma_sg++; + sba_sg_iova(dma_sg) = (char *)(pide | ioc->ibase); + pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]); + n_mappings++; + } + + /* + ** Look for a VCONTIG chunk + */ + if (cnt) { + unsigned long vaddr = (unsigned long) sba_sg_buffer(startsg); + ASSERT(pdirp); + + /* Since multiple Vcontig blocks could make up + ** one DMA stream, *add* cnt to dma_len. + */ + sba_sg_len(dma_sg) += cnt; + cnt += dma_offset; + dma_offset=0; /* only want offset on first chunk */ + cnt = ROUNDUP(cnt, IOVP_SIZE); +#ifdef CONFIG_PROC_FS + ioc->msg_pages += cnt >> IOVP_SHIFT; +#endif + do { + sba_io_pdir_entry(pdirp, vaddr); + vaddr += IOVP_SIZE; + cnt -= IOVP_SIZE; + pdirp++; + } while (cnt > 0); + } + startsg++; + } +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = 0; +#endif + return(n_mappings); +} + + +/* +** Two address ranges are DMA contiguous *iff* "end of prev" and +** "start of next" are both on a page boundry. +** +** (shift left is a quick trick to mask off upper bits) +*/ +#define DMA_CONTIG(__X, __Y) \ + (((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL) + + +/** + * sba_coalesce_chunks - preprocess the SG list + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: list of IOVA/size pairs + * @nents: number of entries in startsg list + * + * First pass is to walk the SG list and determine where the breaks are + * in the DMA stream. Allocates PDIR entries but does not fill them. + * Returns the number of DMA chunks. + * + * Doing the fill seperate from the coalescing/allocation keeps the + * code simpler. Future enhancement could make one pass through + * the sglist do both. + */ +static SBA_INLINE int +sba_coalesce_chunks( struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + struct scatterlist *vcontig_sg; /* VCONTIG chunk head */ + unsigned long vcontig_len; /* len of VCONTIG chunk */ + unsigned long vcontig_end; + struct scatterlist *dma_sg; /* next DMA stream head */ + unsigned long dma_offset, dma_len; /* start/len of DMA stream */ + int n_mappings = 0; + + while (nents > 0) { + unsigned long vaddr = (unsigned long) (startsg->address); + + /* + ** Prepare for first/next DMA stream + */ + dma_sg = vcontig_sg = startsg; + dma_len = vcontig_len = vcontig_end = sba_sg_len(startsg); + vcontig_end += vaddr; + dma_offset = vaddr & ~IOVP_MASK; + + /* PARANOID: clear entries */ + sba_sg_buffer(startsg) = sba_sg_iova(startsg); + sba_sg_iova(startsg) = 0; + sba_sg_len(startsg) = 0; + + /* + ** This loop terminates one iteration "early" since + ** it's always looking one "ahead". + */ + while (--nents > 0) { + unsigned long vaddr; /* tmp */ + + startsg++; + + /* catch brokenness in SCSI layer */ + ASSERT(startsg->length <= DMA_CHUNK_SIZE); + + /* + ** First make sure current dma stream won't + ** exceed DMA_CHUNK_SIZE if we coalesce the + ** next entry. + */ + if (((dma_len + dma_offset + startsg->length + ~IOVP_MASK) & IOVP_MASK) > DMA_CHUNK_SIZE) + break; + + /* + ** Then look for virtually contiguous blocks. + ** + ** append the next transaction? + */ + vaddr = (unsigned long) sba_sg_iova(startsg); + if (vcontig_end == vaddr) + { + vcontig_len += sba_sg_len(startsg); + vcontig_end += sba_sg_len(startsg); + dma_len += sba_sg_len(startsg); + sba_sg_buffer(startsg) = (char *)vaddr; + sba_sg_iova(startsg) = 0; + sba_sg_len(startsg) = 0; + continue; + } + +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = (vcontig_len > IOVP_SIZE); +#endif + + /* + ** Not virtually contigous. + ** Terminate prev chunk. + ** Start a new chunk. + ** + ** Once we start a new VCONTIG chunk, dma_offset + ** can't change. And we need the offset from the first + ** chunk - not the last one. Ergo Successive chunks + ** must start on page boundaries and dove tail + ** with it's predecessor. + */ + sba_sg_len(vcontig_sg) = vcontig_len; + + vcontig_sg = startsg; + vcontig_len = sba_sg_len(startsg); + + /* + ** 3) do the entries end/start on page boundaries? + ** Don't update vcontig_end until we've checked. + */ + if (DMA_CONTIG(vcontig_end, vaddr)) + { + vcontig_end = vcontig_len + vaddr; + dma_len += vcontig_len; + sba_sg_buffer(startsg) = (char *)vaddr; + sba_sg_iova(startsg) = 0; + continue; + } else { + break; + } + } + + /* + ** End of DMA Stream + ** Terminate last VCONTIG block. + ** Allocate space for DMA stream. + */ + sba_sg_len(vcontig_sg) = vcontig_len; + dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK; + ASSERT(dma_len <= DMA_CHUNK_SIZE); + sba_sg_iova(dma_sg) = (char *) (PIDE_FLAG + | (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT) + | dma_offset); + n_mappings++; + } + + return n_mappings; +} + + +/** + * sba_map_sg - map Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +int sba_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, + int direction) +{ + struct ioc *ioc; + int coalesced, filled = 0; + unsigned long flags; +#ifdef ALLOW_IOV_BYPASS + struct scatterlist *sg; +#endif + + DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents); + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + if (dev->dma_mask >= ioc->dma_mask) { + for (sg = sglist ; filled < nents ; filled++, sg++){ + sba_sg_buffer(sg) = sba_sg_iova(sg); + sba_sg_iova(sg) = (char *)virt_to_phys(sba_sg_buffer(sg)); + } +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->msg_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + return filled; + } +#endif + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sba_sg_buffer(sglist) = sba_sg_iova(sglist); + sba_sg_iova(sglist) = (char *)sba_map_single(dev, + sba_sg_buffer(sglist), + sba_sg_len(sglist), direction); +#ifdef CONFIG_PROC_FS + /* + ** Should probably do some stats counting, but trying to + ** be precise quickly starts wasting CPU time. + */ +#endif + return 1; + } + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check before sba_map_sg()"); + } +#endif + +#ifdef CONFIG_PROC_FS + ioc->msg_calls++; +#endif + + /* + ** First coalesce the chunks and allocate I/O pdir space + ** + ** If this is one DMA stream, we can properly map using the + ** correct virtual address associated with each DMA page. + ** w/o this association, we wouldn't have coherent DMA! + ** Access to the virtual address is what forces a two pass algorithm. + */ + coalesced = sba_coalesce_chunks(ioc, sglist, nents); + + /* + ** Program the I/O Pdir + ** + ** map the virtual addresses to the I/O Pdir + ** o dma_address will contain the pdir index + ** o dma_len will contain the number of bytes to map + ** o address contains the virtual address. + */ + filled = sba_fill_pdir(ioc, sglist, nents); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check after sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check after sba_map_sg()\n"); + } +#endif + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + ASSERT(coalesced == filled); + DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled); + + return filled; +} + + +/** + * sba_unmap_sg - unmap Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +void sba_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, + int direction) +{ + struct ioc *ioc; +#ifdef ASSERT_PDIR_SANITY + unsigned long flags; +#endif + + DBG_RUN_SG("%s() START %d entries, %p,%x\n", + __FUNCTION__, nents, sba_sg_buffer(sglist), sglist->length); + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef CONFIG_PROC_FS + ioc->usg_calls++; +#endif + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check before sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + + while (sba_sg_len(sglist) && nents--) { + + sba_unmap_single(dev, (dma_addr_t)sba_sg_iova(sglist), + sba_sg_len(sglist), direction); +#ifdef CONFIG_PROC_FS + /* + ** This leaves inconsistent data in the stats, but we can't + ** tell which sg lists were mapped by map_single and which + ** were coalesced to a single entry. The stats are fun, + ** but speed is more important. + */ + ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT; +#endif + ++sglist; + } + + DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__, nents); + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check after sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + +} + +unsigned long +sba_dma_address (struct scatterlist *sg) +{ + return ((unsigned long)sba_sg_iova(sg)); +} + +/************************************************************** +* +* Initialization and claim +* +***************************************************************/ + + +static void +sba_ioc_init(struct sba_device *sba_dev, struct ioc *ioc, int ioc_num) +{ + u32 iova_space_size, iova_space_mask; + void * pdir_base; + int pdir_size, iov_order, tcnfg; + + /* + ** Firmware programs the maximum IOV space size into the imask reg + */ + iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1; +#ifdef CONFIG_IA64_HP_PROTO + if (!iova_space_size) + iova_space_size = GB(1); +#endif + + /* + ** iov_order is always based on a 1GB IOVA space since we want to + ** turn on the other half for AGP GART. + */ + iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT)); + ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + + DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits) PDIR size 0x%0x\n", + __FUNCTION__, ioc->ioc_hpa, iova_space_size>>20, + iov_order + PAGE_SHIFT, ioc->pdir_size); + + /* FIXME : DMA HINTs not used */ + ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; + ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); + + ioc->pdir_base = + pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); + if (NULL == pdir_base) + { + panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); + } + memset(pdir_base, 0, pdir_size); + + DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n", + __FUNCTION__, pdir_base, pdir_size, + ioc->hint_shift_pdir, ioc->hint_mask_pdir); + + ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); + WRITE_REG(virt_to_phys(pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); + + DBG_INIT(" base %p\n", pdir_base); + + /* build IMASK for IOC and Elroy */ + iova_space_mask = 0xffffffff; + iova_space_mask <<= (iov_order + PAGE_SHIFT); + +#ifdef CONFIG_IA64_HP_PROTO + /* + ** REVISIT - this is a kludge, but we won't be supporting anything but + ** zx1 2.0 or greater for real. When fw is in shape, ibase will + ** be preprogrammed w/ the IOVA hole base and imask will give us + ** the size. + */ + if ((sba_dev->hw_rev & 0xFF) < 0x20) { + DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0. This device will not be supported in the future.\n", __FUNCTION__); + ioc->ibase = 0x0; + } else +#endif + ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL; + + ioc->imask = iova_space_mask; /* save it */ + + DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", + __FUNCTION__, ioc->ibase, ioc->imask); + + /* + ** FIXME: Hint registers are programmed with default hint + ** values during boot, so hints should be sane even if we + ** can't reprogram them the way drivers want. + */ + + WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK); + + /* + ** Setting the upper bits makes checking for bypass addresses + ** a little faster later on. + */ + ioc->imask |= 0xFFFFFFFF00000000UL; + + /* Set I/O PDIR Page size to system page size */ + switch (PAGE_SHIFT) { + case 12: /* 4K */ + tcnfg = 0; + break; + case 13: /* 8K */ + tcnfg = 1; + break; + case 14: /* 16K */ + tcnfg = 2; + break; + case 16: /* 64K */ + tcnfg = 3; + break; + } + WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG); + + /* + ** Program the IOC's ibase and enable IOVA translation + ** Bit zero == enable bit. + */ + WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE); + + /* + ** Clear I/O TLB of any possible entries. + ** (Yes. This is a bit paranoid...but so what) + */ + WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM); + + /* + ** If an AGP device is present, only use half of the IOV space + ** for PCI DMA. Unfortunately we can't know ahead of time + ** whether GART support will actually be used, for now we + ** can just key on an AGP device found in the system. + ** We program the next pdir index after we stop w/ a key for + ** the GART code to handshake on. + */ + if (SBA_GET_AGP(sba_dev)) { + DBG_INIT("%s() AGP Device found, reserving 512MB for GART support\n", __FUNCTION__); + ioc->pdir_size /= 2; + ((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL; + } + + DBG_INIT("%s() DONE\n", __FUNCTION__); +} + + + +/************************************************************************** +** +** SBA initialization code (HW and SW) +** +** o identify SBA chip itself +** o FIXME: initialize DMA hints for reasonable defaults +** +**************************************************************************/ + +static void +sba_hw_init(struct sba_device *sba_dev) +{ + int i; + int num_ioc; + u64 dma_mask; + u32 func_id; + + /* + ** Identify the SBA so we can set the dma_mask. We can make a virtual + ** dma_mask of the memory subsystem such that devices not implmenting + ** a full 64bit mask might still be able to bypass efficiently. + */ + func_id = READ_REG(sba_dev->sba_hpa + SBA_FUNC_ID); + + if (func_id == ZX1_FUNC_ID_VALUE) { + dma_mask = 0xFFFFFFFFFFUL; + } else { + dma_mask = 0xFFFFFFFFFFFFFFFFUL; + } + + DBG_INIT("%s(): ioc->dma_mask == 0x%lx\n", __FUNCTION__, dma_mask); + + /* + ** Leaving in the multiple ioc code from parisc for the future, + ** currently there are no muli-ioc mckinley sbas + */ + sba_dev->ioc[0].ioc_hpa = SBA_IOC_OFFSET; + num_ioc = 1; + + sba_dev->num_ioc = num_ioc; + for (i = 0; i < num_ioc; i++) { + sba_dev->ioc[i].dma_mask = dma_mask; + sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa; + sba_ioc_init(sba_dev, &(sba_dev->ioc[i]), i); + } +} + +static void +sba_common_init(struct sba_device *sba_dev) +{ + int i; + + /* add this one to the head of the list (order doesn't matter) + ** This will be useful for debugging - especially if we get coredumps + */ + sba_dev->next = sba_list; + sba_list = sba_dev; + sba_count++; + + for(i=0; i< sba_dev->num_ioc; i++) { + int res_size; + + /* resource map size dictated by pdir_size */ + res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ + res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT("%s() res_size 0x%x\n", + __FUNCTION__, res_size); + + sba_dev->ioc[i].res_size = res_size; + sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); + + if (NULL == sba_dev->ioc[i].res_map) + { + panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ ); + } + + memset(sba_dev->ioc[i].res_map, 0, res_size); + /* next available IOVP - circular search */ + if ((sba_dev->hw_rev & 0xFF) >= 0x20) { + sba_dev->ioc[i].res_hint = (unsigned long *) + sba_dev->ioc[i].res_map; + } else { + u64 reserved_iov; + + /* Yet another 1.x hack */ + printk("zx1 1.x: Starting resource hint offset into IOV space to avoid initial zero value IOVA\n"); + sba_dev->ioc[i].res_hint = (unsigned long *) + &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]); + + sba_dev->ioc[i].res_map[0] = 0x1; + sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; + + for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) { + u64 *res_ptr = sba_dev->ioc[i].res_map; + int index = PDIR_INDEX(reserved_iov); + int res_word; + u64 mask; + + res_word = (int)(index / BITS_PER_LONG); + mask = 0x1UL << (index - (res_word * BITS_PER_LONG)); + res_ptr[res_word] |= mask; + sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov); + + } + } + +#ifdef ASSERT_PDIR_SANITY + /* Mark first bit busy - ie no IOVA 0 */ + sba_dev->ioc[i].res_map[0] = 0x1; + sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; +#endif + + DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__, + i, res_size, (void *)sba_dev->ioc[i].res_map); + } + + sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; +} + +#ifdef CONFIG_PROC_FS +static int sba_proc_info(char *buf, char **start, off_t offset, int len) +{ + struct sba_device *sba_dev = sba_list; + struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ + int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */ + unsigned long i = 0, avg = 0, min, max; + + sprintf(buf, "%s rev %d.%d\n", + "Hewlett Packard zx1 SBA", + ((sba_dev->hw_rev >> 4) & 0xF), + (sba_dev->hw_rev & 0xF) + ); + sprintf(buf, "%sIO PDIR size : %d bytes (%d entries)\n", + buf, + (int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ + total_pages); + + sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf, + total_pages - ioc->used_pages, ioc->used_pages, + (int) (ioc->used_pages * 100 / total_pages)); + + sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", + buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ + + min = max = ioc->avg_search[0]; + for (i = 0; i < SBA_SEARCH_SAMPLE; i++) { + avg += ioc->avg_search[i]; + if (ioc->avg_search[i] > max) max = ioc->avg_search[i]; + if (ioc->avg_search[i] < min) min = ioc->avg_search[i]; + } + avg /= SBA_SEARCH_SAMPLE; + sprintf(buf, "%s Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", + buf, min, avg, max); + + sprintf(buf, "%spci_map_single(): %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->msingle_calls, ioc->msingle_pages, + (int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_map_single(): %12ld bypasses\n", + buf, ioc->msingle_bypass); +#endif + + sprintf(buf, "%spci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->usingle_calls, ioc->usingle_pages, + (int) ((ioc->usingle_pages * 1000)/ioc->usingle_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_unmap_single: %12ld bypasses\n", + buf, ioc->usingle_bypass); +#endif + + sprintf(buf, "%spci_map_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->msg_calls, ioc->msg_pages, + (int) ((ioc->msg_pages * 1000)/ioc->msg_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_map_sg() : %12ld bypasses\n", + buf, ioc->msg_bypass); +#endif + + sprintf(buf, "%spci_unmap_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->usg_calls, ioc->usg_pages, + (int) ((ioc->usg_pages * 1000)/ioc->usg_calls)); + + return strlen(buf); +} + +static int +sba_resource_map(char *buf, char **start, off_t offset, int len) +{ + struct ioc *ioc = sba_list->ioc; /* FIXME: Multi-IOC support! */ + unsigned int *res_ptr = (unsigned int *)ioc->res_map; + int i; + + buf[0] = '\0'; + for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) { + if ((i & 7) == 0) + strcat(buf,"\n "); + sprintf(buf, "%s %08x", buf, *res_ptr); + } + strcat(buf, "\n"); + + return strlen(buf); +} +#endif + +/* +** Determine if sba should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +void __init sba_init(void) +{ + struct sba_device *sba_dev; + u32 func_id, hw_rev; + u32 *func_offset = NULL; + int i, agp_found = 0; + static char sba_rev[6]; + struct pci_dev *device = NULL; + u64 hpa = 0; + + if (!(device = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_SBA, NULL))) + return; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (pci_resource_flags(device, i) == IORESOURCE_MEM) { + hpa = ioremap(pci_resource_start(device, i), + pci_resource_len(device, i)); + break; + } + } + + func_id = READ_REG(hpa + SBA_FUNC_ID); + + if (func_id == ZX1_FUNC_ID_VALUE) { + (void)strcpy(sba_rev, "zx1"); + func_offset = zx1_func_offsets; + } else { + return; + } + + /* Read HW Rev First */ + hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL; + + /* + * Not all revision registers of the chipset are updated on every + * turn. Must scan through all functions looking for the highest rev + */ + if (func_offset) { + for (i = 0 ; func_offset[i] != -1 ; i++) { + u32 func_rev; + + func_rev = READ_REG(hpa + SBA_FCLASS + func_offset[i]) & 0xFFUL; + DBG_INIT("%s() func offset: 0x%x rev: 0x%x\n", + __FUNCTION__, func_offset[i], func_rev); + if (func_rev > hw_rev) + hw_rev = func_rev; + } + } + + printk(KERN_INFO "%s found %s %d.%d at %s, HPA 0x%lx\n", DRIVER_NAME, + sba_rev, ((hw_rev >> 4) & 0xF), (hw_rev & 0xF), + device->slot_name, hpa); + + if ((hw_rev & 0xFF) < 0x20) { + printk(KERN_INFO "%s WARNING rev 2.0 or greater will be required for IO MMU support in the future\n", DRIVER_NAME); +#ifndef CONFIG_IA64_HP_PROTO + panic("%s: CONFIG_IA64_HP_PROTO MUST be enabled to support SBA rev less than 2.0", DRIVER_NAME); +#endif + } + + sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL); + if (NULL == sba_dev) { + printk(KERN_ERR DRIVER_NAME " - couldn't alloc sba_device\n"); + return; + } + + memset(sba_dev, 0, sizeof(struct sba_device)); + + for(i=0; i<MAX_IOC; i++) + spin_lock_init(&(sba_dev->ioc[i].res_lock)); + + sba_dev->hw_rev = hw_rev; + sba_dev->sba_hpa = hpa; + + /* + * We need to check for an AGP device, if we find one, then only + * use part of the IOVA space for PCI DMA, the rest is for GART. + * REVISIT for multiple IOC. + */ + pci_for_each_dev(device) + agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP); + + if (agp_found && reserve_sba_gart) + SBA_SET_AGP(sba_dev); + + sba_hw_init(sba_dev); + sba_common_init(sba_dev); + +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry * proc_mckinley_root; + + proc_mckinley_root = proc_mkdir("bus/mckinley",0); + create_proc_info_entry(sba_rev, 0, proc_mckinley_root, sba_proc_info); + create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map); + } +#endif +} + +static int __init +nosbagart (char *str) +{ + reserve_sba_gart = 0; + return 1; +} + +__setup("nosbagart",nosbagart); + +EXPORT_SYMBOL(sba_init); +EXPORT_SYMBOL(sba_map_single); +EXPORT_SYMBOL(sba_unmap_single); +EXPORT_SYMBOL(sba_map_sg); +EXPORT_SYMBOL(sba_unmap_sg); +EXPORT_SYMBOL(sba_dma_address); +EXPORT_SYMBOL(sba_alloc_consistent); +EXPORT_SYMBOL(sba_free_consistent); +/* +** IA64 System Bus Adapter (SBA) I/O MMU manager +** +** (c) Copyright 2002 Alex Williamson +** (c) Copyright 2002 Hewlett-Packard Company +** +** Portions (c) 2000 Grant Grundler (from parisc I/O MMU code) +** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code) +** +** 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 module initializes the IOC (I/O Controller) found on HP +** McKinley machines and their successors. +** +*/ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> + +#include <asm/delay.h> /* ia64_get_itc() */ +#include <asm/io.h> +#include <asm/page.h> /* PAGE_OFFSET */ +#include <asm/efi.h> + + +#define DRIVER_NAME "SBA" + +#ifndef CONFIG_IA64_HP_PROTO +#define ALLOW_IOV_BYPASS +#endif +#define ENABLE_MARK_CLEAN +/* +** The number of debug flags is a clue - this code is fragile. +*/ +#undef DEBUG_SBA_INIT +#undef DEBUG_SBA_RUN +#undef DEBUG_SBA_RUN_SG +#undef DEBUG_SBA_RESOURCE +#undef ASSERT_PDIR_SANITY +#undef DEBUG_LARGE_SG_ENTRIES +#undef DEBUG_BYPASS + +#define SBA_INLINE __inline__ +/* #define SBA_INLINE */ + +#ifdef DEBUG_SBA_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_SBA_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_SBA_RUN_SG +#define DBG_RUN_SG(x...) printk(x) +#else +#define DBG_RUN_SG(x...) +#endif + + +#ifdef DEBUG_SBA_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +#ifdef DEBUG_BYPASS +#define DBG_BYPASS(x...) printk(x) +#else +#define DBG_BYPASS(x...) +#endif + +#ifdef ASSERT_PDIR_SANITY +#define ASSERT(expr) \ + if(!(expr)) { \ + printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \ + panic(#expr); \ + } +#else +#define ASSERT(expr) +#endif + +#define KB(x) ((x) * 1024) +#define MB(x) (KB (KB (x))) +#define GB(x) (MB (KB (x))) + +/* +** The number of pdir entries to "free" before issueing +** a read to PCOM register to flush out PCOM writes. +** Interacts with allocation granularity (ie 4 or 8 entries +** allocated and free'd/purged at a time might make this +** less interesting). +*/ +#define DELAYED_RESOURCE_CNT 16 + +#define DEFAULT_DMA_HINT_REG 0 + +#define ZX1_FUNC_ID_VALUE ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP) +#define ZX1_MC_ID ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP) + +#define SBA_FUNC_ID 0x0000 /* function id */ +#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ + +#define SBA_FUNC_SIZE 0x10000 /* SBA configuration function reg set */ + +unsigned int __initdata zx1_func_offsets[] = {0x1000, 0x4000, 0x8000, + 0x9000, 0xa000, -1}; + +#define SBA_IOC_OFFSET 0x1000 + +#define MAX_IOC 1 /* we only have 1 for now*/ + +#define IOC_IBASE 0x300 /* IO TLB */ +#define IOC_IMASK 0x308 +#define IOC_PCOM 0x310 +#define IOC_TCNFG 0x318 +#define IOC_PDIR_BASE 0x320 + +#define IOC_IOVA_SPACE_BASE 0x40000000 /* IOVA ranges start at 1GB */ + +/* +** IOC supports 4/8/16/64KB page sizes (see TCNFG register) +** It's safer (avoid memory corruption) to keep DMA page mappings +** equivalently sized to VM PAGE_SIZE. +** +** We really can't avoid generating a new mapping for each +** page since the Virtual Coherence Index has to be generated +** and updated for each page. +** +** IOVP_SIZE could only be greater than PAGE_SIZE if we are +** confident the drivers really only touch the next physical +** page iff that driver instance owns it. +*/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +struct ioc { + unsigned long ioc_hpa; /* I/O MMU base address */ + char *res_map; /* resource map, bit == pdir entry */ + u64 *pdir_base; /* physical base address */ + unsigned long ibase; /* pdir IOV Space base */ + unsigned long imask; /* pdir IOV Space mask */ + + unsigned long *res_hint; /* next avail IOVP - circular search */ + spinlock_t res_lock; + unsigned long hint_mask_pdir; /* bits used for DMA hints */ + unsigned int res_bitshift; /* from the RIGHT! */ + unsigned int res_size; /* size of resource map in bytes */ + unsigned int hint_shift_pdir; + unsigned long dma_mask; +#if DELAYED_RESOURCE_CNT > 0 + int saved_cnt; + struct sba_dma_pair { + dma_addr_t iova; + size_t size; + } saved[DELAYED_RESOURCE_CNT]; +#endif + +#ifdef CONFIG_PROC_FS +#define SBA_SEARCH_SAMPLE 0x100 + unsigned long avg_search[SBA_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#ifdef ALLOW_IOV_BYPASS + unsigned long msingle_bypass; + unsigned long usingle_bypass; + unsigned long msg_bypass; +#endif +#endif + + /* STUFF We don't need in performance path */ + unsigned int pdir_size; /* in bytes, determined by IOV Space size */ +}; + +struct sba_device { + struct sba_device *next; /* list of SBA's in system */ + const char *name; + unsigned long sba_hpa; /* base address */ + spinlock_t sba_lock; + unsigned int flags; /* state/functionality enabled */ + unsigned int hw_rev; /* HW revision of chip */ + + unsigned int num_ioc; /* number of on-board IOC's */ + struct ioc ioc[MAX_IOC]; +}; + + +static struct sba_device *sba_list; +static int sba_count; +static int reserve_sba_gart = 1; + +#define sba_sg_iova(sg) (sg->address) +#define sba_sg_len(sg) (sg->length) +#define sba_sg_buffer(sg) (sg->orig_address) + +/* REVISIT - fix me for multiple SBAs/IOCs */ +#define GET_IOC(dev) (sba_list->ioc) +#define SBA_SET_AGP(sba_dev) (sba_dev->flags |= 0x1) +#define SBA_GET_AGP(sba_dev) (sba_dev->flags & 0x1) + +/* +** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up +** (or rather not merge) DMA's into managable chunks. +** On parisc, this is more of the software/tuning constraint +** rather than the HW. I/O MMU allocation alogorithms can be +** faster with smaller size is (to some degree). +*/ +#define DMA_CHUNK_SIZE (BITS_PER_LONG*PAGE_SIZE) + +/* Looks nice and keeps the compiler happy */ +#define SBA_DEV(d) ((struct sba_device *) (d)) + +#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) + +/************************************ +** SBA register read and write support +** +** BE WARNED: register writes are posted. +** (ie follow writes which must reach HW with a read) +** +*/ +#define READ_REG(addr) __raw_readq(addr) +#define WRITE_REG(val, addr) __raw_writeq(val, addr) + +#ifdef DEBUG_SBA_INIT + +/** + * sba_dump_tlb - debugging only - print IOMMU operating parameters + * @hpa: base address of the IOMMU + * + * Print the size/location of the IO MMU PDIR. + */ +static void +sba_dump_tlb(char *hpa) +{ + DBG_INIT("IO TLB at 0x%p\n", (void *)hpa); + DBG_INIT("IOC_IBASE : %016lx\n", READ_REG(hpa+IOC_IBASE)); + DBG_INIT("IOC_IMASK : %016lx\n", READ_REG(hpa+IOC_IMASK)); + DBG_INIT("IOC_TCNFG : %016lx\n", READ_REG(hpa+IOC_TCNFG)); + DBG_INIT("IOC_PDIR_BASE: %016lx\n", READ_REG(hpa+IOC_PDIR_BASE)); + DBG_INIT("\n"); +} +#endif + + +#ifdef ASSERT_PDIR_SANITY + +/** + * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * @pide: pdir index. + * + * Print one entry of the IO MMU PDIR in human readable form. + */ +static void +sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) +{ + /* start printing from lowest pde in rval */ + u64 *ptr = &(ioc->pdir_base[pide & ~(BITS_PER_LONG - 1)]); + unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); + uint rcnt; + + /* printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n", */ + printk("SBA: %s rp %p bit %d rval 0x%lx\n", + msg, rptr, pide & (BITS_PER_LONG - 1), *rptr); + + rcnt = 0; + while (rcnt < BITS_PER_LONG) { + printk("%s %2d %p %016Lx\n", + (rcnt == (pide & (BITS_PER_LONG - 1))) + ? " -->" : " ", + rcnt, ptr, *ptr ); + rcnt++; + ptr++; + } + printk("%s", msg); +} + + +/** + * sba_check_pdir - debugging only - consistency checker + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @msg: text to print ont the output line. + * + * Verify the resource map and pdir state is consistent + */ +static int +sba_check_pdir(struct ioc *ioc, char *msg) +{ + u64 *rptr_end = (u64 *) &(ioc->res_map[ioc->res_size]); + u64 *rptr = (u64 *) ioc->res_map; /* resource map ptr */ + u64 *pptr = ioc->pdir_base; /* pdir ptr */ + uint pide = 0; + + while (rptr < rptr_end) { + u64 rval; + int rcnt; /* number of bits we might check */ + + rval = *rptr; + rcnt = 64; + + while (rcnt) { + /* Get last byte and highest bit from that */ + u32 pde = ((u32)((*pptr >> (63)) & 0x1)); + if ((rval & 0x1) ^ pde) + { + /* + ** BUMMER! -- res_map != pdir -- + ** Dump rval and matching pdir entries + */ + sba_dump_pdir_entry(ioc, msg, pide); + return(1); + } + rcnt--; + rval >>= 1; /* try the next bit */ + pptr++; + pide++; + } + rptr++; /* look at next word of res_map */ + } + /* It'd be nice if we always got here :^) */ + return 0; +} + + +/** + * sba_dump_sg - debugging only - print Scatter-Gather list + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: head of the SG list + * @nents: number of entries in SG list + * + * print the SG list so we can verify it's correct by hand. + */ +static void +sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + while (nents-- > 0) { + printk(" %d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), + sba_sg_len(startsg), + sba_sg_buffer(startsg)); + startsg++; + } +} +static void +sba_check_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + struct scatterlist *the_sg = startsg; + int the_nents = nents; + + while (the_nents-- > 0) { + if (sba_sg_buffer(the_sg) == 0x0UL) + sba_dump_sg(NULL, startsg, nents); + the_sg++; + } +} + +#endif /* ASSERT_PDIR_SANITY */ + + + + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +***************************************************************/ +#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */ + +/* Convert from IOVP to IOVA and vice versa. */ +#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir))) +#define SBA_IOVP(ioc,iova) (((iova) & ioc->hint_mask_pdir) & ~(ioc->ibase)) + +/* FIXME : review these macros to verify correctness and usage */ +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) + +#define RESMAP_MASK(n) ~(~0UL << (n)) +#define RESMAP_IDX_MASK (sizeof(unsigned long) - 1) + + +/** + * sba_search_bitmap - find free space in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @bits_wanted: number of entries we need. + * + * Find consecutive free bits in resource bitmap. + * Each bit represents one entry in the IO Pdir. + * Cool perf optimization: search for log2(size) bits at a time. + */ +static SBA_INLINE unsigned long +sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted) +{ + unsigned long *res_ptr = ioc->res_hint; + unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]); + unsigned long pide = ~0UL; + + ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0); + ASSERT(res_ptr < res_end); + if (bits_wanted > (BITS_PER_LONG/2)) { + /* Search word at a time - no mask needed */ + for(; res_ptr < res_end; ++res_ptr) { + if (*res_ptr == 0) { + *res_ptr = RESMAP_MASK(bits_wanted); + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + break; + } + } + /* point to the next word on next pass */ + res_ptr++; + ioc->res_bitshift = 0; + } else { + /* + ** Search the resource bit map on well-aligned values. + ** "o" is the alignment. + ** We need the alignment to invalidate I/O TLB using + ** SBA HW features in the unmap path. + */ + unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT); + uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o); + unsigned long mask; + + if (bitshiftcnt >= BITS_PER_LONG) { + bitshiftcnt = 0; + res_ptr++; + } + mask = RESMAP_MASK(bits_wanted) << bitshiftcnt; + + DBG_RES("%s() o %ld %p", __FUNCTION__, o, res_ptr); + while(res_ptr < res_end) + { + DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr); + ASSERT(0 != mask); + if(0 == ((*res_ptr) & mask)) { + *res_ptr |= mask; /* mark resources busy! */ + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + pide += bitshiftcnt; + break; + } + mask <<= o; + bitshiftcnt += o; + if (0 == mask) { + mask = RESMAP_MASK(bits_wanted); + bitshiftcnt=0; + res_ptr++; + } + } + /* look in the same word on the next pass */ + ioc->res_bitshift = bitshiftcnt + bits_wanted; + } + + /* wrapped ? */ + if (res_end <= res_ptr) { + ioc->res_hint = (unsigned long *) ioc->res_map; + ioc->res_bitshift = 0; + } else { + ioc->res_hint = res_ptr; + } + return (pide); +} + + +/** + * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @size: number of bytes to create a mapping for + * + * Given a size, find consecutive unmarked and then mark those bits in the + * resource bit map. + */ +static int +sba_alloc_range(struct ioc *ioc, size_t size) +{ + unsigned int pages_needed = size >> IOVP_SHIFT; +#ifdef CONFIG_PROC_FS + unsigned long itc_start = ia64_get_itc(); +#endif + unsigned long pide; + + ASSERT(pages_needed); + ASSERT((pages_needed * IOVP_SIZE) <= DMA_CHUNK_SIZE); + ASSERT(pages_needed <= BITS_PER_LONG); + ASSERT(0 == (size & ~IOVP_MASK)); + + /* + ** "seek and ye shall find"...praying never hurts either... + */ + + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) { + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) + panic(__FILE__ ": I/O MMU @ %lx is out of mapping resources\n", ioc->ioc_hpa); + } + +#ifdef ASSERT_PDIR_SANITY + /* verify the first enable bit is clear */ + if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) { + sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide); + } +#endif + + DBG_RES("%s(%x) %d -> %lx hint %x/%x\n", + __FUNCTION__, size, pages_needed, pide, + (uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map), + ioc->res_bitshift ); + +#ifdef CONFIG_PROC_FS + { + unsigned long itc_end = ia64_get_itc(); + unsigned long tmp = itc_end - itc_start; + /* check for roll over */ + itc_start = (itc_end < itc_start) ? -(tmp) : (tmp); + } + ioc->avg_search[ioc->avg_idx++] = itc_start; + ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1; + + ioc->used_pages += pages_needed; +#endif + + return (pide); +} + + +/** + * sba_free_range - unmark bits in IO PDIR resource bitmap + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO virtual address which was previously allocated. + * @size: number of bytes to create a mapping for + * + * clear bits in the ioc's resource map + */ +static SBA_INLINE void +sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size) +{ + unsigned long iovp = SBA_IOVP(ioc, iova); + unsigned int pide = PDIR_INDEX(iovp); + unsigned int ridx = pide >> 3; /* convert bit to byte address */ + unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]); + + int bits_not_wanted = size >> IOVP_SHIFT; + + /* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */ + unsigned long m = RESMAP_MASK(bits_not_wanted) << (pide & (BITS_PER_LONG - 1)); + + DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n", + __FUNCTION__, (uint) iova, size, + bits_not_wanted, m, pide, res_ptr, *res_ptr); + +#ifdef CONFIG_PROC_FS + ioc->used_pages -= bits_not_wanted; +#endif + + ASSERT(m != 0); + ASSERT(bits_not_wanted); + ASSERT((bits_not_wanted * IOVP_SIZE) <= DMA_CHUNK_SIZE); + ASSERT(bits_not_wanted <= BITS_PER_LONG); + ASSERT((*res_ptr & m) == m); /* verify same bits are set */ + *res_ptr &= ~m; +} + + +/************************************************************** +* +* "Dynamic DMA Mapping" support (aka "Coherent I/O") +* +***************************************************************/ + +#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir) + + +/** + * sba_io_pdir_entry - fill in one IO PDIR entry + * @pdir_ptr: pointer to IO PDIR entry + * @vba: Virtual CPU address of buffer to map + * + * SBA Mapping Routine + * + * Given a virtual address (vba, arg1) sba_io_pdir_entry() + * loads the I/O PDIR entry pointed to by pdir_ptr (arg0). + * Each IO Pdir entry consists of 8 bytes as shown below + * (LSB == bit 0): + * + * 63 40 11 7 0 + * +-+---------------------+----------------------------------+----+--------+ + * |V| U | PPN[39:12] | U | FF | + * +-+---------------------+----------------------------------+----+--------+ + * + * V == Valid Bit + * U == Unused + * PPN == Physical Page Number + * + * The physical address fields are filled with the results of virt_to_phys() + * on the vba. + */ + +#if 1 +#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL) +#else +void SBA_INLINE +sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba) +{ + *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL); +} +#endif + +#ifdef ENABLE_MARK_CLEAN +/** + * Since DMA is i-cache coherent, any (complete) pages that were written via + * DMA can be marked as "clean" so that update_mmu_cache() doesn't have to + * flush them when they get mapped into an executable vm-area. + */ +static void +mark_clean (void *addr, size_t size) +{ + unsigned long pg_addr, end; + + pg_addr = PAGE_ALIGN((unsigned long) addr); + end = (unsigned long) addr + size; + while (pg_addr + PAGE_SIZE <= end) { + struct page *page = virt_to_page(pg_addr); + set_bit(PG_arch_1, &page->flags); + pg_addr += PAGE_SIZE; + } +} +#endif + +/** + * sba_mark_invalid - invalidate one or more IO PDIR entries + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @iova: IO Virtual Address mapped earlier + * @byte_cnt: number of bytes this mapping covers. + * + * Marking the IO PDIR entry(ies) as Invalid and invalidate + * corresponding IO TLB entry. The PCOM (Purge Command Register) + * is to purge stale entries in the IO TLB when unmapping entries. + * + * The PCOM register supports purging of multiple pages, with a minium + * of 1 page and a maximum of 2GB. Hardware requires the address be + * aligned to the size of the range being purged. The size of the range + * must be a power of 2. The "Cool perf optimization" in the + * allocation routine helps keep that true. + */ +static SBA_INLINE void +sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32) SBA_IOVP(ioc,iova); + + int off = PDIR_INDEX(iovp); + + /* Must be non-zero and rounded up */ + ASSERT(byte_cnt > 0); + ASSERT(0 == (byte_cnt & ~IOVP_MASK)); + +#ifdef ASSERT_PDIR_SANITY + /* Assert first pdir entry is set */ + if (!(ioc->pdir_base[off] >> 60)) { + sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp)); + } +#endif + + if (byte_cnt <= IOVP_SIZE) + { + ASSERT(off < ioc->pdir_size); + + iovp |= IOVP_SHIFT; /* set "size" field for PCOM */ + + /* + ** clear I/O PDIR entry "valid" bit + ** Do NOT clear the rest - save it for debugging. + ** We should only clear bits that have previously + ** been enabled. + */ + ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + } else { + u32 t = get_order(byte_cnt) + PAGE_SHIFT; + + iovp |= t; + ASSERT(t <= 31); /* 2GB! Max value of "size" field */ + + do { + /* verify this pdir entry is enabled */ + ASSERT(ioc->pdir_base[off] >> 63); + /* clear I/O Pdir entry "valid" bit first */ + ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + off++; + byte_cnt -= IOVP_SIZE; + } while (byte_cnt > 0); + } + + WRITE_REG(iovp, ioc->ioc_hpa+IOC_PCOM); +} + +/** + * sba_map_single - map one buffer and return IOVA for DMA + * @dev: instance of PCI owned by the driver that's asking. + * @addr: driver buffer to map. + * @size: number of bytes to map in driver buffer. + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +dma_addr_t +sba_map_single(struct pci_dev *dev, void *addr, size_t size, int direction) +{ + struct ioc *ioc; + unsigned long flags; + dma_addr_t iovp; + dma_addr_t offset; + u64 *pdir_start; + int pide; +#ifdef ALLOW_IOV_BYPASS + unsigned long pci_addr = virt_to_phys(addr); +#endif + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + /* + ** Check if the PCI device can DMA to ptr... if so, just return ptr + */ + if ((pci_addr & ~dev->dma_mask) == 0) { + /* + ** Device is bit capable of DMA'ing to the buffer... + ** just return the PCI address of ptr + */ +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->msingle_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n", + dev->dma_mask, pci_addr); + return pci_addr; + } +#endif + + ASSERT(size > 0); + ASSERT(size <= DMA_CHUNK_SIZE); + + /* save offset bits */ + offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK; + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + ~IOVP_MASK) & IOVP_MASK; + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_single()")) + panic("Sanity check failed"); +#endif + +#ifdef CONFIG_PROC_FS + ioc->msingle_calls++; + ioc->msingle_pages += size >> IOVP_SHIFT; +#endif + pide = sba_alloc_range(ioc, size); + iovp = (dma_addr_t) pide << IOVP_SHIFT; + + DBG_RUN("%s() 0x%p -> 0x%lx\n", + __FUNCTION__, addr, (long) iovp | offset); + + pdir_start = &(ioc->pdir_base[pide]); + + while (size > 0) { + ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */ + sba_io_pdir_entry(pdir_start, (unsigned long) addr); + + DBG_RUN(" pdir 0x%p %lx\n", pdir_start, *pdir_start); + + addr += IOVP_SIZE; + size -= IOVP_SIZE; + pdir_start++; + } + /* form complete address */ +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check after sba_map_single()"); +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG); +} + +/** + * sba_unmap_single - unmap one IOVA and free resources + * @dev: instance of PCI owned by the driver that's asking. + * @iova: IOVA of driver buffer previously mapped. + * @size: number of bytes mapped in driver buffer. + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +void sba_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size, + int direction) +{ + struct ioc *ioc; +#if DELAYED_RESOURCE_CNT > 0 + struct sba_dma_pair *d; +#endif + unsigned long flags; + dma_addr_t offset; + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + if ((iova & ioc->imask) != ioc->ibase) { + /* + ** Address does not fall w/in IOVA, must be bypassing + */ +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->usingle_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + DBG_BYPASS("sba_unmap_single() bypass addr: 0x%lx\n", iova); + +#ifdef ENABLE_MARK_CLEAN + if (direction == PCI_DMA_FROMDEVICE) { + mark_clean(phys_to_virt(iova), size); + } +#endif + return; + } +#endif + offset = iova & ~IOVP_MASK; + + DBG_RUN("%s() iovp 0x%lx/%x\n", + __FUNCTION__, (long) iova, size); + + iova ^= offset; /* clear offset bits */ + size += offset; + size = ROUNDUP(size, IOVP_SIZE); + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef CONFIG_PROC_FS + ioc->usingle_calls++; + ioc->usingle_pages += size >> IOVP_SHIFT; +#endif + +#if DELAYED_RESOURCE_CNT > 0 + d = &(ioc->saved[ioc->saved_cnt]); + d->iova = iova; + d->size = size; + if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) { + int cnt = ioc->saved_cnt; + while (cnt--) { + sba_mark_invalid(ioc, d->iova, d->size); + sba_free_range(ioc, d->iova, d->size); + d--; + } + ioc->saved_cnt = 0; + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ + } +#else /* DELAYED_RESOURCE_CNT == 0 */ + sba_mark_invalid(ioc, iova, size); + sba_free_range(ioc, iova, size); + READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ +#endif /* DELAYED_RESOURCE_CNT == 0 */ +#ifdef ENABLE_MARK_CLEAN + if (direction == PCI_DMA_FROMDEVICE) { + u32 iovp = (u32) SBA_IOVP(ioc,iova); + int off = PDIR_INDEX(iovp); + void *addr; + + if (size <= IOVP_SIZE) { + addr = phys_to_virt(ioc->pdir_base[off] & + ~0xE000000000000FFFULL); + mark_clean(addr, size); + } else { + size_t byte_cnt = size; + + do { + addr = phys_to_virt(ioc->pdir_base[off] & + ~0xE000000000000FFFULL); + mark_clean(addr, min(byte_cnt, IOVP_SIZE)); + off++; + byte_cnt -= IOVP_SIZE; + + } while (byte_cnt > 0); + } + } +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + + /* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support. + ** For Astro based systems this isn't a big deal WRT performance. + ** As long as 2.4 kernels copyin/copyout data from/to userspace, + ** we don't need the syncdma. The issue here is I/O MMU cachelines + ** are *not* coherent in all cases. May be hwrev dependent. + ** Need to investigate more. + asm volatile("syncdma"); + */ +} + + +/** + * sba_alloc_consistent - allocate/map shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @dma_handle: IOVA of new buffer. + * + * See Documentation/DMA-mapping.txt + */ +void * +sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ret; + + if (!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return 0; + } + + ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = sba_map_single(hwdev, ret, size, 0); + } + + return ret; +} + + +/** + * sba_free_consistent - free/unmap shared mem for DMA + * @hwdev: instance of PCI owned by the driver that's asking. + * @size: number of bytes mapped in driver buffer. + * @vaddr: virtual address IOVA of "consistent" buffer. + * @dma_handler: IO virtual address of "consistent" buffer. + * + * See Documentation/DMA-mapping.txt + */ +void sba_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + sba_unmap_single(hwdev, dma_handle, size, 0); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +/* +** Since 0 is a valid pdir_base index value, can't use that +** to determine if a value is valid or not. Use a flag to indicate +** the SG list entry contains a valid pdir index. +*/ +#define PIDE_FLAG 0x1UL + +#ifdef DEBUG_LARGE_SG_ENTRIES +int dump_run_sg = 0; +#endif + + +/** + * sba_fill_pdir - write allocated SG entries into IO PDIR + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: list of IOVA/size pairs + * @nents: number of entries in startsg list + * + * Take preprocessed SG list and write corresponding entries + * in the IO PDIR. + */ + +static SBA_INLINE int +sba_fill_pdir( + struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ + int n_mappings = 0; + u64 *pdirp = 0; + unsigned long dma_offset = 0; + + dma_sg--; + while (nents-- > 0) { + int cnt = sba_sg_len(startsg); + sba_sg_len(startsg) = 0; + +#ifdef DEBUG_LARGE_SG_ENTRIES + if (dump_run_sg) + printk(" %2d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), cnt, + sba_sg_buffer(startsg) + ); +#else + DBG_RUN_SG(" %d : %08lx/%05x %p\n", + nents, + (unsigned long) sba_sg_iova(startsg), cnt, + sba_sg_buffer(startsg) + ); +#endif + /* + ** Look for the start of a new DMA stream + */ + if ((u64)sba_sg_iova(startsg) & PIDE_FLAG) { + u32 pide = (u64)sba_sg_iova(startsg) & ~PIDE_FLAG; + dma_offset = (unsigned long) pide & ~IOVP_MASK; + sba_sg_iova(startsg) = 0; + dma_sg++; + sba_sg_iova(dma_sg) = (char *)(pide | ioc->ibase); + pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]); + n_mappings++; + } + + /* + ** Look for a VCONTIG chunk + */ + if (cnt) { + unsigned long vaddr = (unsigned long) sba_sg_buffer(startsg); + ASSERT(pdirp); + + /* Since multiple Vcontig blocks could make up + ** one DMA stream, *add* cnt to dma_len. + */ + sba_sg_len(dma_sg) += cnt; + cnt += dma_offset; + dma_offset=0; /* only want offset on first chunk */ + cnt = ROUNDUP(cnt, IOVP_SIZE); +#ifdef CONFIG_PROC_FS + ioc->msg_pages += cnt >> IOVP_SHIFT; +#endif + do { + sba_io_pdir_entry(pdirp, vaddr); + vaddr += IOVP_SIZE; + cnt -= IOVP_SIZE; + pdirp++; + } while (cnt > 0); + } + startsg++; + } +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = 0; +#endif + return(n_mappings); +} + + +/* +** Two address ranges are DMA contiguous *iff* "end of prev" and +** "start of next" are both on a page boundry. +** +** (shift left is a quick trick to mask off upper bits) +*/ +#define DMA_CONTIG(__X, __Y) \ + (((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL) + + +/** + * sba_coalesce_chunks - preprocess the SG list + * @ioc: IO MMU structure which owns the pdir we are interested in. + * @startsg: list of IOVA/size pairs + * @nents: number of entries in startsg list + * + * First pass is to walk the SG list and determine where the breaks are + * in the DMA stream. Allocates PDIR entries but does not fill them. + * Returns the number of DMA chunks. + * + * Doing the fill seperate from the coalescing/allocation keeps the + * code simpler. Future enhancement could make one pass through + * the sglist do both. + */ +static SBA_INLINE int +sba_coalesce_chunks( struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + struct scatterlist *vcontig_sg; /* VCONTIG chunk head */ + unsigned long vcontig_len; /* len of VCONTIG chunk */ + unsigned long vcontig_end; + struct scatterlist *dma_sg; /* next DMA stream head */ + unsigned long dma_offset, dma_len; /* start/len of DMA stream */ + int n_mappings = 0; + + while (nents > 0) { + unsigned long vaddr = (unsigned long) (startsg->address); + + /* + ** Prepare for first/next DMA stream + */ + dma_sg = vcontig_sg = startsg; + dma_len = vcontig_len = vcontig_end = sba_sg_len(startsg); + vcontig_end += vaddr; + dma_offset = vaddr & ~IOVP_MASK; + + /* PARANOID: clear entries */ + sba_sg_buffer(startsg) = sba_sg_iova(startsg); + sba_sg_iova(startsg) = 0; + sba_sg_len(startsg) = 0; + + /* + ** This loop terminates one iteration "early" since + ** it's always looking one "ahead". + */ + while (--nents > 0) { + unsigned long vaddr; /* tmp */ + + startsg++; + + /* catch brokenness in SCSI layer */ + ASSERT(startsg->length <= DMA_CHUNK_SIZE); + + /* + ** First make sure current dma stream won't + ** exceed DMA_CHUNK_SIZE if we coalesce the + ** next entry. + */ + if (((dma_len + dma_offset + startsg->length + ~IOVP_MASK) & IOVP_MASK) > DMA_CHUNK_SIZE) + break; + + /* + ** Then look for virtually contiguous blocks. + ** + ** append the next transaction? + */ + vaddr = (unsigned long) sba_sg_iova(startsg); + if (vcontig_end == vaddr) + { + vcontig_len += sba_sg_len(startsg); + vcontig_end += sba_sg_len(startsg); + dma_len += sba_sg_len(startsg); + sba_sg_buffer(startsg) = (char *)vaddr; + sba_sg_iova(startsg) = 0; + sba_sg_len(startsg) = 0; + continue; + } + +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = (vcontig_len > IOVP_SIZE); +#endif + + /* + ** Not virtually contigous. + ** Terminate prev chunk. + ** Start a new chunk. + ** + ** Once we start a new VCONTIG chunk, dma_offset + ** can't change. And we need the offset from the first + ** chunk - not the last one. Ergo Successive chunks + ** must start on page boundaries and dove tail + ** with it's predecessor. + */ + sba_sg_len(vcontig_sg) = vcontig_len; + + vcontig_sg = startsg; + vcontig_len = sba_sg_len(startsg); + + /* + ** 3) do the entries end/start on page boundaries? + ** Don't update vcontig_end until we've checked. + */ + if (DMA_CONTIG(vcontig_end, vaddr)) + { + vcontig_end = vcontig_len + vaddr; + dma_len += vcontig_len; + sba_sg_buffer(startsg) = (char *)vaddr; + sba_sg_iova(startsg) = 0; + continue; + } else { + break; + } + } + + /* + ** End of DMA Stream + ** Terminate last VCONTIG block. + ** Allocate space for DMA stream. + */ + sba_sg_len(vcontig_sg) = vcontig_len; + dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK; + ASSERT(dma_len <= DMA_CHUNK_SIZE); + sba_sg_iova(dma_sg) = (char *) (PIDE_FLAG + | (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT) + | dma_offset); + n_mappings++; + } + + return n_mappings; +} + + +/** + * sba_map_sg - map Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +int sba_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, + int direction) +{ + struct ioc *ioc; + int coalesced, filled = 0; + unsigned long flags; +#ifdef ALLOW_IOV_BYPASS + struct scatterlist *sg; +#endif + + DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents); + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef ALLOW_IOV_BYPASS + if (dev->dma_mask >= ioc->dma_mask) { + for (sg = sglist ; filled < nents ; filled++, sg++){ + sba_sg_buffer(sg) = sba_sg_iova(sg); + sba_sg_iova(sg) = (char *)virt_to_phys(sba_sg_buffer(sg)); + } +#ifdef CONFIG_PROC_FS + spin_lock_irqsave(&ioc->res_lock, flags); + ioc->msg_bypass++; + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + return filled; + } +#endif + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sba_sg_buffer(sglist) = sba_sg_iova(sglist); + sba_sg_iova(sglist) = (char *)sba_map_single(dev, + sba_sg_buffer(sglist), + sba_sg_len(sglist), direction); +#ifdef CONFIG_PROC_FS + /* + ** Should probably do some stats counting, but trying to + ** be precise quickly starts wasting CPU time. + */ +#endif + return 1; + } + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check before sba_map_sg()"); + } +#endif + +#ifdef CONFIG_PROC_FS + ioc->msg_calls++; +#endif + + /* + ** First coalesce the chunks and allocate I/O pdir space + ** + ** If this is one DMA stream, we can properly map using the + ** correct virtual address associated with each DMA page. + ** w/o this association, we wouldn't have coherent DMA! + ** Access to the virtual address is what forces a two pass algorithm. + */ + coalesced = sba_coalesce_chunks(ioc, sglist, nents); + + /* + ** Program the I/O Pdir + ** + ** map the virtual addresses to the I/O Pdir + ** o dma_address will contain the pdir index + ** o dma_len will contain the number of bytes to map + ** o address contains the virtual address. + */ + filled = sba_fill_pdir(ioc, sglist, nents); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check after sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check after sba_map_sg()\n"); + } +#endif + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + ASSERT(coalesced == filled); + DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled); + + return filled; +} + + +/** + * sba_unmap_sg - unmap Scatter/Gather list + * @dev: instance of PCI owned by the driver that's asking. + * @sglist: array of buffer/length pairs + * @nents: number of entries in list + * @direction: R/W or both. + * + * See Documentation/DMA-mapping.txt + */ +void sba_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, + int direction) +{ + struct ioc *ioc; +#ifdef ASSERT_PDIR_SANITY + unsigned long flags; +#endif + + DBG_RUN_SG("%s() START %d entries, %p,%x\n", + __FUNCTION__, nents, sba_sg_buffer(sglist), sglist->length); + + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef CONFIG_PROC_FS + ioc->usg_calls++; +#endif + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check before sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + + while (sba_sg_len(sglist) && nents--) { + + sba_unmap_single(dev, (dma_addr_t)sba_sg_iova(sglist), + sba_sg_len(sglist), direction); +#ifdef CONFIG_PROC_FS + /* + ** This leaves inconsistent data in the stats, but we can't + ** tell which sg lists were mapped by map_single and which + ** were coalesced to a single entry. The stats are fun, + ** but speed is more important. + */ + ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT; +#endif + ++sglist; + } + + DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__, nents); + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check after sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + +} + +unsigned long +sba_dma_address (struct scatterlist *sg) +{ + return ((unsigned long)sba_sg_iova(sg)); +} + +/************************************************************** +* +* Initialization and claim +* +***************************************************************/ + + +static void +sba_ioc_init(struct sba_device *sba_dev, struct ioc *ioc, int ioc_num) +{ + u32 iova_space_size, iova_space_mask; + void * pdir_base; + int pdir_size, iov_order, tcnfg; + + /* + ** Firmware programs the maximum IOV space size into the imask reg + */ + iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1; +#ifdef CONFIG_IA64_HP_PROTO + if (!iova_space_size) + iova_space_size = GB(1); +#endif + + /* + ** iov_order is always based on a 1GB IOVA space since we want to + ** turn on the other half for AGP GART. + */ + iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT)); + ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + + DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits) PDIR size 0x%0x\n", + __FUNCTION__, ioc->ioc_hpa, iova_space_size>>20, + iov_order + PAGE_SHIFT, ioc->pdir_size); + + /* FIXME : DMA HINTs not used */ + ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; + ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); + + ioc->pdir_base = + pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); + if (NULL == pdir_base) + { + panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); + } + memset(pdir_base, 0, pdir_size); + + DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n", + __FUNCTION__, pdir_base, pdir_size, + ioc->hint_shift_pdir, ioc->hint_mask_pdir); + + ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); + WRITE_REG(virt_to_phys(pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); + + DBG_INIT(" base %p\n", pdir_base); + + /* build IMASK for IOC and Elroy */ + iova_space_mask = 0xffffffff; + iova_space_mask <<= (iov_order + PAGE_SHIFT); + +#ifdef CONFIG_IA64_HP_PROTO + /* + ** REVISIT - this is a kludge, but we won't be supporting anything but + ** zx1 2.0 or greater for real. When fw is in shape, ibase will + ** be preprogrammed w/ the IOVA hole base and imask will give us + ** the size. + */ + if ((sba_dev->hw_rev & 0xFF) < 0x20) { + DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0. This device will not be supported in the future.\n", __FUNCTION__); + ioc->ibase = 0x0; + } else +#endif + ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL; + + ioc->imask = iova_space_mask; /* save it */ + + DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", + __FUNCTION__, ioc->ibase, ioc->imask); + + /* + ** FIXME: Hint registers are programmed with default hint + ** values during boot, so hints should be sane even if we + ** can't reprogram them the way drivers want. + */ + + WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK); + + /* + ** Setting the upper bits makes checking for bypass addresses + ** a little faster later on. + */ + ioc->imask |= 0xFFFFFFFF00000000UL; + + /* Set I/O PDIR Page size to system page size */ + switch (PAGE_SHIFT) { + case 12: /* 4K */ + tcnfg = 0; + break; + case 13: /* 8K */ + tcnfg = 1; + break; + case 14: /* 16K */ + tcnfg = 2; + break; + case 16: /* 64K */ + tcnfg = 3; + break; + } + WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG); + + /* + ** Program the IOC's ibase and enable IOVA translation + ** Bit zero == enable bit. + */ + WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE); + + /* + ** Clear I/O TLB of any possible entries. + ** (Yes. This is a bit paranoid...but so what) + */ + WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM); + + /* + ** If an AGP device is present, only use half of the IOV space + ** for PCI DMA. Unfortunately we can't know ahead of time + ** whether GART support will actually be used, for now we + ** can just key on an AGP device found in the system. + ** We program the next pdir index after we stop w/ a key for + ** the GART code to handshake on. + */ + if (SBA_GET_AGP(sba_dev)) { + DBG_INIT("%s() AGP Device found, reserving 512MB for GART support\n", __FUNCTION__); + ioc->pdir_size /= 2; + ((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL; + } + + DBG_INIT("%s() DONE\n", __FUNCTION__); +} + + + +/************************************************************************** +** +** SBA initialization code (HW and SW) +** +** o identify SBA chip itself +** o FIXME: initialize DMA hints for reasonable defaults +** +**************************************************************************/ + +static void +sba_hw_init(struct sba_device *sba_dev) +{ + int i; + int num_ioc; + u64 dma_mask; + u32 func_id; + + /* + ** Identify the SBA so we can set the dma_mask. We can make a virtual + ** dma_mask of the memory subsystem such that devices not implmenting + ** a full 64bit mask might still be able to bypass efficiently. + */ + func_id = READ_REG(sba_dev->sba_hpa + SBA_FUNC_ID); + + if (func_id == ZX1_FUNC_ID_VALUE) { + dma_mask = 0xFFFFFFFFFFUL; + } else { + dma_mask = 0xFFFFFFFFFFFFFFFFUL; + } + + DBG_INIT("%s(): ioc->dma_mask == 0x%lx\n", __FUNCTION__, dma_mask); + + /* + ** Leaving in the multiple ioc code from parisc for the future, + ** currently there are no muli-ioc mckinley sbas + */ + sba_dev->ioc[0].ioc_hpa = SBA_IOC_OFFSET; + num_ioc = 1; + + sba_dev->num_ioc = num_ioc; + for (i = 0; i < num_ioc; i++) { + sba_dev->ioc[i].dma_mask = dma_mask; + sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa; + sba_ioc_init(sba_dev, &(sba_dev->ioc[i]), i); + } +} + +static void +sba_common_init(struct sba_device *sba_dev) +{ + int i; + + /* add this one to the head of the list (order doesn't matter) + ** This will be useful for debugging - especially if we get coredumps + */ + sba_dev->next = sba_list; + sba_list = sba_dev; + sba_count++; + + for(i=0; i< sba_dev->num_ioc; i++) { + int res_size; + + /* resource map size dictated by pdir_size */ + res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ + res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT("%s() res_size 0x%x\n", + __FUNCTION__, res_size); + + sba_dev->ioc[i].res_size = res_size; + sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); + + if (NULL == sba_dev->ioc[i].res_map) + { + panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ ); + } + + memset(sba_dev->ioc[i].res_map, 0, res_size); + /* next available IOVP - circular search */ + if ((sba_dev->hw_rev & 0xFF) >= 0x20) { + sba_dev->ioc[i].res_hint = (unsigned long *) + sba_dev->ioc[i].res_map; + } else { + u64 reserved_iov; + + /* Yet another 1.x hack */ + printk("zx1 1.x: Starting resource hint offset into IOV space to avoid initial zero value IOVA\n"); + sba_dev->ioc[i].res_hint = (unsigned long *) + &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]); + + sba_dev->ioc[i].res_map[0] = 0x1; + sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; + + for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) { + u64 *res_ptr = sba_dev->ioc[i].res_map; + int index = PDIR_INDEX(reserved_iov); + int res_word; + u64 mask; + + res_word = (int)(index / BITS_PER_LONG); + mask = 0x1UL << (index - (res_word * BITS_PER_LONG)); + res_ptr[res_word] |= mask; + sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov); + + } + } + +#ifdef ASSERT_PDIR_SANITY + /* Mark first bit busy - ie no IOVA 0 */ + sba_dev->ioc[i].res_map[0] = 0x1; + sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; +#endif + + DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__, + i, res_size, (void *)sba_dev->ioc[i].res_map); + } + + sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; +} + +#ifdef CONFIG_PROC_FS +static int sba_proc_info(char *buf, char **start, off_t offset, int len) +{ + struct sba_device *sba_dev = sba_list; + struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ + int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */ + unsigned long i = 0, avg = 0, min, max; + + sprintf(buf, "%s rev %d.%d\n", + "Hewlett Packard zx1 SBA", + ((sba_dev->hw_rev >> 4) & 0xF), + (sba_dev->hw_rev & 0xF) + ); + sprintf(buf, "%sIO PDIR size : %d bytes (%d entries)\n", + buf, + (int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ + total_pages); + + sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf, + total_pages - ioc->used_pages, ioc->used_pages, + (int) (ioc->used_pages * 100 / total_pages)); + + sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", + buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ + + min = max = ioc->avg_search[0]; + for (i = 0; i < SBA_SEARCH_SAMPLE; i++) { + avg += ioc->avg_search[i]; + if (ioc->avg_search[i] > max) max = ioc->avg_search[i]; + if (ioc->avg_search[i] < min) min = ioc->avg_search[i]; + } + avg /= SBA_SEARCH_SAMPLE; + sprintf(buf, "%s Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", + buf, min, avg, max); + + sprintf(buf, "%spci_map_single(): %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->msingle_calls, ioc->msingle_pages, + (int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_map_single(): %12ld bypasses\n", + buf, ioc->msingle_bypass); +#endif + + sprintf(buf, "%spci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->usingle_calls, ioc->usingle_pages, + (int) ((ioc->usingle_pages * 1000)/ioc->usingle_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_unmap_single: %12ld bypasses\n", + buf, ioc->usingle_bypass); +#endif + + sprintf(buf, "%spci_map_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->msg_calls, ioc->msg_pages, + (int) ((ioc->msg_pages * 1000)/ioc->msg_calls)); +#ifdef ALLOW_IOV_BYPASS + sprintf(buf, "%spci_map_sg() : %12ld bypasses\n", + buf, ioc->msg_bypass); +#endif + + sprintf(buf, "%spci_unmap_sg() : %12ld calls %12ld pages (avg %d/1000)\n", + buf, ioc->usg_calls, ioc->usg_pages, + (int) ((ioc->usg_pages * 1000)/ioc->usg_calls)); + + return strlen(buf); +} + +static int +sba_resource_map(char *buf, char **start, off_t offset, int len) +{ + struct ioc *ioc = sba_list->ioc; /* FIXME: Multi-IOC support! */ + unsigned int *res_ptr = (unsigned int *)ioc->res_map; + int i; + + buf[0] = '\0'; + for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) { + if ((i & 7) == 0) + strcat(buf,"\n "); + sprintf(buf, "%s %08x", buf, *res_ptr); + } + strcat(buf, "\n"); + + return strlen(buf); +} +#endif + +/* +** Determine if sba should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +void __init sba_init(void) +{ + struct sba_device *sba_dev; + u32 func_id, hw_rev; + u32 *func_offset = NULL; + int i, agp_found = 0; + static char sba_rev[6]; + struct pci_dev *device = NULL; + u64 hpa = 0; + + if (!(device = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_SBA, NULL))) + return; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (pci_resource_flags(device, i) == IORESOURCE_MEM) { + hpa = ioremap(pci_resource_start(device, i), + pci_resource_len(device, i)); + break; + } + } + + func_id = READ_REG(hpa + SBA_FUNC_ID); + + if (func_id == ZX1_FUNC_ID_VALUE) { + (void)strcpy(sba_rev, "zx1"); + func_offset = zx1_func_offsets; + } else { + return; + } + + /* Read HW Rev First */ + hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL; + + /* + * Not all revision registers of the chipset are updated on every + * turn. Must scan through all functions looking for the highest rev + */ + if (func_offset) { + for (i = 0 ; func_offset[i] != -1 ; i++) { + u32 func_rev; + + func_rev = READ_REG(hpa + SBA_FCLASS + func_offset[i]) & 0xFFUL; + DBG_INIT("%s() func offset: 0x%x rev: 0x%x\n", + __FUNCTION__, func_offset[i], func_rev); + if (func_rev > hw_rev) + hw_rev = func_rev; + } + } + + printk(KERN_INFO "%s found %s %d.%d at %s, HPA 0x%lx\n", DRIVER_NAME, + sba_rev, ((hw_rev >> 4) & 0xF), (hw_rev & 0xF), + device->slot_name, hpa); + + if ((hw_rev & 0xFF) < 0x20) { + printk(KERN_INFO "%s WARNING rev 2.0 or greater will be required for IO MMU support in the future\n", DRIVER_NAME); +#ifndef CONFIG_IA64_HP_PROTO + panic("%s: CONFIG_IA64_HP_PROTO MUST be enabled to support SBA rev less than 2.0", DRIVER_NAME); +#endif + } + + sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL); + if (NULL == sba_dev) { + printk(KERN_ERR DRIVER_NAME " - couldn't alloc sba_device\n"); + return; + } + + memset(sba_dev, 0, sizeof(struct sba_device)); + + for(i=0; i<MAX_IOC; i++) + spin_lock_init(&(sba_dev->ioc[i].res_lock)); + + sba_dev->hw_rev = hw_rev; + sba_dev->sba_hpa = hpa; + + /* + * We need to check for an AGP device, if we find one, then only + * use part of the IOVA space for PCI DMA, the rest is for GART. + * REVISIT for multiple IOC. + */ + pci_for_each_dev(device) + agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP); + + if (agp_found && reserve_sba_gart) + SBA_SET_AGP(sba_dev); + + sba_hw_init(sba_dev); + sba_common_init(sba_dev); + +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry * proc_mckinley_root; + + proc_mckinley_root = proc_mkdir("bus/mckinley",0); + create_proc_info_entry(sba_rev, 0, proc_mckinley_root, sba_proc_info); + create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map); + } +#endif +} + +static int __init +nosbagart (char *str) +{ + reserve_sba_gart = 0; + return 1; +} + +__setup("nosbagart",nosbagart); + +EXPORT_SYMBOL(sba_init); +EXPORT_SYMBOL(sba_map_single); +EXPORT_SYMBOL(sba_unmap_single); +EXPORT_SYMBOL(sba_map_sg); +EXPORT_SYMBOL(sba_unmap_sg); +EXPORT_SYMBOL(sba_dma_address); +EXPORT_SYMBOL(sba_alloc_consistent); +EXPORT_SYMBOL(sba_free_consistent); diff --git a/arch/ia64/hp/sim/Makefile b/arch/ia64/hp/sim/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b6e692c5f477e7c99e2c5ced8cc0fcacfca58440 --- /dev/null +++ b/arch/ia64/hp/sim/Makefile @@ -0,0 +1,19 @@ +# +# ia64/platform/hp/sim/Makefile +# +# Copyright (C) 2002 Hewlett-Packard Co. +# David Mosberger-Tang <davidm@hpl.hp.com> +# Copyright (C) 1999 Silicon Graphics, Inc. +# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) +# + +O_TARGET := sim.o + +obj-y := hpsim_console.o hpsim_irq.o hpsim_setup.o +obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o + +obj-$(CONFIG_HP_SIMETH) += simeth.o +obj-$(CONFIG_HP_SIMSERIAL) += simserial.o +obj-$(CONFIG_HP_SIMSCSI) += simscsi.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/hp/hpsim_console.c b/arch/ia64/hp/sim/hpsim_console.c similarity index 100% rename from arch/ia64/hp/hpsim_console.c rename to arch/ia64/hp/sim/hpsim_console.c diff --git a/arch/ia64/hp/hpsim_irq.c b/arch/ia64/hp/sim/hpsim_irq.c similarity index 100% rename from arch/ia64/hp/hpsim_irq.c rename to arch/ia64/hp/sim/hpsim_irq.c diff --git a/arch/ia64/hp/hpsim_machvec.c b/arch/ia64/hp/sim/hpsim_machvec.c similarity index 100% rename from arch/ia64/hp/hpsim_machvec.c rename to arch/ia64/hp/sim/hpsim_machvec.c diff --git a/arch/ia64/hp/hpsim_setup.c b/arch/ia64/hp/sim/hpsim_setup.c similarity index 100% rename from arch/ia64/hp/hpsim_setup.c rename to arch/ia64/hp/sim/hpsim_setup.c diff --git a/arch/ia64/hp/hpsim_ssc.h b/arch/ia64/hp/sim/hpsim_ssc.h similarity index 100% rename from arch/ia64/hp/hpsim_ssc.h rename to arch/ia64/hp/sim/hpsim_ssc.h diff --git a/arch/ia64/hp/simeth.c b/arch/ia64/hp/sim/simeth.c similarity index 100% rename from arch/ia64/hp/simeth.c rename to arch/ia64/hp/sim/simeth.c diff --git a/arch/ia64/hp/simscsi.c b/arch/ia64/hp/sim/simscsi.c similarity index 100% rename from arch/ia64/hp/simscsi.c rename to arch/ia64/hp/sim/simscsi.c diff --git a/arch/ia64/hp/simscsi.h b/arch/ia64/hp/sim/simscsi.h similarity index 100% rename from arch/ia64/hp/simscsi.h rename to arch/ia64/hp/sim/simscsi.h diff --git a/arch/ia64/hp/simserial.c b/arch/ia64/hp/sim/simserial.c similarity index 100% rename from arch/ia64/hp/simserial.c rename to arch/ia64/hp/sim/simserial.c diff --git a/arch/ia64/hp/zx1/Makefile b/arch/ia64/hp/zx1/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4319d3f972ffa32a5badc82207ca75c7075537e1 --- /dev/null +++ b/arch/ia64/hp/zx1/Makefile @@ -0,0 +1,26 @@ +# +# ia64/platform/hp/zx1/Makefile +# +# Copyright (C) 2002 Hewlett Packard +# Copyright (C) Alex Williamson (alex_williamson@hp.com) +# + +O_TARGET := zx1.o + +obj-y := hpzx1_misc.o +obj-$(CONFIG_IA64_GENERIC) += hpzx1_machvec.o + +include $(TOPDIR)/Rules.make +# +# ia64/platform/hp/zx1/Makefile +# +# Copyright (C) 2002 Hewlett Packard +# Copyright (C) Alex Williamson (alex_williamson@hp.com) +# + +O_TARGET := zx1.o + +obj-y := hpzx1_misc.o +obj-$(CONFIG_IA64_GENERIC) += hpzx1_machvec.o + +include $(TOPDIR)/Rules.make diff --git a/arch/ia64/hp/zx1/hpzx1_machvec.c b/arch/ia64/hp/zx1/hpzx1_machvec.c new file mode 100644 index 0000000000000000000000000000000000000000..1c2a8a625ac65d5240ac425a7af794cfbebf966a --- /dev/null +++ b/arch/ia64/hp/zx1/hpzx1_machvec.c @@ -0,0 +1,4 @@ +#define MACHVEC_PLATFORM_NAME hpzx1 +#include <asm/machvec_init.h> +#define MACHVEC_PLATFORM_NAME hpzx1 +#include <asm/machvec_init.h> diff --git a/arch/ia64/hp/zx1/hpzx1_misc.c b/arch/ia64/hp/zx1/hpzx1_misc.c new file mode 100644 index 0000000000000000000000000000000000000000..991f7b0dd24d6f1e57bacd8453c57e83830873d2 --- /dev/null +++ b/arch/ia64/hp/zx1/hpzx1_misc.c @@ -0,0 +1,800 @@ +/* + * Misc. support for HP zx1 chipset support + * + * Copyright (C) 2002 Hewlett-Packard Co + * Copyright (C) 2002 Alex Williamson <alex_williamson@hp.com> + * Copyright (C) 2002 Bjorn Helgaas <bjorn_helgaas@hp.com> + */ + + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <asm/iosapic.h> +#include <asm/efi.h> + +#include "../drivers/acpi/include/platform/acgcc.h" +#include "../drivers/acpi/include/actypes.h" +#include "../drivers/acpi/include/acexcep.h" +#include "../drivers/acpi/include/acpixf.h" +#include "../drivers/acpi/include/actbl.h" +#include "../drivers/acpi/include/acconfig.h" +#include "../drivers/acpi/include/acmacros.h" +#include "../drivers/acpi/include/aclocal.h" +#include "../drivers/acpi/include/acobject.h" +#include "../drivers/acpi/include/acstruct.h" +#include "../drivers/acpi/include/acnamesp.h" +#include "../drivers/acpi/include/acutils.h" + +#define PFX "hpzx1: " + +struct fake_pci_dev { + struct fake_pci_dev *next; + unsigned char bus; + unsigned int devfn; + int sizing; // in middle of BAR sizing operation? + unsigned long csr_base; + unsigned int csr_size; + unsigned long mapped_csrs; // ioremapped +}; + +static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head; + +static struct pci_ops orig_pci_ops; + +static inline struct fake_pci_dev * +fake_pci_find_slot(unsigned char bus, unsigned int devfn) +{ + struct fake_pci_dev *dev; + + for (dev = fake_pci_head; dev; dev = dev->next) + if (dev->bus == bus && dev->devfn == devfn) + return dev; + return NULL; +} + +static struct fake_pci_dev * +alloc_fake_pci_dev(void) +{ + struct fake_pci_dev *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + memset(dev, 0, sizeof(*dev)); + + *fake_pci_tail = dev; + fake_pci_tail = &dev->next; + + return dev; +} + +#define HP_CFG_RD(sz, bits, name) \ +static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ + return orig_pci_ops.name(dev, where, value); \ + \ + switch (where) { \ + case PCI_COMMAND: \ + *value = read##sz(fake_dev->mapped_csrs + where); \ + *value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \ + break; \ + case PCI_BASE_ADDRESS_0: \ + if (fake_dev->sizing) \ + *value = ~(fake_dev->csr_size - 1); \ + else \ + *value = (fake_dev->csr_base & \ + PCI_BASE_ADDRESS_MEM_MASK) | \ + PCI_BASE_ADDRESS_SPACE_MEMORY; \ + fake_dev->sizing = 0; \ + break; \ + default: \ + *value = read##sz(fake_dev->mapped_csrs + where); \ + break; \ + } \ + return PCIBIOS_SUCCESSFUL; \ +} + +#define HP_CFG_WR(sz, bits, name) \ +static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ + return orig_pci_ops.name(dev, where, value); \ + \ + switch (where) { \ + case PCI_BASE_ADDRESS_0: \ + if (value == ~0) \ + fake_dev->sizing = 1; \ + break; \ + default: \ + write##sz(value, fake_dev->mapped_csrs + where); \ + break; \ + } \ + return PCIBIOS_SUCCESSFUL; \ +} + +HP_CFG_RD(b, 8, read_byte) +HP_CFG_RD(w, 16, read_word) +HP_CFG_RD(l, 32, read_dword) +HP_CFG_WR(b, 8, write_byte) +HP_CFG_WR(w, 16, write_word) +HP_CFG_WR(l, 32, write_dword) + +static struct pci_ops hp_pci_conf = { + hp_cfg_readb, + hp_cfg_readw, + hp_cfg_readl, + hp_cfg_writeb, + hp_cfg_writew, + hp_cfg_writel, +}; + +/* + * Assume we'll never have a physical slot higher than 0x10, so we can + * use slots above that for "fake" PCI devices to represent things + * that only show up in the ACPI namespace. + */ +#define HP_MAX_SLOT 0x10 + +static struct fake_pci_dev * +hpzx1_fake_pci_dev(unsigned long addr, unsigned int bus, unsigned int size) +{ + struct fake_pci_dev *dev; + int slot; + + // Note: lspci thinks 0x1f is invalid + for (slot = 0x1e; slot > HP_MAX_SLOT; slot--) { + if (!fake_pci_find_slot(bus, PCI_DEVFN(slot, 0))) + break; + } + if (slot == HP_MAX_SLOT) { + printk(KERN_ERR PFX + "no slot space for device (0x%p) on bus 0x%02x\n", + (void *) addr, bus); + return NULL; + } + + dev = alloc_fake_pci_dev(); + if (!dev) { + printk(KERN_ERR PFX + "no memory for device (0x%p) on bus 0x%02x\n", + (void *) addr, bus); + return NULL; + } + + dev->bus = bus; + dev->devfn = PCI_DEVFN(slot, 0); + dev->csr_base = addr; + dev->csr_size = size; + + /* + * Drivers should ioremap what they need, but we have to do + * it here, too, so PCI config accesses work. + */ + dev->mapped_csrs = ioremap(dev->csr_base, dev->csr_size); + + return dev; +} + +typedef struct { + u8 guid_id; + u8 guid[16]; + u8 csr_base[8]; + u8 csr_length[8]; +} acpi_hp_vendor_long; + +#define HP_CCSR_LENGTH 0x21 +#define HP_CCSR_TYPE 0x2 +#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, \ + 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad) + +extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *); +extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *); +extern acpi_resource_data *acpi_get_crs_type(acpi_buffer *, int *, int); +extern void acpi_dispose_crs(acpi_buffer *); +extern acpi_status acpi_cf_evaluate_method(acpi_handle, UINT8 *, NATIVE_UINT *); + +static acpi_status +hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length) +{ + int i, offset = 0; + acpi_status status; + acpi_buffer buf; + acpi_resource_vendor *res; + acpi_hp_vendor_long *hp_res; + efi_guid_t vendor_guid; + + *csr_base = 0; + *csr_length = 0; + + status = acpi_get_crs(obj, &buf); + if (status != AE_OK) { + printk(KERN_ERR PFX "Unable to get _CRS data on object\n"); + return status; + } + + res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR); + if (!res) { + printk(KERN_ERR PFX "Failed to find config space for device\n"); + acpi_dispose_crs(&buf); + return AE_NOT_FOUND; + } + + hp_res = (acpi_hp_vendor_long *)(res->reserved); + + if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) { + printk(KERN_ERR PFX "Unknown Vendor data\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t)); + if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) { + printk(KERN_ERR PFX "Vendor GUID does not match\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + for (i = 0 ; i < 8 ; i++) { + *csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8)); + *csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8)); + } + + acpi_dispose_crs(&buf); + + return AE_OK; +} + +static acpi_status +hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret) +{ + u64 csr_base = 0, csr_length = 0; + char *name = context; + struct fake_pci_dev *dev; + acpi_status status; + + status = hp_csr_space(obj, &csr_base, &csr_length); + + if (status != AE_OK) + return status; + + /* + * Only SBA shows up in ACPI namespace, so its CSR space + * includes both SBA and IOC. Make SBA and IOC show up + * separately in PCI space. + */ + if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) + printk(KERN_INFO PFX "%s SBA at 0x%lx; pci dev %02x:%02x.%d\n", + name, csr_base, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, 0x1000))) + printk(KERN_INFO PFX "%s IOC at 0x%lx; pci dev %02x:%02x.%d\n", + name, csr_base + 0x1000, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + + return AE_OK; +} + +static acpi_status +hpzx1_lba_probe(acpi_handle obj, u32 depth, void *context, void **ret) +{ + acpi_status status; + u64 csr_base = 0, csr_length = 0; + char *name = context; + NATIVE_UINT busnum = 0; + struct fake_pci_dev *dev; + + status = hp_csr_space(obj, &csr_base, &csr_length); + + if (status != AE_OK) + return status; + + status = acpi_cf_evaluate_method(obj, METHOD_NAME__BBN, &busnum); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PFX "evaluate _BBN fail=0x%x\n", status); + busnum = 0; // no _BBN; stick it on bus 0 + } + + if ((dev = hpzx1_fake_pci_dev(csr_base, busnum, csr_length))) + printk(KERN_INFO PFX "%s LBA at 0x%lx, _BBN 0x%02x; " + "pci dev %02x:%02x.%d\n", + name, csr_base, busnum, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + + return AE_OK; +} + +static void +hpzx1_acpi_dev_init(void) +{ + extern struct pci_ops pci_conf; + + /* + * Make fake PCI devices for the following hardware in the + * ACPI namespace. This makes it more convenient for drivers + * because they can claim these devices based on PCI + * information, rather than needing to know about ACPI. The + * 64-bit "HPA" space for this hardware is available as BAR + * 0/1. + * + * HWP0001: Single IOC SBA w/o IOC in namespace + * HWP0002: LBA device + * HWP0003: AGP LBA device + */ + acpi_get_devices("HWP0001", hpzx1_sba_probe, "HWP0001", NULL); +#ifdef CONFIG_IA64_HP_PROTO + if (fake_pci_tail != &fake_pci_head) { +#endif + acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002", NULL); + acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003", NULL); + +#ifdef CONFIG_IA64_HP_PROTO + } + +#define ZX1_FUNC_ID_VALUE (PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP + /* + * Early protos don't have bridges in the ACPI namespace, so + * if we didn't find anything, add the things we know are + * there. + */ + if (fake_pci_tail == &fake_pci_head) { + u64 hpa, csr_base; + struct fake_pci_dev *dev; + + csr_base = 0xfed00000UL; + hpa = (u64) ioremap(csr_base, 0x1000); + if (__raw_readl(hpa) == ZX1_FUNC_ID_VALUE) { + if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) + printk(KERN_INFO PFX "HWP0001 SBA at 0x%lx; " + "pci dev %02x:%02x.%d\n", csr_base, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, + 0x1000))) + printk(KERN_INFO PFX "HWP0001 IOC at 0x%lx; " + "pci dev %02x:%02x.%d\n", + csr_base + 0x1000, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + + csr_base = 0xfed24000UL; + iounmap(hpa); + hpa = (u64) ioremap(csr_base, 0x1000); + if ((dev = hpzx1_fake_pci_dev(csr_base, 0x40, 0x1000))) + printk(KERN_INFO PFX "HWP0003 AGP LBA at " + "0x%lx; pci dev %02x:%02x.%d\n", + csr_base, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + } + iounmap(hpa); + } +#endif + + if (fake_pci_tail == &fake_pci_head) + return; + + /* + * Replace PCI ops, but only if we made fake devices. + */ + orig_pci_ops = pci_conf; + pci_conf = hp_pci_conf; +} + +extern void sba_init(void); + +void +hpzx1_pci_fixup (int phase) +{ + if (phase == 0) + hpzx1_acpi_dev_init(); + iosapic_pci_fixup(phase); + if (phase == 1) + sba_init(); +} +/* + * Misc. support for HP zx1 chipset support + * + * Copyright (C) 2002 Hewlett-Packard Co + * Copyright (C) 2002 Alex Williamson <alex_williamson@hp.com> + * Copyright (C) 2002 Bjorn Helgaas <bjorn_helgaas@hp.com> + */ + + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <asm/iosapic.h> +#include <asm/efi.h> + +#include "../drivers/acpi/include/platform/acgcc.h" +#include "../drivers/acpi/include/actypes.h" +#include "../drivers/acpi/include/acexcep.h" +#include "../drivers/acpi/include/acpixf.h" +#include "../drivers/acpi/include/actbl.h" +#include "../drivers/acpi/include/acconfig.h" +#include "../drivers/acpi/include/acmacros.h" +#include "../drivers/acpi/include/aclocal.h" +#include "../drivers/acpi/include/acobject.h" +#include "../drivers/acpi/include/acstruct.h" +#include "../drivers/acpi/include/acnamesp.h" +#include "../drivers/acpi/include/acutils.h" + +#define PFX "hpzx1: " + +struct fake_pci_dev { + struct fake_pci_dev *next; + unsigned char bus; + unsigned int devfn; + int sizing; // in middle of BAR sizing operation? + unsigned long csr_base; + unsigned int csr_size; + unsigned long mapped_csrs; // ioremapped +}; + +static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head; + +static struct pci_ops orig_pci_ops; + +static inline struct fake_pci_dev * +fake_pci_find_slot(unsigned char bus, unsigned int devfn) +{ + struct fake_pci_dev *dev; + + for (dev = fake_pci_head; dev; dev = dev->next) + if (dev->bus == bus && dev->devfn == devfn) + return dev; + return NULL; +} + +static struct fake_pci_dev * +alloc_fake_pci_dev(void) +{ + struct fake_pci_dev *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + memset(dev, 0, sizeof(*dev)); + + *fake_pci_tail = dev; + fake_pci_tail = &dev->next; + + return dev; +} + +#define HP_CFG_RD(sz, bits, name) \ +static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ + return orig_pci_ops.name(dev, where, value); \ + \ + switch (where) { \ + case PCI_COMMAND: \ + *value = read##sz(fake_dev->mapped_csrs + where); \ + *value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \ + break; \ + case PCI_BASE_ADDRESS_0: \ + if (fake_dev->sizing) \ + *value = ~(fake_dev->csr_size - 1); \ + else \ + *value = (fake_dev->csr_base & \ + PCI_BASE_ADDRESS_MEM_MASK) | \ + PCI_BASE_ADDRESS_SPACE_MEMORY; \ + fake_dev->sizing = 0; \ + break; \ + default: \ + *value = read##sz(fake_dev->mapped_csrs + where); \ + break; \ + } \ + return PCIBIOS_SUCCESSFUL; \ +} + +#define HP_CFG_WR(sz, bits, name) \ +static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ + return orig_pci_ops.name(dev, where, value); \ + \ + switch (where) { \ + case PCI_BASE_ADDRESS_0: \ + if (value == ~0) \ + fake_dev->sizing = 1; \ + break; \ + default: \ + write##sz(value, fake_dev->mapped_csrs + where); \ + break; \ + } \ + return PCIBIOS_SUCCESSFUL; \ +} + +HP_CFG_RD(b, 8, read_byte) +HP_CFG_RD(w, 16, read_word) +HP_CFG_RD(l, 32, read_dword) +HP_CFG_WR(b, 8, write_byte) +HP_CFG_WR(w, 16, write_word) +HP_CFG_WR(l, 32, write_dword) + +static struct pci_ops hp_pci_conf = { + hp_cfg_readb, + hp_cfg_readw, + hp_cfg_readl, + hp_cfg_writeb, + hp_cfg_writew, + hp_cfg_writel, +}; + +/* + * Assume we'll never have a physical slot higher than 0x10, so we can + * use slots above that for "fake" PCI devices to represent things + * that only show up in the ACPI namespace. + */ +#define HP_MAX_SLOT 0x10 + +static struct fake_pci_dev * +hpzx1_fake_pci_dev(unsigned long addr, unsigned int bus, unsigned int size) +{ + struct fake_pci_dev *dev; + int slot; + + // Note: lspci thinks 0x1f is invalid + for (slot = 0x1e; slot > HP_MAX_SLOT; slot--) { + if (!fake_pci_find_slot(bus, PCI_DEVFN(slot, 0))) + break; + } + if (slot == HP_MAX_SLOT) { + printk(KERN_ERR PFX + "no slot space for device (0x%p) on bus 0x%02x\n", + (void *) addr, bus); + return NULL; + } + + dev = alloc_fake_pci_dev(); + if (!dev) { + printk(KERN_ERR PFX + "no memory for device (0x%p) on bus 0x%02x\n", + (void *) addr, bus); + return NULL; + } + + dev->bus = bus; + dev->devfn = PCI_DEVFN(slot, 0); + dev->csr_base = addr; + dev->csr_size = size; + + /* + * Drivers should ioremap what they need, but we have to do + * it here, too, so PCI config accesses work. + */ + dev->mapped_csrs = (unsigned long) ioremap(dev->csr_base, dev->csr_size); + + return dev; +} + +typedef struct { + u8 guid_id; + u8 guid[16]; + u8 csr_base[8]; + u8 csr_length[8]; +} acpi_hp_vendor_long; + +#define HP_CCSR_LENGTH 0x21 +#define HP_CCSR_TYPE 0x2 +#define HP_CCSR_GUID \ + ((efi_guid_t) { 0x69e9adf9, 0x924f, 0xab5f, { 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad }}) + +extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *); +extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *); +extern acpi_resource_data *acpi_get_crs_type(acpi_buffer *, int *, int); +extern void acpi_dispose_crs(acpi_buffer *); +extern acpi_status acpi_cf_evaluate_method(acpi_handle, UINT8 *, NATIVE_UINT *); + +static acpi_status +hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length) +{ + int i, offset = 0; + acpi_status status; + acpi_buffer buf; + acpi_resource_vendor *res; + acpi_hp_vendor_long *hp_res; + efi_guid_t vendor_guid; + + *csr_base = 0; + *csr_length = 0; + + status = acpi_get_crs(obj, &buf); + if (status != AE_OK) { + printk(KERN_ERR PFX "Unable to get _CRS data on object\n"); + return status; + } + + res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR); + if (!res) { + printk(KERN_ERR PFX "Failed to find config space for device\n"); + acpi_dispose_crs(&buf); + return AE_NOT_FOUND; + } + + hp_res = (acpi_hp_vendor_long *)(res->reserved); + + if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) { + printk(KERN_ERR PFX "Unknown Vendor data\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t)); + if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) { + printk(KERN_ERR PFX "Vendor GUID does not match\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + for (i = 0 ; i < 8 ; i++) { + *csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8)); + *csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8)); + } + + acpi_dispose_crs(&buf); + + return AE_OK; +} + +static acpi_status +hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret) +{ + u64 csr_base = 0, csr_length = 0; + char *name = context; + struct fake_pci_dev *dev; + acpi_status status; + + status = hp_csr_space(obj, &csr_base, &csr_length); + + if (status != AE_OK) + return status; + + /* + * Only SBA shows up in ACPI namespace, so its CSR space + * includes both SBA and IOC. Make SBA and IOC show up + * separately in PCI space. + */ + if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) + printk(KERN_INFO PFX "%s SBA at 0x%lx; pci dev %02x:%02x.%d\n", + name, csr_base, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, 0x1000))) + printk(KERN_INFO PFX "%s IOC at 0x%lx; pci dev %02x:%02x.%d\n", + name, csr_base + 0x1000, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + + return AE_OK; +} + +static acpi_status +hpzx1_lba_probe(acpi_handle obj, u32 depth, void *context, void **ret) +{ + acpi_status status; + u64 csr_base = 0, csr_length = 0; + char *name = context; + NATIVE_UINT busnum = 0; + struct fake_pci_dev *dev; + + status = hp_csr_space(obj, &csr_base, &csr_length); + + if (status != AE_OK) + return status; + + status = acpi_cf_evaluate_method(obj, METHOD_NAME__BBN, &busnum); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PFX "evaluate _BBN fail=0x%x\n", status); + busnum = 0; // no _BBN; stick it on bus 0 + } + + if ((dev = hpzx1_fake_pci_dev(csr_base, busnum, csr_length))) + printk(KERN_INFO PFX "%s LBA at 0x%lx, _BBN 0x%02x; " + "pci dev %02x:%02x.%d\n", + name, csr_base, busnum, dev->bus, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + + return AE_OK; +} + +static void +hpzx1_acpi_dev_init(void) +{ + extern struct pci_ops pci_conf; + + /* + * Make fake PCI devices for the following hardware in the + * ACPI namespace. This makes it more convenient for drivers + * because they can claim these devices based on PCI + * information, rather than needing to know about ACPI. The + * 64-bit "HPA" space for this hardware is available as BAR + * 0/1. + * + * HWP0001: Single IOC SBA w/o IOC in namespace + * HWP0002: LBA device + * HWP0003: AGP LBA device + */ + acpi_get_devices("HWP0001", hpzx1_sba_probe, "HWP0001", NULL); +#ifdef CONFIG_IA64_HP_PROTO + if (fake_pci_tail != &fake_pci_head) { +#endif + acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002", NULL); + acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003", NULL); + +#ifdef CONFIG_IA64_HP_PROTO + } + +#define ZX1_FUNC_ID_VALUE (PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP + /* + * Early protos don't have bridges in the ACPI namespace, so + * if we didn't find anything, add the things we know are + * there. + */ + if (fake_pci_tail == &fake_pci_head) { + u64 hpa, csr_base; + struct fake_pci_dev *dev; + + csr_base = 0xfed00000UL; + hpa = (u64) ioremap(csr_base, 0x1000); + if (__raw_readl(hpa) == ZX1_FUNC_ID_VALUE) { + if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) + printk(KERN_INFO PFX "HWP0001 SBA at 0x%lx; " + "pci dev %02x:%02x.%d\n", csr_base, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, + 0x1000))) + printk(KERN_INFO PFX "HWP0001 IOC at 0x%lx; " + "pci dev %02x:%02x.%d\n", + csr_base + 0x1000, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + + csr_base = 0xfed24000UL; + iounmap(hpa); + hpa = (u64) ioremap(csr_base, 0x1000); + if ((dev = hpzx1_fake_pci_dev(csr_base, 0x40, 0x1000))) + printk(KERN_INFO PFX "HWP0003 AGP LBA at " + "0x%lx; pci dev %02x:%02x.%d\n", + csr_base, + dev->bus, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + } + iounmap(hpa); + } +#endif + + if (fake_pci_tail == &fake_pci_head) + return; + + /* + * Replace PCI ops, but only if we made fake devices. + */ + orig_pci_ops = pci_conf; + pci_conf = hp_pci_conf; +} + +extern void sba_init(void); + +void +hpzx1_pci_fixup (int phase) +{ + if (phase == 0) + hpzx1_acpi_dev_init(); + iosapic_pci_fixup(phase); + if (phase == 1) + sba_init(); +} diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index 1efa2442d41e3116de12ce0ea00988bc40ecf433..a88c2c3aeb294cb8b5da5c31a0be8c5ade5e4685 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -47,6 +47,8 @@ static void elf32_set_personality (void); #define ELF_PLAT_INIT(_r) ia64_elf32_init(_r) #define setup_arg_pages(bprm) ia32_setup_arg_pages(bprm) #define elf_map elf32_map + +#undef SET_PERSONALITY #define SET_PERSONALITY(ex, ibcs2) elf32_set_personality() /* Ugly but avoids duplication */ diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 009a6448115e409602aed4e4b663ccd053c9d99b..0bcd16ac271e72012275fc51019fca58c936d1b8 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -3003,7 +3003,7 @@ sys32_ptrace (int request, pid_t pid, unsigned int addr, unsigned int data, if (request != PTRACE_KILL) goto out; } - if (child->p_pptr != current) + if (child->parent != current) goto out; switch (request) { diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index add409f97abc5f0f89e0cb4c4d3decb5c20b3cc4..9d67f9003285b55ebbdbecfd6710b1c2a49fbac3 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -17,6 +17,7 @@ obj-y := acpi.o entry.o gate.o efi.o efi_stub.o ia64_ksyms.o irq.o irq_ia64.o ir machvec.o pal.o process.o perfmon.o ptrace.o sal.o semaphore.o setup.o \ signal.o sys_ia64.o traps.o time.o unaligned.o unwind.o obj-$(CONFIG_IA64_GENERIC) += iosapic.o +obj-$(CONFIG_IA64_HP_ZX1) += iosapic.o obj-$(CONFIG_IA64_DIG) += iosapic.o obj-$(CONFIG_IA64_PALINFO) += palinfo.o obj-$(CONFIG_EFI_VARS) += efivars.o diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 4ce497759700bb18f14c772809ac482077c760aa..9b758ed7ceedbae5b6081eab1cae89f9d77e1f05 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -1,21 +1,34 @@ /* - * Advanced Configuration and Power Interface + * acpi.c - Architecture-Specific Low-Level ACPI Support * - * Based on 'ACPI Specification 1.0b' February 2, 1999 and - * 'IA-64 Extensions to ACPI Specification' Revision 0.6 + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com> + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 2000 Intel Corp. + * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> + * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * - * Copyright (C) 1999 VA Linux Systems - * Copyright (C) 1999, 2000 Walt Drummond <drummond@valinux.com> - * Copyright (C) 2000, 2002 Hewlett-Packard Co. - * David Mosberger-Tang <davidm@hpl.hp.com> - * Copyright (C) 2000 Intel Corp. - * Copyright (C) 2000, 2001 J.I. Lee <jung-ik.lee@intel.com> - * ACPI based kernel configuration manager. - * ACPI 2.0 & IA64 ext 0.71 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/config.h> - #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -23,31 +36,16 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/irq.h> -#ifdef CONFIG_SERIAL_ACPI -#include <linux/acpi_serial.h> -#endif - -#include <asm/acpi-ext.h> -#include <asm/acpikcfg.h> +#include <linux/acpi.h> #include <asm/efi.h> #include <asm/io.h> #include <asm/iosapic.h> #include <asm/machvec.h> #include <asm/page.h> +#include <asm/system.h> -#undef ACPI_DEBUG /* Guess what this does? */ - -/* global array to record platform interrupt vectors for generic int routing */ -int platform_irq_list[ACPI_MAX_PLATFORM_IRQS]; -/* These are ugly but will be reclaimed by the kernel */ -int __initdata available_cpus; -int __initdata total_cpus; - -int __initdata pcat_compat; - -void (*pm_idle) (void); -void (*pm_power_off) (void); +#define PREFIX "ACPI: " asm (".weak iosapic_register_irq"); asm (".weak iosapic_register_legacy_irq"); @@ -55,10 +53,16 @@ asm (".weak iosapic_register_platform_irq"); asm (".weak iosapic_init"); asm (".weak iosapic_version"); +void (*pm_idle) (void); +void (*pm_power_off) (void); + + +/* + * TBD: Should go away once we have an ACPI parser. + */ const char * acpi_get_sysname (void) { - /* the following should go away once we have an ACPI parser: */ #ifdef CONFIG_IA64_GENERIC return "hpsim"; #else @@ -74,16 +78,21 @@ acpi_get_sysname (void) # error Unknown platform. Fix acpi.c. # endif #endif - } +#ifdef CONFIG_ACPI_BOOT + +#define ACPI_MAX_PLATFORM_IRQS 256 + +/* Array to record platform interrupt vectors for generic interrupt routing. */ +int platform_irq_list[ACPI_MAX_PLATFORM_IRQS]; + /* - * Interrupt routing API for device drivers. - * Provides the interrupt vector for a generic platform event - * (currently only CPEI implemented) + * Interrupt routing API for device drivers. Provides interrupt vector for + * a generic platform event. Currently only CPEI is implemented. */ int -acpi_request_vector(u32 int_type) +acpi_request_vector (u32 int_type) { int vector = -1; @@ -96,586 +105,494 @@ acpi_request_vector(u32 int_type) return vector; } -/* - * Configure legacy IRQ information. - */ -static void __init -acpi_legacy_irq (char *p) -{ - acpi_entry_int_override_t *legacy = (acpi_entry_int_override_t *) p; - unsigned long polarity = 0, edge_triggered = 0; - /* - * If the platform we're running doesn't define - * iosapic_register_legacy_irq(), we ignore this info... - */ - if (!iosapic_register_legacy_irq) - return; - - switch (legacy->flags) { - case 0x5: polarity = 1; edge_triggered = 1; break; - case 0x7: polarity = 0; edge_triggered = 1; break; - case 0xd: polarity = 1; edge_triggered = 0; break; - case 0xf: polarity = 0; edge_triggered = 0; break; - default: - printk(" ACPI Legacy IRQ 0x%02x: Unknown flags 0x%x\n", legacy->isa_irq, - legacy->flags); - break; - } - iosapic_register_legacy_irq(legacy->isa_irq, legacy->pin, polarity, edge_triggered); -} +/* -------------------------------------------------------------------------- + Boot-time Table Parsing + -------------------------------------------------------------------------- */ + +static int total_cpus __initdata; +static int available_cpus __initdata; +struct acpi_table_madt * acpi_madt __initdata; -/* - * ACPI 2.0 tables parsing functions - */ -static unsigned long -readl_unaligned(void *p) +static int __init +acpi_parse_lapic_addr_ovr (acpi_table_entry_header *header) { - unsigned long ret; + struct acpi_table_lapic_addr_ovr *lapic = NULL; + + lapic = (struct acpi_table_lapic_addr_ovr *) header; + if (!lapic) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + if (lapic->address) { + iounmap((void *) ipi_base_addr); + ipi_base_addr = (unsigned long) ioremap(lapic->address, 0); + } - memcpy(&ret, p, sizeof(long)); - return ret; + return 0; } -/* - * Identify usable CPU's and remember them for SMP bringup later. - */ -static void __init -acpi20_lsapic (char *p) + +static int __init +acpi_parse_lsapic (acpi_table_entry_header *header) { - int add = 1; + struct acpi_table_lsapic *lsapic = NULL; - acpi20_entry_lsapic_t *lsapic = (acpi20_entry_lsapic_t *) p; - printk(" CPU %.04x:%.04x: ", lsapic->eid, lsapic->id); + lsapic = (struct acpi_table_lsapic *) header; + if (!lsapic) + return -EINVAL; - if ((lsapic->flags & LSAPIC_ENABLED) == 0) { - printk("disabled.\n"); - add = 0; - } + acpi_table_print_madt_entry(header); -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = -1; -#endif - if (add) { + printk("CPU %d (0x%04x)", total_cpus, (lsapic->id << 8) | lsapic->eid); + + if (lsapic->flags.enabled) { available_cpus++; - printk("available"); + printk(" enabled"); #ifdef CONFIG_SMP smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid; if (hard_smp_processor_id() == smp_boot_data.cpu_phys_id[total_cpus]) printk(" (BSP)"); #endif - printk(".\n"); } + else { + printk(" disabled"); +#ifdef CONFIG_SMP + smp_boot_data.cpu_phys_id[total_cpus] = -1; +#endif + } + + printk("\n"); + total_cpus++; + return 0; } -/* - * Extract iosapic info from madt (again) to determine which iosapic - * this platform interrupt resides in - */ + +static int __init +acpi_parse_lapic_nmi (acpi_table_entry_header *header) +{ + struct acpi_table_lapic_nmi *lacpi_nmi = NULL; + + lacpi_nmi = (struct acpi_table_lapic_nmi*) header; + if (!lacpi_nmi) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + /* TBD: Support lapic_nmi entries */ + + return 0; +} + + static int __init -acpi20_which_iosapic (int global_vector, acpi_madt_t *madt, u32 *irq_base, char **iosapic_address) +acpi_find_iosapic (int global_vector, u32 *irq_base, char **iosapic_address) { - acpi_entry_iosapic_t *iosapic; - char *p, *end; - int ver, max_pin; + struct acpi_table_iosapic *iosapic = NULL; + int ver = 0; + int max_pin = 0; + char *p = 0; + char *end = 0; + + if (!irq_base || !iosapic_address) + return -ENODEV; - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + p = (char *) (acpi_madt + 1); + end = p + (acpi_madt->header.length - sizeof(struct acpi_table_madt)); while (p < end) { - switch (*p) { - case ACPI20_ENTRY_IO_SAPIC: - /* collect IOSAPIC info for platform int use later */ - iosapic = (acpi_entry_iosapic_t *)p; - *irq_base = iosapic->irq_base; + if (*p == ACPI_MADT_IOSAPIC) { + iosapic = (struct acpi_table_iosapic *) p; + + *irq_base = iosapic->global_irq_base; *iosapic_address = ioremap(iosapic->address, 0); - /* is this the iosapic we're looking for? */ + ver = iosapic_version(*iosapic_address); max_pin = (ver >> 16) & 0xff; + if ((global_vector - *irq_base) <= max_pin) - return 0; /* found it! */ - break; - default: - break; + return 0; /* Found it! */ } p += p[1]; } - return 1; + return -ENODEV; } -/* - * Info on platform interrupt sources: NMI, PMI, INIT, etc. - */ -static void __init -acpi20_platform (char *p, acpi_madt_t *madt) + +static int __init +acpi_parse_iosapic (acpi_table_entry_header *header) { - int vector; - u32 irq_base; - char *iosapic_address; - unsigned long polarity = 0, trigger = 0; - acpi20_entry_platform_src_t *plat = (acpi20_entry_platform_src_t *) p; + struct acpi_table_iosapic *iosapic; - printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n", - plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); + iosapic = (struct acpi_table_iosapic *) header; + if (!iosapic) + return -EINVAL; - /* record platform interrupt vectors for generic int routing code */ + acpi_table_print_madt_entry(header); - if (!iosapic_register_platform_irq) { - printk("acpi20_platform(): no ACPI platform IRQ support\n"); - return; + if (iosapic_init) { +#ifndef CONFIG_ITANIUM + /* PCAT_COMPAT flag indicates dual-8259 setup */ + iosapic_init(iosapic->address, iosapic->global_irq_base, + acpi_madt->flags.pcat_compat); +#else + /* Firmware on old Itanium systems is broken */ + iosapic_init(iosapic->address, iosapic->global_irq_base, 1); +#endif } + return 0; +} + + +static int __init +acpi_parse_plat_int_src (acpi_table_entry_header *header) +{ + struct acpi_table_plat_int_src *plintsrc = NULL; + int vector = 0; + u32 irq_base = 0; + char *iosapic_address = NULL; + + plintsrc = (struct acpi_table_plat_int_src *) header; + if (!plintsrc) + return -EINVAL; - /* extract polarity and trigger info from flags */ - switch (plat->flags) { - case 0x5: polarity = 1; trigger = 1; break; - case 0x7: polarity = 0; trigger = 1; break; - case 0xd: polarity = 1; trigger = 0; break; - case 0xf: polarity = 0; trigger = 0; break; - default: - printk("acpi20_platform(): unknown flags 0x%x\n", plat->flags); - break; + acpi_table_print_madt_entry(header); + + if (!iosapic_register_platform_irq) { + printk(KERN_WARNING PREFIX "No ACPI platform IRQ support\n"); + return -ENODEV; } - /* which iosapic does this IRQ belong to? */ - if (acpi20_which_iosapic(plat->global_vector, madt, &irq_base, &iosapic_address)) { - printk("acpi20_platform(): I/O SAPIC not found!\n"); - return; + if (0 != acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) { + printk(KERN_WARNING PREFIX "IOSAPIC not found\n"); + return -ENODEV; } /* - * get vector assignment for this IRQ, set attributes, and program the IOSAPIC - * routing table + * Get vector assignment for this IRQ, set attributes, and program the + * IOSAPIC routing table. */ - vector = iosapic_register_platform_irq(plat->int_type, - plat->global_vector, - plat->iosapic_vector, - plat->eid, - plat->id, - polarity, - trigger, - irq_base, - iosapic_address); - platform_irq_list[plat->int_type] = vector; + vector = iosapic_register_platform_irq (plintsrc->type, + plintsrc->global_irq, + plintsrc->iosapic_vector, + plintsrc->eid, + plintsrc->id, + (plintsrc->flags.polarity == 1) ? 1 : 0, + (plintsrc->flags.trigger == 1) ? 1 : 0, + irq_base, + iosapic_address); + + platform_irq_list[plintsrc->type] = vector; + return 0; } -/* - * Override the physical address of the local APIC in the MADT stable header. - */ -static void __init -acpi20_lapic_addr_override (char *p) + +static int __init +acpi_parse_int_src_ovr (acpi_table_entry_header *header) { - acpi20_entry_lapic_addr_override_t * lapic = (acpi20_entry_lapic_addr_override_t *) p; + struct acpi_table_int_src_ovr *p = NULL; - if (lapic->lapic_address) { - iounmap((void *)ipi_base_addr); - ipi_base_addr = (unsigned long) ioremap(lapic->lapic_address, 0); + p = (struct acpi_table_int_src_ovr *) header; + if (!p) + return -EINVAL; - printk("LOCAL ACPI override to 0x%lx(p=0x%lx)\n", - ipi_base_addr, lapic->lapic_address); - } + acpi_table_print_madt_entry(header); + + /* Ignore if the platform doesn't support overrides */ + if (!iosapic_register_legacy_irq) + return 0; + + iosapic_register_legacy_irq(p->bus_irq, p->global_irq, + (p->flags.polarity == 1) ? 1 : 0, + (p->flags.trigger == 1) ? 1 : 0); + + return 0; } -/* - * Parse the ACPI Multiple APIC Description Table - */ -static void __init -acpi20_parse_madt (acpi_madt_t *madt) + +static int __init +acpi_parse_nmi_src (acpi_table_entry_header *header) { - acpi_entry_iosapic_t *iosapic = NULL; - acpi20_entry_lsapic_t *lsapic = NULL; - char *p, *end; - int i; - - /* Base address of IPI Message Block */ - if (madt->lapic_address) { - ipi_base_addr = (unsigned long) ioremap(madt->lapic_address, 0); - printk("Lapic address set to 0x%lx\n", ipi_base_addr); - } else - printk("Lapic address set to default 0x%lx\n", ipi_base_addr); + struct acpi_table_nmi_src *nmi_src = NULL; - /* - * The PCAT_COMPAT flag indicates that the system has a dual-8259 compatible - * setup. - */ -#ifdef CONFIG_ITANIUM - pcat_compat = 1; /* fw on some Itanium systems is broken... */ -#else - pcat_compat = (madt->flags & MADT_PCAT_COMPAT); -#endif + nmi_src = (struct acpi_table_nmi_src*) header; + if (!nmi_src) + return -EINVAL; - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + acpi_table_print_madt_entry(header); - /* Initialize platform interrupt vector array */ - for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++) - platform_irq_list[i] = -1; + /* TBD: Support nimsrc entries */ - /* - * Split-up entry parsing to ensure ordering. - */ - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE: - printk("ACPI 2.0 MADT: LOCAL APIC Override\n"); - acpi20_lapic_addr_override(p); - break; - - case ACPI20_ENTRY_LOCAL_SAPIC: - printk("ACPI 2.0 MADT: LOCAL SAPIC\n"); - lsapic = (acpi20_entry_lsapic_t *) p; - acpi20_lsapic(p); - break; - - case ACPI20_ENTRY_IO_SAPIC: - iosapic = (acpi_entry_iosapic_t *) p; - if (iosapic_init) - iosapic_init(iosapic->address, iosapic->irq_base, pcat_compat); - break; - - case ACPI20_ENTRY_PLATFORM_INT_SOURCE: - printk("ACPI 2.0 MADT: PLATFORM INT SOURCE\n"); - acpi20_platform(p, madt); - break; - - case ACPI20_ENTRY_LOCAL_APIC: - printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); break; - case ACPI20_ENTRY_IO_APIC: - printk("ACPI 2.0 MADT: IO APIC entry\n"); break; - case ACPI20_ENTRY_NMI_SOURCE: - printk("ACPI 2.0 MADT: NMI SOURCE entry\n"); break; - case ACPI20_ENTRY_LOCAL_APIC_NMI: - printk("ACPI 2.0 MADT: LOCAL APIC NMI entry\n"); break; - case ACPI20_ENTRY_INT_SRC_OVERRIDE: - break; - default: - printk("ACPI 2.0 MADT: unknown entry skip\n"); break; - break; - } - p += p[1]; - } + return 0; +} - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_LOCAL_APIC: - if (lsapic) break; - printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); - /* parse local apic if there's no local Sapic */ - break; - case ACPI20_ENTRY_IO_APIC: - if (iosapic) break; - printk("ACPI 2.0 MADT: IO APIC entry\n"); - /* parse ioapic if there's no ioSapic */ - break; - default: - break; - } - p += p[1]; - } +static int __init +acpi_parse_madt (unsigned long phys_addr, unsigned long size) +{ + int i = 0; - p = (char *) (madt + 1); - end = p + (madt->header.length - sizeof(acpi_madt_t)); + if (!phys_addr || !size) + return -EINVAL; - while (p < end) { - switch (*p) { - case ACPI20_ENTRY_INT_SRC_OVERRIDE: - printk("ACPI 2.0 MADT: INT SOURCE Override\n"); - acpi_legacy_irq(p); - break; - default: - break; - } - p += p[1]; + acpi_madt = (struct acpi_table_madt *) __va(phys_addr); + if (!acpi_madt) { + printk(KERN_WARNING PREFIX "Unable to map MADT\n"); + return -ENODEV; } - /* Make bootup pretty */ - printk(" %d CPUs available, %d CPUs total\n", - available_cpus, total_cpus); + /* Initialize platform interrupt vector array */ + + for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++) + platform_irq_list[i] = -1; + + /* Get base address of IPI Message Block */ + + if (acpi_madt->lapic_address) + ipi_base_addr = (unsigned long) + ioremap(acpi_madt->lapic_address, 0); + + printk(KERN_INFO PREFIX "Local APIC address 0x%lx\n", ipi_base_addr); + + return 0; } + int __init -acpi20_parse (acpi20_rsdp_t *rsdp20) +acpi_find_rsdp (unsigned long *rsdp_phys) { -# ifdef CONFIG_ACPI - acpi_xsdt_t *xsdt; - acpi_desc_table_hdr_t *hdrp; - acpi_madt_t *madt = NULL; - int tables, i; - - if (strncmp(rsdp20->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { - printk("ACPI 2.0 RSDP signature incorrect!\n"); - return 0; - } else { - printk("ACPI 2.0 Root System Description Ptr at 0x%lx\n", - (unsigned long)rsdp20); - } + if (!rsdp_phys) + return -EINVAL; - xsdt = __va(rsdp20->xsdt); - hdrp = &xsdt->header; - if (strncmp(hdrp->signature, - ACPI_XSDT_SIG, ACPI_XSDT_SIG_LEN)) { - printk("ACPI 2.0 XSDT signature incorrect. Trying RSDT\n"); - /* RSDT parsing here */ + if (efi.acpi20) { + (*rsdp_phys) = __pa(efi.acpi20); return 0; - } else { - printk("ACPI 2.0 XSDT at 0x%lx (p=0x%lx)\n", - (unsigned long)xsdt, (unsigned long)rsdp20->xsdt); + } + else if (efi.acpi) { + printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n"); } - printk("ACPI 2.0: %.6s %.8s %d.%d\n", - hdrp->oem_id, - hdrp->oem_table_id, - hdrp->oem_revision >> 16, - hdrp->oem_revision & 0xffff); + return -ENODEV; +} - acpi_cf_init((void *)rsdp20); - tables =(hdrp->length -sizeof(acpi_desc_table_hdr_t))>>3; +#ifdef CONFIG_SERIAL_ACPI - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i])); - printk(" :table %4.4s found\n", hdrp->signature); +#include <linux/acpi_serial.h> - /* Only interested int the MADT table for now ... */ - if (strncmp(hdrp->signature, - ACPI_MADT_SIG, ACPI_MADT_SIG_LEN) != 0) - continue; +static int __init +acpi_parse_spcr (unsigned long phys_addr, unsigned long size) +{ + acpi_ser_t *spcr = NULL; + unsigned long global_int = 0; - /* Save MADT pointer for later */ - madt = (acpi_madt_t *) hdrp; - acpi20_parse_madt(madt); - } + if (!phys_addr || !size) + return -EINVAL; + + if (!iosapic_register_irq) + return -ENODEV; -#ifdef CONFIG_SERIAL_ACPI /* - * Now we're interested in other tables. We want the iosapics already - * initialized, so we do it in a separate loop. + * ACPI is able to describe serial ports that live at non-standard + * memory addresses and use non-standard interrupts, either via + * direct SAPIC mappings or via PCI interrupts. We handle interrupt + * routing for SAPIC-based (non-PCI) devices here. Interrupt routing + * for PCI devices will be handled when processing the PCI Interrupt + * Routing Table (PRT). */ - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i])); - /* - * search for SPCR and DBGP table entries so we can enable - * non-pci interrupts to IO-SAPICs. - */ - if (!strncmp(hdrp->signature, ACPI_SPCRT_SIG, ACPI_SPCRT_SIG_LEN) || - !strncmp(hdrp->signature, ACPI_DBGPT_SIG, ACPI_DBGPT_SIG_LEN)) - { - acpi_ser_t *spcr = (void *)hdrp; - unsigned long global_int; - - setup_serial_acpi(hdrp); - - /* - * ACPI is able to describe serial ports that live at non-standard - * memory space addresses and use SAPIC interrupts. If not also - * PCI devices, there would be no interrupt vector information for - * them. This checks for and fixes that situation. - */ - if (spcr->length < sizeof(acpi_ser_t)) - /* table is not long enough for full info, thus no int */ - break; - - /* - * If the device is not in PCI space, but uses a SAPIC interrupt, - * we need to program the SAPIC so that serial can autoprobe for - * the IA64 interrupt vector later on. If the device is in PCI - * space, it should already be setup via the PCI vectors - */ - if (spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE && - spcr->int_type == ACPI_SERIAL_INT_SAPIC) - { - u32 irq_base; - char *iosapic_address; - int vector; - - /* We have a UART in memory space with a SAPIC interrupt */ - global_int = ( (spcr->global_int[3] << 24) - | (spcr->global_int[2] << 16) - | (spcr->global_int[1] << 8) - | spcr->global_int[0]); - - if (!iosapic_register_irq) - continue; - - /* which iosapic does this IRQ belong to? */ - if (acpi20_which_iosapic(global_int, madt, &irq_base, - &iosapic_address) == 0) - { - vector = iosapic_register_irq(global_int, - 1, /* active high polarity */ - 1, /* edge triggered */ - irq_base, - iosapic_address); - } - } - } + + spcr = (acpi_ser_t *) __va(phys_addr); + if (!spcr) { + printk(KERN_WARNING PREFIX "Unable to map SPCR\n"); + return -ENODEV; } -#endif - acpi_cf_terminate(); -# ifdef CONFIG_SMP - if (available_cpus == 0) { - printk("ACPI: Found 0 CPUS; assuming 1\n"); - available_cpus = 1; /* We've got at least one of these, no? */ + setup_serial_acpi(spcr); + + if (spcr->length < sizeof(acpi_ser_t)) + /* Table not long enough for full info, thus no interrupt */ + return -ENODEV; + + if ((spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE) && + (spcr->int_type == ACPI_SERIAL_INT_SAPIC)) + { + u32 irq_base = 0; + char *iosapic_address = NULL; + int vector = 0; + + /* We have a UART in memory space with an SAPIC interrupt */ + + global_int = ( (spcr->global_int[3] << 24) | + (spcr->global_int[2] << 16) | + (spcr->global_int[1] << 8) | + (spcr->global_int[0]) ); + + /* Which iosapic does this IRQ belong to? */ + + if (0 == acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) { + vector = iosapic_register_irq (global_int, 1, 1, + irq_base, iosapic_address); + } } - smp_boot_data.cpu_count = total_cpus; -# endif -# endif /* CONFIG_ACPI */ - return 1; + return 0; } -/* - * ACPI 1.0b with 0.71 IA64 extensions functions; should be removed once all - * platforms start supporting ACPI 2.0 - */ -/* - * Identify usable CPU's and remember them for SMP bringup later. - */ -static void __init -acpi_lsapic (char *p) +#endif /*CONFIG_SERIAL_ACPI*/ + + +int __init +acpi_boot_init (char *cmdline) { - int add = 1; + int result = 0; + + /* Initialize the ACPI boot-time table parser */ + result = acpi_table_init(cmdline); + if (0 != result) + return result; - acpi_entry_lsapic_t *lsapic = (acpi_entry_lsapic_t *) p; + /* + * MADT + * ---- + * Parse the Multiple APIC Description Table (MADT), if exists. + * Note that this table provides platform SMP configuration + * information -- the successor to MPS tables. + */ - if ((lsapic->flags & LSAPIC_PRESENT) == 0) - return; + result = acpi_table_parse(ACPI_APIC, acpi_parse_madt); + if (1 > result) + return result; - printk(" CPU %d (%.04x:%.04x): ", total_cpus, lsapic->eid, lsapic->id); + /* Local APIC */ - if ((lsapic->flags & LSAPIC_ENABLED) == 0) { - printk("Disabled.\n"); - add = 0; - } else if (lsapic->flags & LSAPIC_PERFORMANCE_RESTRICTED) { - printk("Performance Restricted; ignoring.\n"); - add = 0; + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); + return result; } -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = -1; -#endif - if (add) { - printk("Available.\n"); - available_cpus++; -#ifdef CONFIG_SMP - smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid; -#endif /* CONFIG_SMP */ + result = acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic); + if (1 > result) { + printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n"); + return -ENODEV; } - total_cpus++; -} -/* - * Info on platform interrupt sources: NMI. PMI, INIT, etc. - */ -static void __init -acpi_platform (char *p) -{ - acpi_entry_platform_src_t *plat = (acpi_entry_platform_src_t *) p; + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); + return result; + } - printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n", - plat->iosapic_vector, plat->global_vector, plat->eid, plat->id); -} + /* I/O APIC */ -/* - * Parse the ACPI Multiple SAPIC Table - */ -static void __init -acpi_parse_msapic (acpi_sapic_t *msapic) -{ - acpi_entry_iosapic_t *iosapic; - char *p, *end; + result = acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic); + if (1 > result) { + printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n"); + return ((result == 0) ? -ENODEV : result); + } - /* Base address of IPI Message Block */ - ipi_base_addr = (unsigned long) ioremap(msapic->interrupt_block, 0); + /* System-Level Interrupt Routing */ - p = (char *) (msapic + 1); - end = p + (msapic->header.length - sizeof(acpi_sapic_t)); + result = acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n"); + return result; + } - while (p < end) { - switch (*p) { - case ACPI_ENTRY_LOCAL_SAPIC: - acpi_lsapic(p); - break; - - case ACPI_ENTRY_IO_SAPIC: - iosapic = (acpi_entry_iosapic_t *) p; - if (iosapic_init) - /* - * The ACPI I/O SAPIC table doesn't have a PCAT_COMPAT - * flag like the MADT table, but we can safely assume that - * ACPI 1.0b systems have a dual-8259 setup. - */ - iosapic_init(iosapic->address, iosapic->irq_base, 1); - break; - - case ACPI_ENTRY_INT_SRC_OVERRIDE: - acpi_legacy_irq(p); - break; - - case ACPI_ENTRY_PLATFORM_INT_SOURCE: - acpi_platform(p); - break; - - default: - break; - } + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); + return result; + } - /* Move to next table entry. */ - p += p[1]; + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + if (0 > result) { + printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); + return result; + } + +#ifdef CONFIG_SERIAL_ACPI + /* + * TBD: Need phased approach to table parsing (only do those absolutely + * required during boot-up). Recommend expanding concept of fix- + * feature devices (LDM) to include table-based devices such as + * serial ports, EC, SMBus, etc. + */ + acpi_table_parse(ACPI_SPCR, acpi_parse_spcr); +#endif /*CONFIG_SERIAL_ACPI*/ + +#ifdef CONFIG_SMP + if (available_cpus == 0) { + printk("ACPI: Found 0 CPUS; assuming 1\n"); + available_cpus = 1; /* We've got at least one of these, no? */ } + smp_boot_data.cpu_count = total_cpus; +#endif + /* Make boot-up look pretty */ + printk("%d CPUs available, %d CPUs total\n", available_cpus, total_cpus); - /* Make bootup pretty */ - printk(" %d CPUs available, %d CPUs total\n", available_cpus, total_cpus); + return 0; } + +/* -------------------------------------------------------------------------- + PCI Interrupt Routing + -------------------------------------------------------------------------- */ + int __init -acpi_parse (acpi_rsdp_t *rsdp) +acpi_get_prt (struct pci_vector_struct **vectors, int *count) { -# ifdef CONFIG_ACPI - acpi_rsdt_t *rsdt; - acpi_desc_table_hdr_t *hdrp; - long tables, i; + struct pci_vector_struct *vector = NULL; + struct list_head *node = NULL; + struct acpi_prt_entry *entry = NULL; + int i = 0; - if (strncmp(rsdp->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) { - printk("Uh-oh, ACPI RSDP signature incorrect!\n"); - return 0; - } + if (!vectors || !count) + return -EINVAL; - rsdt = __va(rsdp->rsdt); - if (strncmp(rsdt->header.signature, ACPI_RSDT_SIG, ACPI_RSDT_SIG_LEN)) { - printk("Uh-oh, ACPI RDST signature incorrect!\n"); - return 0; + *vectors = NULL; + *count = 0; + + if (acpi_prts.count < 0) { + printk(KERN_ERR PREFIX "No PCI IRQ routing entries\n"); + return -ENODEV; } - printk("ACPI: %.6s %.8s %d.%d\n", rsdt->header.oem_id, rsdt->header.oem_table_id, - rsdt->header.oem_revision >> 16, rsdt->header.oem_revision & 0xffff); + /* Allocate vectors */ - acpi_cf_init(rsdp); + *vectors = kmalloc(sizeof(struct pci_vector_struct) * acpi_prts.count, GFP_KERNEL); + if (!(*vectors)) + return -ENOMEM; - tables = (rsdt->header.length - sizeof(acpi_desc_table_hdr_t)) / 8; - for (i = 0; i < tables; i++) { - hdrp = (acpi_desc_table_hdr_t *) __va(rsdt->entry_ptrs[i]); + /* Convert PRT entries to IOSAPIC PCI vectors */ - /* Only interested int the MSAPIC table for now ... */ - if (strncmp(hdrp->signature, ACPI_SAPIC_SIG, ACPI_SAPIC_SIG_LEN) != 0) - continue; + vector = *vectors; - acpi_parse_msapic((acpi_sapic_t *) hdrp); + list_for_each(node, &acpi_prts.entries) { + entry = (struct acpi_prt_entry *)node; + vector[i].bus = (u16) entry->id.bus; + vector[i].pci_id = (u32) entry->id.dev << 16 | 0xffff; + vector[i].pin = (u8) entry->id.pin; + vector[i].irq = (u8) entry->source.index; + i++; } + *count = acpi_prts.count; + return 0; +} - acpi_cf_terminate(); +/* Assume IA64 always use I/O SAPIC */ -# ifdef CONFIG_SMP - if (available_cpus == 0) { - printk("ACPI: Found 0 CPUS; assuming 1\n"); - available_cpus = 1; /* We've got at least one of these, no? */ - } - smp_boot_data.cpu_count = total_cpus; -# endif -# endif /* CONFIG_ACPI */ - return 1; +int __init +acpi_get_interrupt_model (int *type) +{ + if (!type) + return -EINVAL; + + *type = ACPI_INT_MODEL_IOSAPIC; + + return 0; } + +#endif /* CONFIG_ACPI_BOOT */ diff --git a/arch/ia64/kernel/brl_emu.c b/arch/ia64/kernel/brl_emu.c index abfd6a870976625f5f587e6eb18b8bd4d0703676..6101686da2a927349ef673ab38239a6e82d55d0c 100644 --- a/arch/ia64/kernel/brl_emu.c +++ b/arch/ia64/kernel/brl_emu.c @@ -55,7 +55,7 @@ struct illegal_op_return ia64_emulate_brl (struct pt_regs *regs, unsigned long ar_ec) { unsigned long bundle[2]; - unsigned long opcode, btype, qp, offset; + unsigned long opcode, btype, qp, offset, cpl; unsigned long next_ip; struct siginfo siginfo; struct illegal_op_return rv; @@ -158,9 +158,9 @@ ia64_emulate_brl (struct pt_regs *regs, unsigned long ar_ec) * AR[PFS].pec = AR[EC] * AR[PFS].ppl = PSR.cpl */ + cpl = ia64_psr(regs)->cpl; regs->ar_pfs = ((regs->cr_ifs & 0x3fffffffff) - | (ar_ec << 52) - | ((unsigned long) ia64_psr(regs)->cpl << 62)); + | (ar_ec << 52) | (cpl << 62)); /* * CFM.sof -= CFM.sol diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 9f961ed9e455c86b2aeaae82fe651a027f6d3d95..9c35117d738ab5178082b48c7162af91ce999473 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -155,10 +155,10 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg) case EFI_CONVENTIONAL_MEMORY: if (!(md->attribute & EFI_MEMORY_WB)) continue; - if (md->phys_addr + (md->num_pages << 12) > mem_limit) { + if (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) > mem_limit) { if (md->phys_addr > mem_limit) continue; - md->num_pages = (mem_limit - md->phys_addr) >> 12; + md->num_pages = (mem_limit - md->phys_addr) >> EFI_PAGE_SHIFT; } if (md->num_pages == 0) { printk("efi_memmap_walk: ignoring empty region at 0x%lx", @@ -167,7 +167,7 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg) } curr.start = PAGE_OFFSET + md->phys_addr; - curr.end = curr.start + (md->num_pages << 12); + curr.end = curr.start + (md->num_pages << EFI_PAGE_SHIFT); if (!prev_valid) { prev = curr; @@ -250,16 +250,17 @@ efi_map_pal_code (void) * dedicated ITR for the PAL code. */ if ((vaddr & mask) == (KERNEL_START & mask)) { - printk(__FUNCTION__ ": no need to install ITR for PAL code\n"); + printk("%s: no need to install ITR for PAL code\n", __FUNCTION__); continue; } - if (md->num_pages << 12 > IA64_GRANULE_SIZE) + if (md->num_pages << EFI_PAGE_SHIFT > IA64_GRANULE_SIZE) panic("Woah! PAL code size bigger than a granule!"); mask = ~((1 << IA64_GRANULE_SHIFT) - 1); printk("CPU %d: mapping PAL code [0x%lx-0x%lx) into [0x%lx-0x%lx)\n", - smp_processor_id(), md->phys_addr, md->phys_addr + (md->num_pages << 12), + smp_processor_id(), md->phys_addr, + md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), vaddr & mask, (vaddr & mask) + IA64_GRANULE_SIZE); /* @@ -375,7 +376,8 @@ efi_init (void) md = p; printk("mem%02u: type=%u, attr=0x%lx, range=[0x%016lx-0x%016lx) (%luMB)\n", i, md->type, md->attribute, md->phys_addr, - md->phys_addr + (md->num_pages<<12) - 1, md->num_pages >> 8); + md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1, + md->num_pages >> (20 - EFI_PAGE_SHIFT)); } } #endif @@ -482,8 +484,50 @@ efi_get_iobase (void) return 0; } +u32 +efi_mem_type (u64 phys_addr) +{ + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + u64 efi_desc_size; + + efi_map_start = __va(ia64_boot_param->efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size; + efi_desc_size = ia64_boot_param->efi_memdesc_size; + + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + + if ((md->phys_addr <= phys_addr) && (phys_addr <= + (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1))) + return md->type; + } + return 0; +} + +u64 +efi_mem_attributes (u64 phys_addr) +{ + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + u64 efi_desc_size; + + efi_map_start = __va(ia64_boot_param->efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size; + efi_desc_size = ia64_boot_param->efi_memdesc_size; + + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + + if ((md->phys_addr <= phys_addr) && (phys_addr <= + (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1))) + return md->attribute; + } + return 0; +} + static void __exit -efivars_exit(void) +efivars_exit (void) { #ifdef CONFIG_PROC_FS remove_proc_entry(efi_dir->name, NULL); diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index a32530a033a62f878e38e612dd8f96a0e5395450..5760abe91602076e4c0ff46c2aed2d0f845910a0 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -36,6 +36,7 @@ #include <asm/kregs.h> #include <asm/offsets.h> #include <asm/pgtable.h> +#include <asm/percpu.h> #include <asm/processor.h> #include <asm/thread_info.h> #include <asm/unistd.h> @@ -214,62 +215,80 @@ GLOBAL_ENTRY(save_switch_stack) .save @priunat,r17 mov r17=ar.unat // preserve caller's .body - adds r3=80,sp +#ifdef CONFIG_ITANIUM + adds r2=16+128,sp + adds r3=16+64,sp + adds r14=SW(R4)+16,sp ;; + st8.spill [r14]=r4,16 // spill r4 lfetch.fault.excl.nt1 [r3],128 - mov ar.rsc=0 // put RSE in mode: enforced lazy, little endian, pl 0 - adds r2=16+128,sp ;; lfetch.fault.excl.nt1 [r2],128 lfetch.fault.excl.nt1 [r3],128 - adds r14=SW(R4)+16,sp ;; lfetch.fault.excl [r2] lfetch.fault.excl [r3] adds r15=SW(R5)+16,sp +#else + add r2=16+3*128,sp + add r3=16,sp + add r14=SW(R4)+16,sp + ;; + st8.spill [r14]=r4,SW(R6)-SW(R4) // spill r4 and prefetch offset 0x1c0 + lfetch.fault.excl.nt1 [r3],128 // prefetch offset 0x010 ;; - mov r18=ar.fpsr // preserve fpsr - mov r19=ar.rnat - add r2=SW(F2)+16,sp // r2 = &sw->f2 -.mem.offset 0,0; st8.spill [r14]=r4,16 // spill r4 -.mem.offset 8,0; st8.spill [r15]=r5,16 // spill r5 - add r3=SW(F3)+16,sp // r3 = &sw->f3 + lfetch.fault.excl.nt1 [r3],128 // prefetch offset 0x090 + lfetch.fault.excl.nt1 [r2],128 // prefetch offset 0x190 + ;; + lfetch.fault.excl.nt1 [r3] // prefetch offset 0x110 + lfetch.fault.excl.nt1 [r2] // prefetch offset 0x210 + adds r15=SW(R5)+16,sp +#endif + ;; + st8.spill [r15]=r5,SW(R7)-SW(R5) // spill r5 + mov.m ar.rsc=0 // put RSE in mode: enforced lazy, little endian, pl 0 + add r2=SW(F2)+16,sp // r2 = &sw->f2 + ;; + st8.spill [r14]=r6,SW(B0)-SW(R6) // spill r6 + mov.m r18=ar.fpsr // preserve fpsr + add r3=SW(F3)+16,sp // r3 = &sw->f3 ;; stf.spill [r2]=f2,32 - stf.spill [r3]=f3,32 + mov.m r19=ar.rnat mov r21=b0 -.mem.offset 0,0; st8.spill [r14]=r6,16 // spill r6 -.mem.offset 8,0; st8.spill [r15]=r7,16 // spill r7 + + stf.spill [r3]=f3,32 + st8.spill [r15]=r7,SW(B2)-SW(R7) // spill r7 mov r22=b1 ;; // since we're done with the spills, read and save ar.unat: - mov r29=ar.unat // M-unit - mov r20=ar.bspstore // M-unit + mov.m r29=ar.unat + mov.m r20=ar.bspstore mov r23=b2 stf.spill [r2]=f4,32 stf.spill [r3]=f5,32 mov r24=b3 ;; - st8 [r14]=r21,16 // save b0 - st8 [r15]=r22,16 // save b1 + st8 [r14]=r21,SW(B1)-SW(B0) // save b0 + st8 [r15]=r23,SW(B3)-SW(B2) // save b2 mov r25=b4 stf.spill [r2]=f10,32 stf.spill [r3]=f11,32 mov r26=b5 ;; - st8 [r14]=r23,16 // save b2 - st8 [r15]=r24,16 // save b3 + st8 [r14]=r22,SW(B4)-SW(B1) // save b1 + st8 [r15]=r24,SW(AR_PFS)-SW(B3) // save b3 mov r21=ar.lc // I-unit stf.spill [r2]=f12,32 stf.spill [r3]=f13,32 ;; - st8 [r14]=r25,16 // save b4 - st8 [r15]=r26,16 // save b5 + st8 [r14]=r25,SW(B5)-SW(B4) // save b4 + st8 [r15]=r16,SW(AR_LC)-SW(AR_PFS) // save ar.pfs stf.spill [r2]=f14,32 stf.spill [r3]=f15,32 ;; - st8 [r14]=r16 // save ar.pfs - st8 [r15]=r21 // save ar.lc + st8 [r14]=r26 // save b5 + st8 [r15]=r21 // save ar.lc stf.spill [r2]=f16,32 stf.spill [r3]=f17,32 ;; @@ -284,26 +303,26 @@ GLOBAL_ENTRY(save_switch_stack) ;; stf.spill [r2]=f24,32 stf.spill [r3]=f25,32 - add r14=SW(CALLER_UNAT)+16,sp ;; stf.spill [r2]=f26,32 stf.spill [r3]=f27,32 - add r15=SW(AR_FPSR)+16,sp ;; stf.spill [r2]=f28,32 stf.spill [r3]=f29,32 - st8 [r14]=r17 // save caller_unat - st8 [r15]=r18 // save fpsr - mov r21=pr ;; - stf.spill [r2]=f30,(SW(AR_UNAT)-SW(F30)) - stf.spill [r3]=f31,(SW(AR_RNAT)-SW(F31)) + stf.spill [r2]=f30,SW(AR_UNAT)-SW(F30) + stf.spill [r3]=f31,SW(PR)-SW(F31) + add r14=SW(CALLER_UNAT)+16,sp ;; - st8 [r2]=r29,16 // save ar.unat - st8 [r3]=r19,16 // save ar.rnat + st8 [r2]=r29,SW(AR_RNAT)-SW(AR_UNAT) // save ar.unat + st8 [r14]=r17,SW(AR_FPSR)-SW(CALLER_UNAT) // save caller_unat + mov r21=pr + ;; + st8 [r2]=r19,SW(AR_BSPSTORE)-SW(AR_RNAT) // save ar.rnat + st8 [r3]=r21 // save predicate registers ;; - st8 [r2]=r20 // save ar.bspstore - st8 [r3]=r21 // save predicate registers + st8 [r2]=r20 // save ar.bspstore + st8 [r14]=r18 // save fpsr mov ar.rsc=3 // put RSE back into eager mode, pl 0 br.cond.sptk.many b7 END(save_switch_stack) @@ -647,23 +666,38 @@ dont_preserve_current_frame: /* * To prevent leaking bits between the kernel and user-space, * we must clear the stacked registers in the "invalid" partition here. - * Not pretty, but at least it's fast (3.34 registers/cycle). - * Architecturally, this loop could go at 4.67 registers/cycle, but that would - * oversubscribe Itanium. + * Not pretty, but at least it's fast (3.34 registers/cycle on Itanium, + * 5 registers/cycle on McKinley). */ # define pRecurse p6 # define pReturn p7 +#ifdef CONFIG_ITANIUM # define Nregs 10 +#else +# define Nregs 14 +#endif alloc loc0=ar.pfs,2,Nregs-2,2,0 shr.u loc1=r18,9 // RNaTslots <= dirtySize / (64*8) + 1 sub r17=r17,r18 // r17 = (physStackedSize + 8) - dirtySize ;; +#if 1 + .align 32 // see comment below about gas bug... +#endif mov ar.rsc=r19 // load ar.rsc to be used for "loadrs" shladd in0=loc1,3,r17 mov in1=0 +#if 0 + // gas-2.12.90 is unable to generate a stop bit after .align, which is bad, + // because alloc must be at the beginning of an insn-group. + .align 32 +#else + nop 0 + nop 0 + nop 0 +#endif ;; -// .align 32 // gas-2.11.90 is unable to generate a stop bit after .align rse_clear_invalid: +#ifdef CONFIG_ITANIUM // cycle 0 { .mii alloc loc0=ar.pfs,2,Nregs-2,2,0 @@ -692,9 +726,31 @@ rse_clear_invalid: mov loc7=0 (pReturn) br.ret.sptk.many b6 } +#else /* !CONFIG_ITANIUM */ + alloc loc0=ar.pfs,2,Nregs-2,2,0 + cmp.lt pRecurse,p0=Nregs*8,in0 // if more than Nregs regs left to clear, (re)curse + add out0=-Nregs*8,in0 + add out1=1,in1 // increment recursion count + mov loc1=0 + mov loc2=0 + ;; + mov loc3=0 + mov loc4=0 + mov loc9=0 + mov loc5=0 + mov loc6=0 +(pRecurse) br.call.sptk.many b6=rse_clear_invalid + ;; + mov loc7=0 + mov loc8=0 + cmp.ne pReturn,p0=r0,in1 // if recursion count != 0, we need to do a br.ret + mov loc10=0 + mov loc11=0 +(pReturn) br.ret.sptk.many b6 +#endif /* !CONFIG_ITANIUM */ # undef pRecurse # undef pReturn - + ;; alloc r17=ar.pfs,0,0,0,0 // drop current register frame ;; loadrs @@ -1087,7 +1143,11 @@ sys_call_table: data8 sys_sched_get_priority_min data8 sys_sched_rr_get_interval data8 sys_nanosleep +#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) data8 sys_nfsservctl +#else + data8 sys_ni_syscall +#endif data8 sys_prctl // 1170 data8 sys_getpagesize data8 sys_mmap2 @@ -1147,14 +1207,10 @@ sys_call_table: data8 sys_removexattr data8 sys_lremovexattr data8 sys_fremovexattr -#if 0 data8 sys_tkill -#else - data8 ia64_ni_syscall -#endif - data8 ia64_ni_syscall // 1230 - data8 ia64_ni_syscall - data8 ia64_ni_syscall + data8 sys_futex // 1230 + data8 sys_sched_setaffinity + data8 sys_sched_getaffinity data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1235 diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S index 1d1e63e0589c4ae97117e968d39e27cdf88ebd69..7aca9dab40f170f94d7105b39c841cf061f52cde 100644 --- a/arch/ia64/kernel/head.S +++ b/arch/ia64/kernel/head.S @@ -560,137 +560,114 @@ GLOBAL_ENTRY(__ia64_load_fpu) END(__ia64_load_fpu) GLOBAL_ENTRY(__ia64_init_fpu) - alloc r2=ar.pfs,0,0,0,0 - stf.spill [sp]=f0 - mov f32=f0 - ;; - ldf.fill f33=[sp] - ldf.fill f34=[sp] - mov f35=f0 - ;; - ldf.fill f36=[sp] - ldf.fill f37=[sp] - mov f38=f0 - ;; - ldf.fill f39=[sp] - ldf.fill f40=[sp] - mov f41=f0 - ;; - ldf.fill f42=[sp] - ldf.fill f43=[sp] - mov f44=f0 - ;; - ldf.fill f45=[sp] - ldf.fill f46=[sp] - mov f47=f0 - ;; - ldf.fill f48=[sp] - ldf.fill f49=[sp] - mov f50=f0 - ;; - ldf.fill f51=[sp] - ldf.fill f52=[sp] - mov f53=f0 - ;; - ldf.fill f54=[sp] - ldf.fill f55=[sp] - mov f56=f0 - ;; - ldf.fill f57=[sp] - ldf.fill f58=[sp] - mov f59=f0 - ;; - ldf.fill f60=[sp] - ldf.fill f61=[sp] - mov f62=f0 - ;; - ldf.fill f63=[sp] - ldf.fill f64=[sp] - mov f65=f0 - ;; - ldf.fill f66=[sp] - ldf.fill f67=[sp] - mov f68=f0 - ;; - ldf.fill f69=[sp] - ldf.fill f70=[sp] - mov f71=f0 - ;; - ldf.fill f72=[sp] - ldf.fill f73=[sp] - mov f74=f0 - ;; - ldf.fill f75=[sp] - ldf.fill f76=[sp] - mov f77=f0 - ;; - ldf.fill f78=[sp] - ldf.fill f79=[sp] - mov f80=f0 - ;; - ldf.fill f81=[sp] - ldf.fill f82=[sp] - mov f83=f0 - ;; - ldf.fill f84=[sp] - ldf.fill f85=[sp] - mov f86=f0 - ;; - ldf.fill f87=[sp] - ldf.fill f88=[sp] - mov f89=f0 - ;; - ldf.fill f90=[sp] - ldf.fill f91=[sp] - mov f92=f0 - ;; - ldf.fill f93=[sp] - ldf.fill f94=[sp] - mov f95=f0 - ;; - ldf.fill f96=[sp] - ldf.fill f97=[sp] - mov f98=f0 - ;; - ldf.fill f99=[sp] - ldf.fill f100=[sp] - mov f101=f0 - ;; - ldf.fill f102=[sp] - ldf.fill f103=[sp] - mov f104=f0 - ;; - ldf.fill f105=[sp] - ldf.fill f106=[sp] - mov f107=f0 - ;; - ldf.fill f108=[sp] - ldf.fill f109=[sp] - mov f110=f0 - ;; - ldf.fill f111=[sp] - ldf.fill f112=[sp] - mov f113=f0 - ;; - ldf.fill f114=[sp] - ldf.fill f115=[sp] - mov f116=f0 - ;; - ldf.fill f117=[sp] - ldf.fill f118=[sp] - mov f119=f0 - ;; - ldf.fill f120=[sp] - ldf.fill f121=[sp] - mov f122=f0 - ;; - ldf.fill f123=[sp] - ldf.fill f124=[sp] - mov f125=f0 + stf.spill [sp]=f0 // M3 + mov f32=f0 // F + nop.b 0 + + ldfps f33,f34=[sp] // M0 + ldfps f35,f36=[sp] // M1 + mov f37=f0 // F ;; - ldf.fill f126=[sp] - mov f127=f0 - br.ret.sptk.many rp + + setf.s f38=r0 // M2 + setf.s f39=r0 // M3 + mov f40=f0 // F + + ldfps f41,f42=[sp] // M0 + ldfps f43,f44=[sp] // M1 + mov f45=f0 // F + + setf.s f46=r0 // M2 + setf.s f47=r0 // M3 + mov f48=f0 // F + + ldfps f49,f50=[sp] // M0 + ldfps f51,f52=[sp] // M1 + mov f53=f0 // F + + setf.s f54=r0 // M2 + setf.s f55=r0 // M3 + mov f56=f0 // F + + ldfps f57,f58=[sp] // M0 + ldfps f59,f60=[sp] // M1 + mov f61=f0 // F + + setf.s f62=r0 // M2 + setf.s f63=r0 // M3 + mov f64=f0 // F + + ldfps f65,f66=[sp] // M0 + ldfps f67,f68=[sp] // M1 + mov f69=f0 // F + + setf.s f70=r0 // M2 + setf.s f71=r0 // M3 + mov f72=f0 // F + + ldfps f73,f74=[sp] // M0 + ldfps f75,f76=[sp] // M1 + mov f77=f0 // F + + setf.s f78=r0 // M2 + setf.s f79=r0 // M3 + mov f80=f0 // F + + ldfps f81,f82=[sp] // M0 + ldfps f83,f84=[sp] // M1 + mov f85=f0 // F + + setf.s f86=r0 // M2 + setf.s f87=r0 // M3 + mov f88=f0 // F + + /* + * When the instructions are cached, it would be faster to initialize + * the remaining registers with simply mov instructions (F-unit). + * This gets the time down to ~29 cycles. However, this would use up + * 33 bundles, whereas continuing with the above pattern yields + * 10 bundles and ~30 cycles. + */ + + ldfps f89,f90=[sp] // M0 + ldfps f91,f92=[sp] // M1 + mov f93=f0 // F + + setf.s f94=r0 // M2 + setf.s f95=r0 // M3 + mov f96=f0 // F + + ldfps f97,f98=[sp] // M0 + ldfps f99,f100=[sp] // M1 + mov f101=f0 // F + + setf.s f102=r0 // M2 + setf.s f103=r0 // M3 + mov f104=f0 // F + + ldfps f105,f106=[sp] // M0 + ldfps f107,f108=[sp] // M1 + mov f109=f0 // F + + setf.s f110=r0 // M2 + setf.s f111=r0 // M3 + mov f112=f0 // F + + ldfps f113,f114=[sp] // M0 + ldfps f115,f116=[sp] // M1 + mov f117=f0 // F + + setf.s f118=r0 // M2 + setf.s f119=r0 // M3 + mov f120=f0 // F + + ldfps f121,f122=[sp] // M0 + ldfps f123,f124=[sp] // M1 + mov f125=f0 // F + + setf.s f126=r0 // M2 + setf.s f127=r0 // M3 + br.ret.sptk.many rp // F END(__ia64_init_fpu) /* diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c index c2964682dff86169ad08ac04d9dcda5619739f3d..d03ee09dcf7aff5f638d7443470d311d8b3e4d51 100644 --- a/arch/ia64/kernel/ia64_ksyms.c +++ b/arch/ia64/kernel/ia64_ksyms.c @@ -6,7 +6,11 @@ #include <linux/module.h> #include <linux/string.h> -EXPORT_SYMBOL_NOVERS(memset); +#undef memset +extern void *memset (void *, int, size_t); +EXPORT_SYMBOL_NOVERS(memset); /* gcc generates direct calls to memset()... */ +EXPORT_SYMBOL_NOVERS(__memset_generic); +EXPORT_SYMBOL_NOVERS(__bzero); EXPORT_SYMBOL(memchr); EXPORT_SYMBOL(memcmp); EXPORT_SYMBOL_NOVERS(memcpy); @@ -67,7 +71,7 @@ EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(last_cli_ip); #endif -#include <asm/pgalloc.h> +#include <asm/tlbflush.h> EXPORT_SYMBOL(flush_tlb_range); @@ -147,3 +151,10 @@ EXPORT_SYMBOL(efi); #include <linux/proc_fs.h> extern struct proc_dir_entry *efi_dir; EXPORT_SYMBOL(efi_dir); + +#include <asm/machvec.h> +#ifdef CONFIG_IA64_GENERIC +EXPORT_SYMBOL(ia64_mv); +#endif +EXPORT_SYMBOL(machvec_noop); + diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 262fb32ff33fd381e3edb7cf064b50f480bfbefa..01ff645853d5f63980e8dd2899b51bd3196f68fc 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -22,6 +22,7 @@ * 02/01/07 E. Focht <efocht@ess.nec.de> Redirectable interrupt vectors in * iosapic_set_affinity(), initializations for * /proc/irq/#/smp_affinity + * 02/04/02 P. Diefenbaugh Cleaned up ACPI PCI IRQ routing. */ /* * Here is what the interrupt logic between a PCI device and the CPU looks like: @@ -56,9 +57,8 @@ #include <linux/smp_lock.h> #include <linux/string.h> #include <linux/irq.h> +#include <linux/acpi.h> -#include <asm/acpi-ext.h> -#include <asm/acpikcfg.h> #include <asm/delay.h> #include <asm/hw_irq.h> #include <asm/io.h> @@ -92,11 +92,37 @@ static struct iosapic_irq { unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ } iosapic_irq[IA64_NUM_VECTORS]; +static struct iosapic { + char *addr; /* base address of IOSAPIC */ + unsigned char pcat_compat; /* 8259 compatibility flag */ + unsigned char base_irq; /* first irq assigned to this IOSAPIC */ + unsigned short max_pin; /* max input pin supported in this IOSAPIC */ +} iosapic_lists[256] __initdata; + +static int num_iosapic = 0; + + +/* + * Find an IOSAPIC associated with an IRQ + */ +static inline int __init +find_iosapic (unsigned int irq) +{ + int i; + + for (i = 0; i < num_iosapic; i++) { + if ((irq - iosapic_lists[i].base_irq) < iosapic_lists[i].max_pin) + return i; + } + + return -1; +} + /* * Translate IOSAPIC irq number to the corresponding IA-64 interrupt vector. If no * entry exists, return -1. */ -int +static int iosapic_irq_to_vector (int irq) { int vector; @@ -479,7 +505,7 @@ iosapic_register_platform_irq (u32 int_type, u32 global_vector, int vector; switch (int_type) { - case ACPI20_ENTRY_PIS_PMI: + case ACPI_INTERRUPT_PMI: vector = iosapic_vector; /* * since PMI vector is alloc'd by FW(ACPI) not by kernel, @@ -488,15 +514,15 @@ iosapic_register_platform_irq (u32 int_type, u32 global_vector, iosapic_reassign_vector(vector); delivery = IOSAPIC_PMI; break; - case ACPI20_ENTRY_PIS_CPEI: - vector = IA64_PCE_VECTOR; - delivery = IOSAPIC_LOWEST_PRIORITY; - break; - case ACPI20_ENTRY_PIS_INIT: + case ACPI_INTERRUPT_INIT: vector = ia64_alloc_irq(); delivery = IOSAPIC_INIT; break; - default: + case ACPI_INTERRUPT_CPEI: + vector = IA64_PCE_VECTOR; + delivery = IOSAPIC_LOWEST_PRIORITY; + break; + default: printk("iosapic_register_platform_irq(): invalid int type\n"); return -1; } @@ -542,31 +568,41 @@ iosapic_register_legacy_irq (unsigned long irq, void __init iosapic_init (unsigned long phys_addr, unsigned int base_irq, int pcat_compat) { - int i, irq, max_pin, vector, pin; + int irq, max_pin, vector, pin; unsigned int ver; char *addr; static int first_time = 1; if (first_time) { first_time = 0; - for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) iosapic_irq[vector].pin = -1; /* mark as unused */ + } + if (pcat_compat) { /* - * Fetch the PCI interrupt routing table: + * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support + * enabled. */ - acpi_cf_get_pci_vectors(&pci_irq.route, &pci_irq.num_routes); + printk("%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__); + outb(0xff, 0xA1); + outb(0xff, 0x21); } addr = ioremap(phys_addr, 0); ver = iosapic_version(addr); max_pin = (ver >> 16) & 0xff; + iosapic_lists[num_iosapic].addr = addr; + iosapic_lists[num_iosapic].pcat_compat = pcat_compat; + iosapic_lists[num_iosapic].base_irq = base_irq; + iosapic_lists[num_iosapic].max_pin = max_pin; + num_iosapic++; + printk("IOSAPIC: version %x.%x, address 0x%lx, IRQs 0x%02x-0x%02x\n", (ver & 0xf0) >> 4, (ver & 0x0f), phys_addr, base_irq, base_irq + max_pin); - if ((base_irq == 0) && pcat_compat) + if ((base_irq == 0) && pcat_compat) { /* * Map the legacy ISA devices into the IOSAPIC data. Some of these may * get reprogrammed later on with data from the ACPI Interrupt Source @@ -590,11 +626,37 @@ iosapic_init (unsigned long phys_addr, unsigned int base_irq, int pcat_compat) /* program the IOSAPIC routing table: */ set_rte(vector, (ia64_get_lid() >> 16) & 0xffff); } + } +} + +void __init +iosapic_init_pci_irq (void) +{ + int i, index, vector, pin; + int base_irq, max_pin, pcat_compat; + unsigned int irq; + char *addr; + + if (0 != acpi_get_prt(&pci_irq.route, &pci_irq.num_routes)) + return; for (i = 0; i < pci_irq.num_routes; i++) { + irq = pci_irq.route[i].irq; - if ((irq < (int)base_irq) || (irq > (int)(base_irq + max_pin))) + index = find_iosapic(irq); + if (index < 0) { + printk("PCI: IRQ %u has no IOSAPIC mapping\n", irq); + continue; + } + + addr = iosapic_lists[index].addr; + base_irq = iosapic_lists[index].base_irq; + max_pin = iosapic_lists[index].max_pin; + pcat_compat = iosapic_lists[index].pcat_compat; + pin = irq - base_irq; + + if ((unsigned) pin > max_pin) /* the interrupt route is for another controller... */ continue; @@ -607,18 +669,13 @@ iosapic_init (unsigned long phys_addr, unsigned int base_irq, int pcat_compat) vector = ia64_alloc_irq(); } - register_irq(irq, vector, irq - base_irq, - /* IOSAPIC_POL_LOW, IOSAPIC_LEVEL */ - IOSAPIC_LOWEST_PRIORITY, 0, 0, base_irq, addr); + register_irq(irq, vector, pin, IOSAPIC_LOWEST_PRIORITY, 0, 0, base_irq, addr); -# ifdef DEBUG_IRQ_ROUTING +#ifdef DEBUG_IRQ_ROUTING printk("PCI: (B%d,I%d,P%d) -> IOSAPIC irq 0x%02x -> vector 0x%02x\n", pci_irq.route[i].bus, pci_irq.route[i].pci_id>>16, pci_irq.route[i].pin, iosapic_irq[vector].base_irq + iosapic_irq[vector].pin, vector); -# endif - - /* program the IOSAPIC routing table: */ - set_rte(vector, (ia64_get_lid() >> 16) & 0xffff); +#endif } } @@ -631,6 +688,11 @@ iosapic_pci_fixup (int phase) struct hw_interrupt_type *irq_type; irq_desc_t *idesc; + if (phase == 0) { + iosapic_init_pci_irq(); + return; + } + if (phase != 1) return; @@ -670,10 +732,10 @@ iosapic_pci_fixup (int phase) irq_type = &irq_type_iosapic_level; idesc = irq_desc(vector); - if (idesc->handler != irq_type){ + if (idesc->handler != irq_type) { if (idesc->handler != &no_irq_type) - printk("iosapic_pci_fixup: changing vector 0x%02x from " - "%s to %s\n", vector, + printk("iosapic_pci_fixup: changing vector 0x%02x " + "from %s to %s\n", vector, idesc->handler->typename, irq_type->typename); idesc->handler = irq_type; diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index e83200dfea8280958100d514b8a8c3cd20620b85..22cf6165e8947a12d74bd70dc0d37e66f86768d2 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -68,6 +68,23 @@ irq_desc_t _irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { IRQ_DISABLED, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}}; +#ifdef CONFIG_IA64_GENERIC +struct irq_desc * __ia64_irq_desc (unsigned int irq) +{ + return _irq_desc + irq; +} + +ia64_vector __ia64_irq_to_vector (unsigned int irq) +{ + return (ia64_vector) irq; +} + +unsigned int __ia64_local_vector_to_irq (ia64_vector vec) +{ + return (unsigned int) vec; +} +#endif + static void register_irq_proc (unsigned int irq); /* diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S index 18a9d647069fa4eb9363d904cbbc3b402803da55..35ac537723a2f51bfda78a20b10780131a008b8b 100644 --- a/arch/ia64/kernel/ivt.S +++ b/arch/ia64/kernel/ivt.S @@ -127,7 +127,7 @@ ENTRY(vhpt_miss) ;; (p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place srlz.d // ensure "rsm psr.dt" has taken effect -(p6) movl r19=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) movl r19=__pa(swapper_pg_dir) // region 5 is rooted at swapper_pg_dir (p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT (p7) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT-3 ;; @@ -413,7 +413,7 @@ ENTRY(nested_dtlb_miss) ;; (p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place srlz.d -(p6) movl r19=__pa(SWAPPER_PGD_ADDR) // region 5 is rooted at swapper_pg_dir +(p6) movl r19=__pa(swapper_pg_dir) // region 5 is rooted at swapper_pg_dir (p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT (p7) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT-3 ;; diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 82c7bbd54f443fec11423522bde9f1d37dd967eb..81187abfde62ed3187c5950207c8dd2a7169c075 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -41,6 +41,7 @@ #include <linux/irq.h> #include <linux/smp_lock.h> #include <linux/bootmem.h> +#include <linux/acpi.h> #include <asm/machvec.h> #include <asm/page.h> @@ -51,7 +52,6 @@ #include <asm/irq.h> #include <asm/hw_irq.h> -#include <asm/acpi-ext.h> #undef MCA_PRT_XTRA_DATA @@ -353,7 +353,9 @@ static int verify_guid (efi_guid_t *test, efi_guid_t *target) { int rc; +#ifdef IA64_MCA_DEBUG_INFO char out[40]; +#endif if ((rc = efi_guidcmp(*test, *target))) { IA64_MCA_DEBUG(KERN_DEBUG @@ -497,7 +499,7 @@ ia64_mca_init(void) { irq_desc_t *desc; unsigned int irq; - int cpev = acpi_request_vector(ACPI20_ENTRY_PIS_CPEI); + int cpev = acpi_request_vector(ACPI_INTERRUPT_CPEI); if (cpev >= 0) { for (irq = 0; irq < NR_IRQS; ++irq) diff --git a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h index 4ce74a4ad4024e59fd53a22b1619efd57f5e9ff2..81aebc4a2924036479fb03036f8fd7414a9f95a3 100644 --- a/arch/ia64/kernel/minstate.h +++ b/arch/ia64/kernel/minstate.h @@ -1,5 +1,7 @@ #include <linux/config.h> +#include <asm/cache.h> + #include "entry.h" /* @@ -28,18 +30,19 @@ * on interrupts. */ #define MINSTATE_START_SAVE_MIN_VIRT \ - dep r1=-1,r1,61,3; /* r1 = current (virtual) */ \ (pUser) mov ar.rsc=0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \ + dep r1=-1,r1,61,3; /* r1 = current (virtual) */ \ ;; \ +(pUser) mov.m rARRNAT=ar.rnat; \ (pUser) addl rKRBS=IA64_RBS_OFFSET,r1; /* compute base of RBS */ \ -(pUser) mov rARRNAT=ar.rnat; \ (pKern) mov r1=sp; /* get sp */ \ ;; \ -(pUser) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \ +(pUser) lfetch.fault.excl.nt1 [rKRBS]; \ (pUser) mov rARBSPSTORE=ar.bspstore; /* save ar.bspstore */ \ +(pUser) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \ ;; \ -(pKern) addl r1=-IA64_PT_REGS_SIZE,r1; /* if in kernel mode, use sp (r12) */ \ (pUser) mov ar.bspstore=rKRBS; /* switch to kernel RBS */ \ +(pKern) addl r1=-IA64_PT_REGS_SIZE,r1; /* if in kernel mode, use sp (r12) */ \ ;; \ (pUser) mov r18=ar.bsp; \ (pUser) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \ @@ -125,52 +128,58 @@ ;; \ SAVE_IFS; \ MINSTATE_START_SAVE_MIN \ + add r17=L1_CACHE_BYTES,r1 /* really: biggest cache-line size */ \ ;; \ - mov r16=r1; /* initialize first base pointer */ \ - adds r17=8,r1; /* initialize second base pointer */ \ + st8 [r1]=rCRIPSR; /* save cr.ipsr */ \ + lfetch.fault.excl.nt1 [r17],L1_CACHE_BYTES; \ + add r16=16,r1; /* initialize first base pointer */ \ ;; \ - st8 [r16]=rCRIPSR,16; /* save cr.ipsr */ \ - st8 [r17]=rCRIIP,16; /* save cr.iip */ \ + lfetch.fault.excl.nt1 [r17],L1_CACHE_BYTES; \ + ;; \ + lfetch.fault.excl.nt1 [r17]; \ + adds r17=8,r1; /* initialize second base pointer */ \ (pKern) mov r18=r0; /* make sure r18 isn't NaT */ \ ;; \ + st8 [r17]=rCRIIP,16; /* save cr.iip */ \ st8 [r16]=rCRIFS,16; /* save cr.ifs */ \ - st8 [r17]=rARUNAT,16; /* save ar.unat */ \ (pUser) sub r18=r18,rKRBS; /* r18=RSE.ndirty*8 */ \ ;; \ + st8 [r17]=rARUNAT,16; /* save ar.unat */ \ st8 [r16]=rARPFS,16; /* save ar.pfs */ \ + shl r18=r18,16; /* compute ar.rsc to be used for "loadrs" */ \ + ;; \ st8 [r17]=rARRSC,16; /* save ar.rsc */ \ - tbit.nz p15,p0=rCRIPSR,IA64_PSR_I_BIT \ - ;; /* avoid RAW on r16 & r17 */ \ -(pKern) adds r16=16,r16; /* skip over ar_rnat field */ \ -(pKern) adds r17=16,r17; /* skip over ar_bspstore field */ \ (pUser) st8 [r16]=rARRNAT,16; /* save ar.rnat */ \ +(pKern) adds r16=16,r16; /* skip over ar_rnat field */ \ + ;; /* avoid RAW on r16 & r17 */ \ (pUser) st8 [r17]=rARBSPSTORE,16; /* save ar.bspstore */ \ - ;; \ st8 [r16]=rARPR,16; /* save predicates */ \ - st8 [r17]=rB6,16; /* save b6 */ \ - shl r18=r18,16; /* compute ar.rsc to be used for "loadrs" */ \ +(pKern) adds r17=16,r17; /* skip over ar_bspstore field */ \ ;; \ + st8 [r17]=rB6,16; /* save b6 */ \ st8 [r16]=r18,16; /* save ar.rsc value for "loadrs" */ \ - st8.spill [r17]=rR1,16; /* save original r1 */ \ + tbit.nz p15,p0=rCRIPSR,IA64_PSR_I_BIT \ ;; \ +.mem.offset 8,0; st8.spill [r17]=rR1,16; /* save original r1 */ \ .mem.offset 0,0; st8.spill [r16]=r2,16; \ + ;; \ .mem.offset 8,0; st8.spill [r17]=r3,16; \ +.mem.offset 0,0; st8.spill [r16]=r12,16; \ adds r2=IA64_PT_REGS_R16_OFFSET,r1; \ ;; \ -.mem.offset 0,0; st8.spill [r16]=r12,16; \ -.mem.offset 8,0; st8.spill [r17]=r13,16; \ +.mem.offset 8,0; st8.spill [r17]=r13,16; \ +.mem.offset 0,0; st8.spill [r16]=r14,16; \ cmp.eq pNonSys,pSys=r0,r0 /* initialize pSys=0, pNonSys=1 */ \ ;; \ -.mem.offset 0,0; st8.spill [r16]=r14,16; \ -.mem.offset 8,0; st8.spill [r17]=r15,16; \ +.mem.offset 8,0; st8.spill [r17]=r15,16; \ +.mem.offset 0,0; st8.spill [r16]=r8,16; \ dep r14=-1,r0,61,3; \ ;; \ -.mem.offset 0,0; st8.spill [r16]=r8,16; \ -.mem.offset 8,0; st8.spill [r17]=r9,16; \ +.mem.offset 8,0; st8.spill [r17]=r9,16; \ +.mem.offset 0,0; st8.spill [r16]=r10,16; \ adds r12=-16,r1; /* switch to kernel memory stack (with 16 bytes of scratch) */ \ ;; \ -.mem.offset 0,0; st8.spill [r16]=r10,16; \ -.mem.offset 8,0; st8.spill [r17]=r11,16; \ +.mem.offset 8,0; st8.spill [r17]=r11,16; \ mov r13=IA64_KR(CURRENT); /* establish `current' */ \ ;; \ EXTRA; \ @@ -190,10 +199,12 @@ */ #define SAVE_REST \ .mem.offset 0,0; st8.spill [r2]=r16,16; \ -.mem.offset 8,0; st8.spill [r3]=r17,16; \ ;; \ +.mem.offset 8,0; st8.spill [r3]=r17,16; \ .mem.offset 0,0; st8.spill [r2]=r18,16; \ + ;; \ .mem.offset 8,0; st8.spill [r3]=r19,16; \ +.mem.offset 0,0; st8.spill [r2]=r20,16; \ ;; \ mov r16=ar.ccv; /* M-unit */ \ movl r18=FPSR_DEFAULT /* L-unit */ \ @@ -201,30 +212,29 @@ mov r17=ar.fpsr; /* M-unit */ \ mov ar.fpsr=r18; /* M-unit */ \ ;; \ -.mem.offset 0,0; st8.spill [r2]=r20,16; \ .mem.offset 8,0; st8.spill [r3]=r21,16; \ +.mem.offset 0,0; st8.spill [r2]=r22,16; \ mov r18=b0; \ ;; \ -.mem.offset 0,0; st8.spill [r2]=r22,16; \ .mem.offset 8,0; st8.spill [r3]=r23,16; \ +.mem.offset 0,0; st8.spill [r2]=r24,16; \ mov r19=b7; \ ;; \ -.mem.offset 0,0; st8.spill [r2]=r24,16; \ .mem.offset 8,0; st8.spill [r3]=r25,16; \ - ;; \ .mem.offset 0,0; st8.spill [r2]=r26,16; \ -.mem.offset 8,0; st8.spill [r3]=r27,16; \ ;; \ +.mem.offset 8,0; st8.spill [r3]=r27,16; \ .mem.offset 0,0; st8.spill [r2]=r28,16; \ -.mem.offset 8,0; st8.spill [r3]=r29,16; \ ;; \ +.mem.offset 8,0; st8.spill [r3]=r29,16; \ .mem.offset 0,0; st8.spill [r2]=r30,16; \ -.mem.offset 8,0; st8.spill [r3]=r31,16; \ ;; \ +.mem.offset 8,0; st8.spill [r3]=r31,16; \ st8 [r2]=r16,16; /* ar.ccv */ \ - st8 [r3]=r17,16; /* ar.fpsr */ \ ;; \ + st8 [r3]=r17,16; /* ar.fpsr */ \ st8 [r2]=r18,16; /* b0 */ \ + ;; \ st8 [r3]=r19,16+8; /* b7 */ \ ;; \ stf.spill [r2]=f6,32; \ diff --git a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c index 7f44f62f402b2a6aeb0cf9d6736af0fad346dc04..3a4725bace0b3afb4354971d421b9ae8f72f4df1 100644 --- a/arch/ia64/kernel/pci.c +++ b/arch/ia64/kernel/pci.c @@ -42,101 +42,183 @@ extern void ia64_mca_check_errors( void ); #endif +struct pci_fixup pcibios_fixups[1]; + +struct pci_ops *pci_root_ops; + +int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value); +int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value); + + /* - * This interrupt-safe spinlock protects all accesses to PCI - * configuration space. + * Low-level SAL-based PCI configuration access functions. Note that SAL + * calls are already serialized (via sal_lock), so we don't need another + * synchronization mechanism here. Not using segment number (yet). */ -static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED; -struct pci_fixup pcibios_fixups[] = { - { 0 } -}; +#define PCI_SAL_ADDRESS(bus, dev, fn, reg) \ + ((u64)(bus << 16) | (u64)(dev << 11) | (u64)(fn << 8) | (u64)(reg)) -/* Macro to build a PCI configuration address to be passed as a parameter to SAL. */ +static int +pci_sal_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +{ + int result = 0; + u64 data = 0; -#define PCI_CONFIG_ADDRESS(dev, where) \ - (((u64) dev->bus->number << 16) | ((u64) (dev->devfn & 0xff) << 8) | (where & 0xff)) + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + result = ia64_sal_pci_config_read(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, &data); + + *value = (u32) data; + + return result; +} static int -pci_conf_read_config_byte(struct pci_dev *dev, int where, u8 *value) +pci_sal_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) { - s64 status; - u64 lval; + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; - status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 1, &lval); - *value = lval; - return status; + return ia64_sal_pci_config_write(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, value); } + static int -pci_conf_read_config_word(struct pci_dev *dev, int where, u16 *value) +pci_sal_read_config_byte (struct pci_dev *dev, int where, u8 *value) { - s64 status; - u64 lval; + int result = 0; + u32 data = 0; + + if (!value) + return -EINVAL; - status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 2, &lval); - *value = lval; - return status; + result = pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, &data); + + *value = (u8) data; + + return result; } static int -pci_conf_read_config_dword(struct pci_dev *dev, int where, u32 *value) +pci_sal_read_config_word (struct pci_dev *dev, int where, u16 *value) { - s64 status; - u64 lval; + int result = 0; + u32 data = 0; + + if (!value) + return -EINVAL; + + result = pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, &data); + + *value = (u16) data; - status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 4, &lval); - *value = lval; - return status; + return result; } static int -pci_conf_write_config_byte (struct pci_dev *dev, int where, u8 value) +pci_sal_read_config_dword (struct pci_dev *dev, int where, u32 *value) { - return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 1, value); + if (!value) + return -EINVAL; + + return pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); } static int -pci_conf_write_config_word (struct pci_dev *dev, int where, u16 value) +pci_sal_write_config_byte (struct pci_dev *dev, int where, u8 value) { - return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 2, value); + return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, value); } static int -pci_conf_write_config_dword (struct pci_dev *dev, int where, u32 value) +pci_sal_write_config_word (struct pci_dev *dev, int where, u16 value) { - return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 4, value); + return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, value); } -struct pci_ops pci_conf = { - pci_conf_read_config_byte, - pci_conf_read_config_word, - pci_conf_read_config_dword, - pci_conf_write_config_byte, - pci_conf_write_config_word, - pci_conf_write_config_dword +static int +pci_sal_write_config_dword (struct pci_dev *dev, int where, u32 value) +{ + return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +struct pci_ops pci_sal_ops = { + pci_sal_read_config_byte, + pci_sal_read_config_word, + pci_sal_read_config_dword, + pci_sal_write_config_byte, + pci_sal_write_config_word, + pci_sal_write_config_dword }; + /* * Initialization. Uses the SAL interface */ + +struct pci_bus * +pcibios_scan_root(int seg, int bus) +{ + struct list_head *list = NULL; + struct pci_bus *pci_bus = NULL; + + list_for_each(list, &pci_root_buses) { + pci_bus = pci_bus_b(list); + if (pci_bus->number == bus) { + /* Already scanned */ + printk("PCI: Bus (%02x:%02x) already probed\n", seg, bus); + return pci_bus; + } + } + + printk("PCI: Probing PCI hardware on bus (%02x:%02x)\n", seg, bus); + + return pci_scan_bus(bus, pci_root_ops, NULL); +} + +void __init +pcibios_config_init (void) +{ + if (pci_root_ops) + return; + + printk("PCI: Using SAL to access configuration space\n"); + + pci_root_ops = &pci_sal_ops; + pci_config_read = pci_sal_read; + pci_config_write = pci_sal_write; + + return; +} + void __init pcibios_init (void) { # define PCI_BUSES_TO_SCAN 255 - int i; + int i = 0; #ifdef CONFIG_IA64_MCA ia64_mca_check_errors(); /* For post-failure MCA error logging */ #endif - platform_pci_fixup(0); /* phase 0 initialization (before PCI bus has been scanned) */ + pcibios_config_init(); + + platform_pci_fixup(0); /* phase 0 fixups (before buses scanned) */ printk("PCI: Probing PCI hardware\n"); for (i = 0; i < PCI_BUSES_TO_SCAN; i++) - pci_scan_bus(i, &pci_conf, NULL); + pci_scan_bus(i, pci_root_ops, NULL); + + platform_pci_fixup(1); /* phase 1 fixups (after buses scanned) */ - platform_pci_fixup(1); /* phase 1 initialization (after PCI bus has been scanned) */ return; } @@ -186,7 +268,12 @@ pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * r int pcibios_enable_device (struct pci_dev *dev) { + if (!dev) + return -EINVAL; + /* Not needed, since we enable all devices at startup. */ + + printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", dev->irq, dev->slot_name); return 0; } @@ -233,8 +320,7 @@ pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) + vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 7bf6855cc198d31740e5ea9b83d87f60f188b6e3..abb43c7f2a5049c5b675ba83fd21a52a3816abd0 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -24,7 +24,7 @@ #include <asm/efi.h> #include <asm/elf.h> #include <asm/perfmon.h> -#include <asm/pgtable.h> +#include <asm/pgalloc.h> #include <asm/processor.h> #include <asm/sal.h> #include <asm/uaccess.h> @@ -145,7 +145,7 @@ do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall /* * We use this if we don't have any better idle routine.. */ -static void +void default_idle (void) { /* may want to do PAL_LIGHT_HALT here... */ @@ -660,7 +660,7 @@ dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; - tsk = __get_free_pages(GFP_KERNEL, KERNEL_STACK_SIZE_ORDER); + tsk = (void *) __get_free_pages(GFP_KERNEL, KERNEL_STACK_SIZE_ORDER); if (!tsk) return NULL; diff --git a/arch/ia64/kernel/semaphore.c b/arch/ia64/kernel/semaphore.c index da6de324a5706eafb397eefd65f6b53097cacc51..0a2082fb17b19d671b48b6f8f22dc8e497e73efc 100644 --- a/arch/ia64/kernel/semaphore.c +++ b/arch/ia64/kernel/semaphore.c @@ -1,8 +1,8 @@ /* * IA-64 semaphore implementation (derived from x86 version). * - * Copyright (C) 1999-2000 Hewlett-Packard Co - * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> */ /* @@ -25,6 +25,7 @@ */ #include <linux/sched.h> +#include <asm/errno.h> #include <asm/semaphore.h> /* diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 1befaecbf33956d7b5aea7677a875846451c1504..c3d8f071dca8fae557d54bfb123f5bf6e764f687 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -19,6 +19,7 @@ #include <linux/config.h> #include <linux/init.h> +#include <linux/acpi.h> #include <linux/bootmem.h> #include <linux/console.h> #include <linux/delay.h> @@ -30,7 +31,6 @@ #include <linux/threads.h> #include <linux/tty.h> -#include <asm/acpi-ext.h> #include <asm/ia32.h> #include <asm/page.h> #include <asm/machvec.h> @@ -54,7 +54,10 @@ extern char _end; +#ifdef CONFIG_SMP unsigned long __per_cpu_offset[NR_CPUS]; +#endif + struct cpuinfo_ia64 cpu_info __per_cpu_data; unsigned long ia64_phys_stacked_size_p8; @@ -64,6 +67,8 @@ struct screen_info screen_info; unsigned long ia64_iobase; /* virtual address for I/O accesses */ +unsigned char aux_device_present = 0xaa; /* XXX remove this when legacy I/O is gone */ + #define COMMAND_LINE_SIZE 512 char saved_command_line[COMMAND_LINE_SIZE]; /* used in proc filesystem */ @@ -282,6 +287,7 @@ void __init setup_arch (char **cmdline_p) { extern unsigned long ia64_iobase; + unsigned long phys_iobase; unw_init(); @@ -314,24 +320,23 @@ setup_arch (char **cmdline_p) #endif /* - * Set `iobase' to the appropriate address in region 6 - * (uncached access range) + * Set `iobase' to the appropriate address in region 6 (uncached access range). * - * The EFI memory map is the "prefered" location to get the I/O port - * space base, rather the relying on AR.KR0. This should become more - * clear in future SAL specs. We'll fall back to getting it out of - * AR.KR0 if no appropriate entry is found in the memory map. + * The EFI memory map is the "preferred" location to get the I/O port space base, + * rather the relying on AR.KR0. This should become more clear in future SAL + * specs. We'll fall back to getting it out of AR.KR0 if no appropriate entry is + * found in the memory map. */ - ia64_iobase = efi_get_iobase(); - if (ia64_iobase) + phys_iobase = efi_get_iobase(); + if (phys_iobase) /* set AR.KR0 since this is all we use it for anyway */ - ia64_set_kr(IA64_KR_IO_BASE, ia64_iobase); + ia64_set_kr(IA64_KR_IO_BASE, phys_iobase); else { - ia64_iobase = ia64_get_kr(IA64_KR_IO_BASE); + phys_iobase = ia64_get_kr(IA64_KR_IO_BASE); printk("No I/O port range found in EFI memory map, falling back to AR.KR0\n"); - printk("I/O port base = 0x%lx\n", ia64_iobase); + printk("I/O port base = 0x%lx\n", phys_iobase); } - ia64_iobase = __IA64_UNCACHED_OFFSET | (ia64_iobase & ~PAGE_OFFSET); + ia64_iobase = (unsigned long) ioremap(phys_iobase, 0); #ifdef CONFIG_SMP cpu_physical_id(0) = hard_smp_processor_id(); @@ -339,20 +344,23 @@ setup_arch (char **cmdline_p) cpu_init(); /* initialize the bootstrap CPU */ - if (efi.acpi20) { - /* Parse the ACPI 2.0 tables */ - acpi20_parse(efi.acpi20); - } else if (efi.acpi) { - /* Parse the ACPI tables */ - acpi_parse(efi.acpi); - } - +#ifdef CONFIG_ACPI_BOOT + acpi_boot_init(*cmdline_p); +#endif #ifdef CONFIG_VT -# if defined(CONFIG_VGA_CONSOLE) - conswitchp = &vga_con; -# elif defined(CONFIG_DUMMY_CONSOLE) +# if defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; # endif +# if defined(CONFIG_VGA_CONSOLE) + /* + * Non-legacy systems may route legacy VGA MMIO range to system + * memory. vga_con probes the MMIO hole, so memory looks like + * a VGA device to it. The EFI memory map can tell us if it's + * memory so we can avoid this problem. + */ + if (efi_mem_type(0xA0000) != EFI_CONVENTIONAL_MEMORY) + conswitchp = &vga_con; +# endif #endif #ifdef CONFIG_IA64_MCA @@ -522,20 +530,25 @@ setup_per_cpu_areas (void) void cpu_init (void) { - extern char __per_cpu_start[], __phys_per_cpu_start[], __per_cpu_end[]; + extern char __per_cpu_start[], __phys_per_cpu_start[]; extern void __init ia64_mmu_init (void *); unsigned long num_phys_stacked; pal_vm_info_2_u_t vmi; unsigned int max_ctx; struct cpuinfo_ia64 *my_cpu_info; void *my_cpu_data; + +#ifdef CONFIG_SMP + extern char __per_cpu_end[]; int cpu = smp_processor_id(); my_cpu_data = alloc_bootmem_pages(__per_cpu_end - __per_cpu_start); memcpy(my_cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start); - __per_cpu_offset[cpu] = (char *) my_cpu_data - __per_cpu_start; - + my_cpu_info = my_cpu_data + ((char *) &cpu_info - __per_cpu_start); +#else + my_cpu_data = __phys_per_cpu_start; +#endif my_cpu_info = my_cpu_data + ((char *) &cpu_info - __per_cpu_start); /* diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index 5bf67baf058be165fd05002191072ca4a6ae032d..333126fad0f2792c79cc267ddf3ace25915c1196 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -530,8 +530,8 @@ ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) info.si_signo = signr; info.si_errno = 0; info.si_code = SI_USER; - info.si_pid = current->p_pptr->pid; - info.si_uid = current->p_pptr->uid; + info.si_pid = current->parent->pid; + info.si_uid = current->parent->uid; } /* If the (new) signal is now blocked, requeue it. */ @@ -570,7 +570,7 @@ ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) case SIGSTOP: current->state = TASK_STOPPED; current->exit_code = signr; - sig = current->p_pptr->sig; + sig = current->parent->sig; if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index f6d2038d545cdb58d6bf2f5b9da7f5cd0bfdc692..f2176bb9a11fdf8524a6036e00a946ec333eb623 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -48,6 +48,7 @@ #include <asm/ptrace.h> #include <asm/sal.h> #include <asm/system.h> +#include <asm/tlbflush.h> #include <asm/unistd.h> #include <asm/mca.h> @@ -236,7 +237,7 @@ smp_call_function_single (int cpuid, void (*func) (void *info), void *info, int int cpus = 1; if (cpuid == smp_processor_id()) { - printk(__FUNCTION__" trying to call self\n"); + printk("%s: trying to call self\n", __FUNCTION__); return -EBUSY; } diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index c339635170d28d1ebd1770892960ed6b7ee026ef..fcc10d7840cc6b856205bdbf899a56877c1f18d0 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -68,6 +68,7 @@ static volatile unsigned long go[SLAVE + 1]; extern void __init calibrate_delay(void); extern void start_ap(void); +extern unsigned long ia64_iobase; int cpucount; task_t *task_for_booting_cpu; @@ -345,6 +346,11 @@ smp_callin (void) */ ia64_init_itm(); + /* + * Set I/O port base per CPU + */ + ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase)); + #ifdef CONFIG_IA64_MCA ia64_mca_cmc_vector_setup(); /* Setup vector on AP & enable */ ia64_mca_check_errors(); /* For post-failure MCA error logging */ diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c index 80798577c61fad5b060e755892ab3f3d3d61fc73..fa230acc7cffee77d1f76771653c697fc5e1e6d5 100644 --- a/arch/ia64/kernel/sys_ia64.c +++ b/arch/ia64/kernel/sys_ia64.c @@ -47,7 +47,7 @@ arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len /* At this point: (!vmm || addr < vmm->vm_end). */ if (TASK_SIZE - len < addr) return -ENOMEM; - if (rgn_offset(addr) + len > RGN_MAP_LIMIT) /* no risk of overflow here... */ + if (REGION_OFFSET(addr) + len > RGN_MAP_LIMIT) /* no risk of overflow here... */ return -ENOMEM; if (!vmm || addr + len <= vmm->vm_start) return addr; @@ -126,7 +126,7 @@ ia64_brk (unsigned long brk, long arg1, long arg2, long arg3, } /* Check against unimplemented/unmapped addresses: */ - if ((newbrk - oldbrk) > RGN_MAP_LIMIT || rgn_offset(newbrk) > RGN_MAP_LIMIT) + if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT) goto out; /* Check against rlimit.. */ @@ -206,7 +206,7 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un * or across a region boundary. Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE * (for some integer n <= 61) and len > 0. */ - roff = rgn_offset(addr); + roff = REGION_OFFSET(addr); if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) { addr = -EINVAL; goto out; diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index ccef9a55889639bfc907472a65639605017a244a..2b8aa6942f1785f55cc89017d06e2ac97fa3ab6a 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -214,7 +214,8 @@ ia64_ni_syscall (unsigned long arg0, unsigned long arg1, unsigned long arg2, uns { struct pt_regs *regs = (struct pt_regs *) &stack; - printk("<sc%ld(%lx,%lx,%lx,%lx)>\n", regs->r15, arg0, arg1, arg2, arg3); + printk("%s(%d): <sc%ld(%lx,%lx,%lx,%lx)>\n", current->comm, current->pid, + regs->r15, arg0, arg1, arg2, arg3); return -ENOSYS; } diff --git a/arch/ia64/kernel/unwind_i.h b/arch/ia64/kernel/unwind_i.h index c4763f3032b8bb2a9e9a3a0aa6728cc110181b96..2d91f9cd8de28bb83a7c3c6faae9845e92f5115e 100644 --- a/arch/ia64/kernel/unwind_i.h +++ b/arch/ia64/kernel/unwind_i.h @@ -103,7 +103,7 @@ struct unw_state_record { unsigned int in_body : 1; /* are we inside a body (as opposed to a prologue)? */ unsigned long flags; /* see UNW_FLAG_* in unwind.h */ - u8 *imask; /* imask of of spill_mask record or NULL */ + u8 *imask; /* imask of spill_mask record or NULL */ unsigned long pr_val; /* predicate values */ unsigned long pr_mask; /* predicate mask */ long spill_offset; /* psp-relative offset for spill base */ diff --git a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile index 714aa1ffb3a9f2efbcc88fdc4f838c331b766a80..a53fa02c07c4f16a7be036f569e3e70805f83dbb 100644 --- a/arch/ia64/lib/Makefile +++ b/arch/ia64/lib/Makefile @@ -16,6 +16,9 @@ obj-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ flush.o io.o do_csum.o \ memcpy.o memset.o strlen.o swiotlb.o +obj-$(CONFIG_ITANIUM) += copy_page.o +obj-$(CONFIG_MCKINLEY) += copy_page_mck.o + IGNORE_FLAGS_OBJS = __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o diff --git a/arch/ia64/lib/copy_page_mck.S b/arch/ia64/lib/copy_page_mck.S new file mode 100644 index 0000000000000000000000000000000000000000..3c45d60a81b44789563a9a733fd82ddc15349613 --- /dev/null +++ b/arch/ia64/lib/copy_page_mck.S @@ -0,0 +1,185 @@ +/* + * McKinley-optimized version of copy_page(). + * + * Copyright (C) 2002 Hewlett-Packard Co + * David Mosberger <davidm@hpl.hp.com> + * + * Inputs: + * in0: address of target page + * in1: address of source page + * Output: + * no return value + * + * General idea: + * - use regular loads and stores to prefetch data to avoid consuming M-slot just for + * lfetches => good for in-cache performance + * - avoid l2 bank-conflicts by not storing into the same 16-byte bank within a single + * cycle + * + * Principle of operation: + * First, note that L1 has a line-size of 64 bytes and L2 a line-size of 128 bytes. + * To avoid secondary misses in L2, we prefetch both source and destination with a line-size + * of 128 bytes. When both of these lines are in the L2 and the first half of the + * source line is in L1, we start copying the remaining words. The second half of the + * source line is prefetched in an earlier iteration, so that by the time we start + * accessing it, it's also present in the L1. + * + * We use a software-pipelined loop to control the overall operation. The pipeline + * has 2*PREFETCH_DIST+K stages. The first PREFETCH_DIST stages are used for prefetching + * source cache-lines. The second PREFETCH_DIST stages are used for prefetching destination + * cache-lines, the last K stages are used to copy the cache-line words not copied by + * the prefetches. The four relevant points in the pipelined are called A, B, C, D: + * p[A] is TRUE if a source-line should be prefetched, p[B] is TRUE if a destination-line + * should be prefetched, p[C] is TRUE if the second half of an L2 line should be brought + * into L1D and p[D] is TRUE if a cacheline needs to be copied. + * + * This all sounds very complicated, but thanks to the modulo-scheduled loop support, + * the resulting code is very regular and quite easy to follow (once you get the idea). + * + * As a secondary optimization, the first 2*PREFETCH_DIST iterations are implemented + * as the separate .prefetch_loop. Logically, this loop performs exactly like the + * main-loop (.line_copy), but has all known-to-be-predicated-off instructions removed, + * so that each loop iteration is faster (again, good for cached case). + * + * When reading the code, it helps to keep the following picture in mind: + * + * word 0 word 1 + * +------+------+--- + * | v[x] | t1 | ^ + * | t2 | t3 | | + * | t4 | t5 | | + * | t6 | t7 | | 128 bytes + * | n[y] | t9 | | (L2 cache line) + * | t10 | t11 | | + * | t12 | t13 | | + * | t14 | t15 | v + * +------+------+--- + * + * Here, v[x] is copied by the (memory) prefetch. n[y] is loaded at p[C] + * to fetch the second-half of the L2 cache line into L1, and the tX words are copied in + * an order that avoids bank conflicts. + */ +#include <asm/asmmacro.h> +#include <asm/page.h> + +#define PREFETCH_DIST 8 // McKinley sustains 16 outstanding L2 misses (8 ld, 8 st) + +#define src0 r2 +#define src1 r3 +#define dst0 r9 +#define dst1 r10 +#define src_pre_mem r11 +#define dst_pre_mem r14 +#define src_pre_l2 r15 +#define dst_pre_l2 r16 +#define t1 r17 +#define t2 r18 +#define t3 r19 +#define t4 r20 +#define t5 t1 // alias! +#define t6 t2 // alias! +#define t7 t3 // alias! +#define t9 t5 // alias! +#define t10 t4 // alias! +#define t11 t7 // alias! +#define t12 t6 // alias! +#define t14 t10 // alias! +#define t13 r21 +#define t15 r22 + +#define saved_lc r23 +#define saved_pr r24 + +#define A 0 +#define B (PREFETCH_DIST) +#define C (B + PREFETCH_DIST) +#define D (C + 3) +#define N (D + 1) +#define Nrot ((N + 7) & ~7) + +GLOBAL_ENTRY(copy_page) + .prologue + alloc r8 = ar.pfs, 2, Nrot-2, 0, Nrot + + .rotr v[2*PREFETCH_DIST], n[D-C+1] + .rotp p[N] + + .save ar.lc, saved_lc + mov saved_lc = ar.lc + .save pr, saved_pr + mov saved_pr = pr + .body + + mov src_pre_mem = in1 + mov pr.rot = 0x10000 + mov ar.ec = 1 // special unrolled loop + + mov dst_pre_mem = in0 + mov ar.lc = 2*PREFETCH_DIST - 1 + + add src_pre_l2 = 8*8, in1 + add dst_pre_l2 = 8*8, in0 + add src0 = 8, in1 // first t1 src + add src1 = 3*8, in1 // first t3 src + add dst0 = 8, in0 // first t1 dst + add dst1 = 3*8, in0 // first t3 dst + mov t1 = (PAGE_SIZE/128) - (2*PREFETCH_DIST) - 1 + nop.m 0 + nop.i 0 + ;; + // same as .line_copy loop, but with all predicated-off instructions removed: +.prefetch_loop: +(p[A]) ld8 v[A] = [src_pre_mem], 128 // M0 +(p[B]) st8 [dst_pre_mem] = v[B], 128 // M2 + br.ctop.sptk .prefetch_loop + ;; + cmp.eq p16, p0 = r0, r0 // reset p16 to 1 (br.ctop cleared it to zero) + mov ar.lc = t1 // with 64KB pages, t1 is too big to fit in 8 bits! + mov ar.ec = N // # of stages in pipeline + ;; +.line_copy: +(p[D]) ld8 t2 = [src0], 3*8 // M0 +(p[D]) ld8 t4 = [src1], 3*8 // M1 +(p[B]) st8 [dst_pre_mem] = v[B], 128 // M2 prefetch dst from memory +(p[D]) st8 [dst_pre_l2] = n[D-C], 128 // M3 prefetch dst from L2 + ;; +(p[A]) ld8 v[A] = [src_pre_mem], 128 // M0 prefetch src from memory +(p[C]) ld8 n[0] = [src_pre_l2], 128 // M1 prefetch src from L2 +(p[D]) st8 [dst0] = t1, 8 // M2 +(p[D]) st8 [dst1] = t3, 8 // M3 + ;; +(p[D]) ld8 t5 = [src0], 8 +(p[D]) ld8 t7 = [src1], 3*8 +(p[D]) st8 [dst0] = t2, 3*8 +(p[D]) st8 [dst1] = t4, 3*8 + ;; +(p[D]) ld8 t6 = [src0], 3*8 +(p[D]) ld8 t10 = [src1], 8 +(p[D]) st8 [dst0] = t5, 8 +(p[D]) st8 [dst1] = t7, 3*8 + ;; +(p[D]) ld8 t9 = [src0], 3*8 +(p[D]) ld8 t11 = [src1], 3*8 +(p[D]) st8 [dst0] = t6, 3*8 +(p[D]) st8 [dst1] = t10, 8 + ;; +(p[D]) ld8 t12 = [src0], 8 +(p[D]) ld8 t14 = [src1], 8 +(p[D]) st8 [dst0] = t9, 3*8 +(p[D]) st8 [dst1] = t11, 3*8 + ;; +(p[D]) ld8 t13 = [src0], 4*8 +(p[D]) ld8 t15 = [src1], 4*8 +(p[D]) st8 [dst0] = t12, 8 +(p[D]) st8 [dst1] = t14, 8 + ;; +(p[D-1])ld8 t1 = [src0], 8 +(p[D-1])ld8 t3 = [src1], 8 +(p[D]) st8 [dst0] = t13, 4*8 +(p[D]) st8 [dst1] = t15, 4*8 + br.ctop.sptk .line_copy + ;; + mov ar.lc = saved_lc + mov pr = saved_pr, -1 + br.ret.sptk.many rp +END(copy_page) diff --git a/arch/ia64/lib/do_csum.S b/arch/ia64/lib/do_csum.S index 398a2b9338610ad92db5d56f5c2e24ff2e1fc4f7..42647ae754375deede61913183298f601f2b9fd1 100644 --- a/arch/ia64/lib/do_csum.S +++ b/arch/ia64/lib/do_csum.S @@ -8,9 +8,11 @@ * in0: address of buffer to checksum (char *) * in1: length of the buffer (int) * - * Copyright (C) 1999, 2001 Hewlett-Packard Co - * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co + * Stephane Eranian <eranian@hpl.hp.com> * + * 02/04/08 David Mosberger <davidm@hpl.hp.com> + * More cleanup and tuning. * 01/04/18 Jun Nakajima <jun.nakajima@intel.com> * Clean up and optimize and the software pipeline, loading two * back-to-back 8-byte words per loop. Clean up the initialization @@ -71,8 +73,6 @@ // calculating the Internet checksum. // // NOT YET DONE: -// - use the lfetch instruction to augment the chances of the data being in -// the cache when we need it. // - Maybe another algorithm which would take care of the folding at the // end in a different manner // - Work with people more knowledgeable than me on the network stack @@ -102,10 +102,6 @@ #define buf in0 #define len in1 -#ifndef CONFIG_IA64_LOAD_LATENCY -#define CONFIG_IA64_LOAD_LATENCY 2 -#endif - #define LOAD_LATENCY 2 // XXX fix me #if (LOAD_LATENCY != 1) && (LOAD_LATENCY != 2) @@ -122,45 +118,46 @@ GLOBAL_ENTRY(do_csum) .prologue .save ar.pfs, saved_pfs alloc saved_pfs=ar.pfs,2,16,1,16 - .rotr word1[4], word2[4],result1[4],result2[4] - .rotp p[PIPE_DEPTH] + .rotr word1[4], word2[4],result1[LOAD_LATENCY+2],result2[LOAD_LATENCY+2] + .rotp p[PIPE_DEPTH], pC1[2], pC2[2] mov ret0=r0 // in case we have zero length cmp.lt p0,p6=r0,len // check for zero length or negative (32bit len) - ;; // avoid WAW on CFM - mov tmp3=0x7 // a temporary mask/value + ;; add tmp1=buf,len // last byte's address -(p6) br.ret.spnt.many rp // return if true (hope we can avoid that) + .save pr, saved_pr + mov saved_pr=pr // preserve predicates (rotation) +(p6) br.ret.spnt.many rp // return if zero or negative length - and firstoff=7,buf // how many bytes off for first1 element - tbit.nz p15,p0=buf,0 // is buf an odd address ? mov hmask=-1 // intialize head mask - ;; - andcm first1=buf,tmp3 // 8byte aligned down address of first1 element + tbit.nz p15,p0=buf,0 // is buf an odd address? + and first1=-8,buf // 8-byte align down address of first1 element + + and firstoff=7,buf // how many bytes off for first1 element mov tmask=-1 // initialize tail mask - adds tmp2=-1,tmp1 // last-1 + ;; + adds tmp2=-1,tmp1 // last-1 and lastoff=7,tmp1 // how many bytes off for last element - andcm last=tmp2,tmp3 // address of word containing last byte - .save pr, saved_pr - mov saved_pr=pr // preserve predicates (rotation) + ;; + sub tmp1=8,lastoff // complement to lastoff + and last=-8,tmp2 // address of word containing last byte ;; sub tmp3=last,first1 // tmp3=distance from first1 to last + .save ar.lc, saved_lc + mov saved_lc=ar.lc // save lc cmp.eq p8,p9=last,first1 // everything fits in one word ? - sub tmp1=8,lastoff // complement to lastoff - ld8 firstval=[first1],8 // load,ahead of time, "first1" word + + ld8 firstval=[first1],8 // load, ahead of time, "first1" word + and tmp1=7, tmp1 // make sure that if tmp1==8 -> tmp1=0 shl tmp2=firstoff,3 // number of bits ;; - and tmp1=7, tmp1 // make sure that if tmp1==8 -> tmp1=0 -(p9) ld8 lastval=[last] // load,ahead of time, "last" word, if needed +(p9) ld8 lastval=[last] // load, ahead of time, "last" word, if needed + shl tmp1=tmp1,3 // number of bits (p9) adds tmp3=-8,tmp3 // effectively loaded ;; (p8) mov lastval=r0 // we don't need lastval if first1==last - shl tmp1=tmp1,3 // number of bits shl hmask=hmask,tmp2 // build head mask, mask off [0,first1off[ - ;; shr.u tmask=tmask,tmp1 // build tail mask, mask off ]8,lastoff] - .save ar.lc, saved_lc - mov saved_lc=ar.lc // save lc ;; .body #define count tmp3 @@ -171,8 +168,8 @@ GLOBAL_ENTRY(do_csum) ;; // If count is odd, finish this 8-byte word so that we can // load two back-to-back 8-byte words per loop thereafter. - tbit.nz p10,p11=count,0 // if (count is odd) and word1[0]=firstval,hmask // and mask it as appropriate + tbit.nz p10,p11=count,0 // if (count is odd) ;; (p8) mov result1[0]=word1[0] (p9) add result1[0]=word1[0],word2[0] @@ -181,9 +178,8 @@ GLOBAL_ENTRY(do_csum) ;; (p6) adds result1[0]=1,result1[0] (p8) br.cond.dptk .do_csum_exit // if (within an 8-byte word) - ;; (p11) br.cond.dptk .do_csum16 // if (count is even) - ;; + // Here count is odd. ld8 word1[1]=[first1],8 // load an 8-byte word cmp.eq p9,p10=1,count // if (count == 1) @@ -194,11 +190,9 @@ GLOBAL_ENTRY(do_csum) cmp.ltu p6,p0=result1[0],word1[1] ;; (p6) adds result1[0]=1,result1[0] - ;; (p9) br.cond.sptk .do_csum_exit // if (count == 1) exit // Fall through to caluculate the checksum, feeding result1[0] as // the initial value in result1[0]. - ;; // // Calculate the checksum loading two 8-byte words per loop. // @@ -207,45 +201,36 @@ GLOBAL_ENTRY(do_csum) shr.u count=count,1 // we do 16 bytes per loop ;; cmp.eq p9,p10=r0,count // if (count == 0) + adds count=-1,count brp.loop.imp 1f,2f ;; - adds count=-1,count mov ar.ec=PIPE_DEPTH - ;; mov ar.lc=count // set lc - ;; + // result1[0] must be initialized in advance. mov result2[0]=r0 - ;; mov pr.rot=1<<16 - ;; mov carry1=r0 mov carry2=r0 - ;; add first2=8,first1 - ;; (p9) br.cond.sptk .do_csum_exit - ;; - nop.m 0 - nop.i 0 ;; .align 32 1: -(ELD_1) cmp.ltu p31,p0=result1[LOAD_LATENCY],word1[LOAD_LATENCY+1] -(p32) adds carry1=1,carry1 -(ELD_1) cmp.ltu p47,p0=result2[LOAD_LATENCY],word2[LOAD_LATENCY+1] -(p48) adds carry2=1,carry2 +(ELD_1) cmp.ltu pC1[0],p0=result1[LOAD_LATENCY],word1[LOAD_LATENCY+1] +(pC1[1])adds carry1=1,carry1 +(ELD_1) cmp.ltu pC2[0],p0=result2[LOAD_LATENCY],word2[LOAD_LATENCY+1] +(pC2[1])adds carry2=1,carry2 (ELD) add result1[LOAD_LATENCY-1]=result1[LOAD_LATENCY],word1[LOAD_LATENCY] (ELD) add result2[LOAD_LATENCY-1]=result2[LOAD_LATENCY],word2[LOAD_LATENCY] -2: -(p16) ld8 word1[0]=[first1],16 -(p16) ld8 word2[0]=[first2],16 +[2:] +(p[0]) ld8 word1[0]=[first1],16 +(p[0]) ld8 word2[0]=[first2],16 br.ctop.sptk 1b ;; - // Since len is a 32-bit value, carry cannot be larger than - // a 64-bit value. -(p32) adds carry1=1,carry1 // since we miss the last one -(p48) adds carry2=1,carry2 + // Since len is a 32-bit value, carry cannot be larger than a 64-bit value. +(pC1[1])adds carry1=1,carry1 // since we miss the last one +(pC2[1])adds carry2=1,carry2 ;; add result1[LOAD_LATENCY+1]=result1[LOAD_LATENCY+1],carry1 add result2[LOAD_LATENCY+1]=result2[LOAD_LATENCY+1],carry2 @@ -263,18 +248,15 @@ GLOBAL_ENTRY(do_csum) (p6) adds result1[0]=1,result1[0] ;; .do_csum_exit: - movl tmp3=0xffffffff - ;; - // XXX Fixme // // now fold 64 into 16 bits taking care of carry // that's not very good because it has lots of sequentiality // - and tmp1=result1[0],tmp3 + mov tmp3=0xffff + zxt4 tmp1=result1[0] shr.u tmp2=result1[0],32 ;; add result1[0]=tmp1,tmp2 - shr.u tmp3=tmp3,16 ;; and tmp1=result1[0],tmp3 shr.u tmp2=result1[0],16 diff --git a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S index 00806c1bd1c9e717cc96b08212a50132f9e0f17c..0368eee8461fa24a810b5b49b385bd48890bb025 100644 --- a/arch/ia64/lib/memset.S +++ b/arch/ia64/lib/memset.S @@ -9,8 +9,8 @@ * in1: byte value to use for storing * in2: length of the buffer * - * Copyright (C) 1999, 2001 Hewlett-Packard Co - * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999, 2001, 2002 Hewlett-Packard Co + * Stephane Eranian <eranian@hpl.hp.com> */ #include <asm/asmmacro.h> @@ -30,7 +30,19 @@ #define saved_lc r20 #define tmp r21 -GLOBAL_ENTRY(memset) +GLOBAL_ENTRY(__bzero) + .prologue + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,0,0,3,0 + mov out2=out1 + mov out1=0 + /* FALL THROUGH (explicit NOPs so that next alloc is preceded by stop bit!) */ + nop.m 0 + nop.f 0 + nop.i 0 + ;; +END(__bzero) +GLOBAL_ENTRY(__memset_generic) .prologue .save ar.pfs, saved_pfs alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here @@ -105,4 +117,7 @@ GLOBAL_ENTRY(memset) ;; (p6) st1 [buf]=val // only 1 byte left br.ret.sptk.many rp -END(memset) +END(__memset_generic) + + .global memset +memset = __memset_generic // alias needed for gcc diff --git a/arch/ia64/lib/swiotlb.c b/arch/ia64/lib/swiotlb.c index 319ce20ea03c7e03bc862ade1f2de246010fdcca..6a4e856c48dbd1e91f0f5c8129e833f43a249ace 100644 --- a/arch/ia64/lib/swiotlb.c +++ b/arch/ia64/lib/swiotlb.c @@ -10,6 +10,7 @@ * unnecessary i-cache flushing. */ +#include <linux/cache.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/pci.h> @@ -24,9 +25,6 @@ #include <linux/init.h> #include <linux/bootmem.h> -#define ALIGN(val, align) ((unsigned long) \ - (((unsigned long) (val) + ((align) - 1)) & ~((align) - 1))) - #define OFFSET(val,align) ((unsigned long) \ ( (val) & ( (align) - 1))) @@ -276,8 +274,11 @@ swiotlb_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_ha int gfp = GFP_ATOMIC; void *ret; - if (!hwdev || hwdev->dma_mask <= 0xffffffff) - gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */ + /* + * Alloc_consistent() is defined to return memory < 4GB, no matter what the DMA + * mask says. + */ + gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */ ret = (void *)__get_free_pages(gfp, get_order(size)); if (!ret) return NULL; diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index b3c75b7fea6f793c75806d99247deee19f70ae04..aa84967115660c943046c430541406340681dd45 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -120,15 +120,15 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re if (!(prev_vma && (prev_vma->vm_flags & VM_GROWSUP) && (address == prev_vma->vm_end))) { if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; - if (rgn_index(address) != rgn_index(vma->vm_start) - || rgn_offset(address) >= RGN_MAP_LIMIT) + if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start) + || REGION_OFFSET(address) >= RGN_MAP_LIMIT) goto bad_area; if (expand_stack(vma, address)) goto bad_area; } else { vma = prev_vma; - if (rgn_index(address) != rgn_index(vma->vm_start) - || rgn_offset(address) >= RGN_MAP_LIMIT) + if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start) + || REGION_OFFSET(address) >= RGN_MAP_LIMIT) goto bad_area; if (expand_backing_store(vma, address)) goto bad_area; diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index a5d26ac2df2e771ec9edbe8914c0193ca582fd8c..ddc604251f82a078e374f13a20b22d3471a3f5ea 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -40,10 +40,10 @@ static unsigned long totalram_pages; static int pgt_cache_water[2] = { 25, 50 }; -int +void check_pgt_cache (void) { - int low, high, freed = 0; + int low, high; low = pgt_cache_water[0]; high = pgt_cache_water[1]; @@ -51,12 +51,11 @@ check_pgt_cache (void) if (pgtable_cache_size > high) { do { if (pgd_quicklist) - free_page((unsigned long)pgd_alloc_one_fast(0)), ++freed; + free_page((unsigned long)pgd_alloc_one_fast(0)); if (pmd_quicklist) - free_page((unsigned long)pmd_alloc_one_fast(0, 0)), ++freed; + free_page((unsigned long)pmd_alloc_one_fast(0, 0)); } while (pgtable_cache_size > low); } - return freed; } /* @@ -348,8 +347,6 @@ paging_init (void) { unsigned long max_dma, zones_size[MAX_NR_ZONES]; - clear_page((void *) ZERO_PAGE_ADDR); - /* initialize mem_map[] */ memset(zones_size, 0, sizeof(zones_size)); diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index 3a3169cab1184dff3edaa367494b49f0a1fe3c60..19fe60e5fb71cc01d8f3c1e821f7c4c3e8dc66e2 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c @@ -16,10 +16,11 @@ #include <linux/smp.h> #include <linux/mm.h> +#include <asm/delay.h> #include <asm/mmu_context.h> #include <asm/pgalloc.h> #include <asm/pal.h> -#include <asm/delay.h> +#include <asm/tlbflush.h> #define SUPPORTED_PGBITS ( \ 1 << _PAGE_SIZE_256M | \ @@ -79,7 +80,7 @@ wrap_mmu_context (struct mm_struct *mm) flush_tlb_all(); } -static inline void +void ia64_global_tlb_purge (unsigned long start, unsigned long end, unsigned long nbits) { static spinlock_t ptcg_lock = SPIN_LOCK_UNLOCKED; diff --git a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S index a634192326efa1ce62ff2ff2ce8fbc7d127b761e..349ae209cff879da528de4ee7a80e51b8c757f42 100644 --- a/arch/ia64/vmlinux.lds.S +++ b/arch/ia64/vmlinux.lds.S @@ -28,11 +28,6 @@ SECTIONS .text : AT(ADDR(.text) - PAGE_OFFSET) { *(.text.ivt) - /* these are not really text pages, but they need to be page aligned: */ - *(__special_page_section) - __start_gate_section = .; - *(.text.gate) - __stop_gate_section = .; *(.text) } .text2 : AT(ADDR(.text2) - PAGE_OFFSET) @@ -120,6 +115,13 @@ SECTIONS .data.init_task : AT(ADDR(.data.init_task) - PAGE_OFFSET) { *(.data.init_task) } + .data.page_aligned : AT(ADDR(.data.page_aligned) - PAGE_OFFSET) + { *(__special_page_section) + __start_gate_section = .; + *(.text.gate) + __stop_gate_section = .; + } + . = ALIGN(SMP_CACHE_BYTES); .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - PAGE_OFFSET) { *(.data.cacheline_aligned) } diff --git a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile index d3a2880d80b6ddfad78926c95482f6fa1c846e64..8dbb83fa326d2cdd85cf17b427f48acae1127c4d 100644 --- a/arch/sparc64/solaris/Makefile +++ b/arch/sparc64/solaris/Makefile @@ -1,11 +1,6 @@ # # Makefile for the Solaris binary emulation. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now in the main makefile... ifeq ($(CONFIG_SOLARIS_EMUL),m) CPPFLAGS = $(MODFLAGS) @@ -17,18 +12,9 @@ endif .S.o: $(CC) $(AFLAGS) $(CPPFLAGS) -ansi -c $< -o $*.o -list-multi := solaris.o - solaris-objs := entry64.o fs.o misc.o signal.o systbl.o socket.o \ ioctl.o ipc.o socksys.o timod.o obj-$(CONFIG_SOLARIS_EMUL) += solaris.o -ifneq ($(CONFIG_SOLARIS_EMUL),y) -do_it_all: -endif - -solaris.o: $(solaris-objs) - $(LD) -r -o $@ $(solaris-objs) - include $(TOPDIR)/Rules.make diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile index 59d8c51d57fdf5a02bafb1fe60dc6e18e0807d90..45162e99841d521a7426244c983fee9f1de8ad6c 100644 --- a/drivers/acorn/block/Makefile +++ b/drivers/acorn/block/Makefile @@ -1,13 +1,6 @@ # # Makefile for the Acorn block device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now inherited from the -# parent makefile. -# USE_STANDARD_AS_RULE := true @@ -19,7 +12,6 @@ obj-n := obj- := export-objs := -list-multi := fd1772_mod.o mfmhd_mod.o fd1772_mod-objs := fd1772.o fd1772dma.o mfmhd_mod-objs := mfmhd.o mfm.o @@ -27,9 +19,3 @@ obj-$(CONFIG_BLK_DEV_FD1772) += fd1772_mod.o obj-$(CONFIG_BLK_DEV_MFM) += mfmhd_mod.o include $(TOPDIR)/Rules.make - -fd1772_mod.o: $(fd1772_mod-objs) - $(LD) -r -o $@ $(fd1772_mod-objs) - -mfmhd_mod.o: $(mfmhd_mod-objs) - $(LD) -r -o $@ $(mfmhd_mod-objs) diff --git a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile index 0610d8bf520d7fbbffa8c4c0af7701c9b82d9c7f..65edeed36f6ef6ef4abffcdde840e1e50ec25cba 100644 --- a/drivers/acorn/scsi/Makefile +++ b/drivers/acorn/scsi/Makefile @@ -12,7 +12,6 @@ obj-n := obj- := export-objs := fas216.o queue.o msgqueue.o -list-multi := acornscsi_mod.o acornscsi_mod-objs := acornscsi.o acornscsi-io.o obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o @@ -25,6 +24,3 @@ obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o include $(TOPDIR)/Rules.make - -acornscsi_mod.o: $(acornscsi_mod-objs) - $(LD) -r -o $@ $(acornscsi_mod-objs) diff --git a/drivers/atm/Makefile b/drivers/atm/Makefile index 52f2e96ae4068f6d697a4fd1855a4a0418d1e9ba..5afffde2e58e0af81d650348148faffe4843ad7d 100644 --- a/drivers/atm/Makefile +++ b/drivers/atm/Makefile @@ -1,4 +1,3 @@ -# File: drivers/atm/Makefile # # Makefile for the Linux network (ATM) device drivers. # @@ -57,7 +56,6 @@ obj-$(CONFIG_ATM_FORE200E) += fore_200e.o EXTRA_CFLAGS=-g -list-multi := fore_200e.o fore_200e-objs := fore200e.o $(FORE200E_FW_OBJS) include $(TOPDIR)/Rules.make @@ -92,9 +90,6 @@ fore200e_mkfirm: fore200e_mkfirm.c objcopy -Iihex $< -Obinary $@.gz gzip -df $@.gz -fore_200e.o: $(fore_200e-objs) - $(LD) -r -o $@ $(fore_200e-objs) - # firmware dependency stuff taken from drivers/sound/Makefile FORE200E_FW_UP_TO_DATE := diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 6cc85ffd0f4e575ec887b322ce76d3af198da369..72c85651d21086f2f0c0acc8282e0ad6f9227444 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -899,10 +899,10 @@ static struct request *get_request_wait(request_queue_t *q, int rw) spin_lock_prefetch(q->queue_lock); generic_unplug_device(q); - add_wait_queue(&rl->wait, &wait); + add_wait_queue_exclusive(&rl->wait, &wait); do { set_current_state(TASK_UNINTERRUPTIBLE); - if (rl->count < batch_requests) + if (!rl->count) schedule(); spin_lock_irq(q->queue_lock); rq = get_request(q, rw); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index ff6805fc8a0246f4a73aeccf06b4738503e35962..b9f2c58c564a82bc1676567f338728350787de0e 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -240,11 +240,11 @@ void nbd_do_it(struct nbd_device *lo) { struct request *req; - down (&lo->queue_lock); + down (&lo->tx_lock); while (1) { - up (&lo->queue_lock); + up (&lo->tx_lock); req = nbd_read_stat(lo); - down (&lo->queue_lock); + down (&lo->tx_lock); if (!req) { printk(KERN_ALERT "req should never be null\n" ); @@ -264,14 +264,14 @@ void nbd_do_it(struct nbd_device *lo) } #endif blkdev_dequeue_request(req); - up (&lo->queue_lock); + up (&lo->tx_lock); nbd_end_request(req); - down (&lo->queue_lock); + down (&lo->tx_lock); } out: - up (&lo->queue_lock); + up (&lo->tx_lock); } void nbd_clear_que(struct nbd_device *lo) @@ -299,11 +299,11 @@ void nbd_clear_que(struct nbd_device *lo) #endif req->errors++; blkdev_dequeue_request(req); - up(&lo->queue_lock); + up(&lo->tx_lock); nbd_end_request(req); - down(&lo->queue_lock); + down(&lo->tx_lock); } } @@ -351,10 +351,10 @@ static void do_nbd_request(request_queue_t * q) blkdev_dequeue_request(req); spin_unlock_irq(q->queue_lock); - down (&lo->queue_lock); + down (&lo->tx_lock); list_add(&req->queuelist, &lo->queue_head); nbd_send_req(lo->sock, req); /* Why does this block? */ - up (&lo->queue_lock); + up (&lo->tx_lock); spin_lock_irq(q->queue_lock); continue; @@ -396,14 +396,14 @@ static int nbd_ioctl(struct inode *inode, struct file *file, return 0 ; case NBD_CLEAR_SOCK: - down(&lo->queue_lock); + down(&lo->tx_lock); nbd_clear_que(lo); if (!list_empty(&lo->queue_head)) { - up(&lo->queue_lock); + up(&lo->tx_lock); printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n"); return -EBUSY; } - up(&lo->queue_lock); + up(&lo->tx_lock); file = lo->file; if (!file) return -EINVAL; @@ -527,7 +527,7 @@ static int __init nbd_init(void) nbd_dev[i].magic = LO_MAGIC; nbd_dev[i].flags = 0; INIT_LIST_HEAD(&nbd_dev[i].queue_head); - init_MUTEX(&nbd_dev[i].queue_lock); + init_MUTEX(&nbd_dev[i].tx_lock); nbd_blksizes[i] = 1024; nbd_blksize_bits[i] = 10; nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 1a9647b4a05241c5a4386537c3e64167f01c90bc..da59f3b8bca0f15dd266c554bb9066a1b10cdee6 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -1,13 +1,6 @@ # # Makefile for the kernel character device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# # # This file contains the font map for the default (hardware) font @@ -27,8 +20,6 @@ export-objs := busmouse.o console.o keyboard.o sysrq.o \ mod-subdirs := ftape drm pcmcia -list-multi := - KEYMAP =defkeymap.o KEYBD =pc_keyb.o CONSOLE =console.o diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index 42b4053ca37dd8851a4d339cae86fe11016649c0..4b8ee1713eda0762c63e740b8c929e81bdc3d430 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -7,12 +7,8 @@ O_TARGET := agp.o export-objs := agpgart_be.o -list-multi := agpgart.o agpgart-objs := agpgart_fe.o agpgart_be.o obj-$(CONFIG_AGP) += agpgart.o include $(TOPDIR)/Rules.make - -agpgart.o: $(agpgart-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(agpgart-objs) diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index 819418eb5e00eca63b70fe2cd4a2033d2007dc73..a0ffc1152fe0aa57996c359937cbae3549c646bd 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -3,7 +3,6 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. O_TARGET := drm.o -list-multi := gamma.o tdfx.o r128.o mga.o i810.o radeon.o ffb.o gamma-objs := gamma_drv.o gamma_dma.o tdfx-objs := tdfx_drv.o @@ -22,24 +21,3 @@ obj-$(CONFIG_DRM_I810) += i810.o obj-$(CONFIG_DRM_FFB) += ffb.o include $(TOPDIR)/Rules.make - -gamma.o: $(gamma-objs) $(lib) - $(LD) -r -o $@ $(gamma-objs) $(lib) - -tdfx.o: $(tdfx-objs) $(lib) - $(LD) -r -o $@ $(tdfx-objs) $(lib) - -mga.o: $(mga-objs) $(lib) - $(LD) -r -o $@ $(mga-objs) $(lib) - -i810.o: $(i810-objs) $(lib) - $(LD) -r -o $@ $(i810-objs) $(lib) - -r128.o: $(r128-objs) $(lib) - $(LD) -r -o $@ $(r128-objs) $(lib) - -radeon.o: $(radeon-objs) $(lib) - $(LD) -r -o $@ $(radeon-objs) $(lib) - -ffb.o: $(ffb-objs) $(lib) - $(LD) -r -o $@ $(ffb-objs) $(lib) diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 6058bf8782c205484687055ab2bd48e75fb46d95..b0dcc32b53ce3afda03c12b5acd8b7d943ca9535 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -9,7 +9,7 @@ * Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> * * Original release 01/11/99 - * $Id: n_hdlc.c,v 3.2 2000/11/06 22:34:38 paul Exp $ + * $Id: n_hdlc.c,v 4.1 2002/04/10 19:30:58 paulkf Exp $ * * This code is released under the GNU General Public License (GPL) * @@ -78,7 +78,7 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "3.2" +#define HDLC_VERSION "$Revision: 4.1 $" #include <linux/version.h> #include <linux/config.h> @@ -159,11 +159,6 @@ struct n_hdlc { struct tty_struct *tty; /* ptr to TTY structure */ struct tty_struct *backup_tty; /* TTY to use if tty gets closed */ - /* Queues for select() functionality */ - wait_queue_head_t read_wait; - wait_queue_head_t write_wait; - wait_queue_head_t poll_wait; - int tbusy; /* reentrancy flag for tx wakeup code */ int woke_up; N_HDLC_BUF *tbuf; /* currently transmitting tx buffer */ @@ -234,9 +229,8 @@ static void n_hdlc_release (struct n_hdlc *n_hdlc) printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__); /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ - wake_up_interruptible (&n_hdlc->read_wait); - wake_up_interruptible (&n_hdlc->poll_wait); - wake_up_interruptible (&n_hdlc->write_wait); + wake_up_interruptible (&tty->read_wait); + wake_up_interruptible (&tty->write_wait); if (tty != NULL && tty->disc_data == n_hdlc) tty->disc_data = NULL; /* Break the tty->n_hdlc link */ @@ -431,8 +425,7 @@ static void n_hdlc_send_frames (struct n_hdlc *n_hdlc, struct tty_struct *tty) n_hdlc->tbuf = NULL; /* wait up sleeping writers */ - wake_up_interruptible(&n_hdlc->write_wait); - wake_up_interruptible(&n_hdlc->poll_wait); + wake_up_interruptible(&tty->write_wait); /* get next pending transmit buffer */ tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); @@ -574,8 +567,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf); /* wake up any blocked reads and perform async signalling */ - wake_up_interruptible (&n_hdlc->read_wait); - wake_up_interruptible (&n_hdlc->poll_wait); + wake_up_interruptible (&tty->read_wait); if (n_hdlc->tty->fasync != NULL) kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); @@ -620,6 +612,9 @@ static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, } for (;;) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + return -EIO; + n_hdlc = tty2n_hdlc (tty); if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || tty != n_hdlc->tty) @@ -633,7 +628,7 @@ static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, if (file->f_flags & O_NONBLOCK) return -EAGAIN; - interruptible_sleep_on (&n_hdlc->read_wait); + interruptible_sleep_on (&tty->read_wait); if (signal_pending(current)) return -EINTR; } @@ -702,7 +697,7 @@ static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, count = maxframe; } - add_wait_queue(&n_hdlc->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); /* Allocate transmit buffer */ @@ -725,7 +720,7 @@ static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, } set_current_state(TASK_RUNNING); - remove_wait_queue(&n_hdlc->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); if (!error) { /* Retrieve the user's buffer */ @@ -835,12 +830,14 @@ static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) { /* queue current process into any wait queue that */ /* may awaken in the future (read and write) */ - poll_wait(filp, &n_hdlc->poll_wait, wait); + + poll_wait(filp, &tty->read_wait, wait); + poll_wait(filp, &tty->write_wait, wait); /* set bits for operations that wont block */ if(n_hdlc->rx_buf_list.head) mask |= POLLIN | POLLRDNORM; /* readable */ - if(tty->flags & (1 << TTY_OTHER_CLOSED)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if(tty_hung_up_p(filp)) mask |= POLLHUP; @@ -894,11 +891,7 @@ static struct n_hdlc *n_hdlc_alloc (void) /* Initialize the control block */ n_hdlc->magic = HDLC_MAGIC; - n_hdlc->flags = 0; - init_waitqueue_head(&n_hdlc->read_wait); - init_waitqueue_head(&n_hdlc->poll_wait); - init_waitqueue_head(&n_hdlc->write_wait); return n_hdlc; diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index c50bc72474249103eb6f60f192251177a9a95884..583adeb4333b68511e1dcd85984b5e8c8a7ab09c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -1,7 +1,7 @@ /* * linux/drivers/char/synclink.c * - * $Id: synclink.c,v 3.12 2001/07/18 19:14:21 paulkf Exp $ + * $Id: synclink.c,v 4.2 2002/04/10 20:45:13 paulkf Exp $ * * Device driver for Microgate SyncLink ISA and PCI * high speed multiprotocol serial adapters. @@ -60,8 +60,6 @@ # define BREAKPOINT() { } #endif -#error Please convert me to Documentation/DMA-mapping.txt - #define MAX_ISA_DEVICES 10 #define MAX_PCI_DEVICES 10 #define MAX_TOTAL_DEVICES 20 @@ -109,12 +107,8 @@ #endif #ifdef CONFIG_SYNCLINK_SYNCPPP -#if LINUX_VERSION_CODE < VERSION(2,4,3) -#include "../net/wan/syncppp.h" -#else #include <net/syncppp.h> #endif -#endif #define GET_USER(error,value,addr) error = get_user(value,addr) #define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 @@ -923,7 +917,7 @@ MODULE_PARM(txdmabufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i"); MODULE_PARM(txholdbufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i"); static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "$Revision: 3.12 $"; +static char *driver_version = "$Revision: 4.2 $"; static int synclink_init_one (struct pci_dev *dev, const struct pci_device_id *ent); @@ -3985,7 +3979,7 @@ int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info ) if ( info->buffer_list == NULL ) return -ENOMEM; - info->buffer_list_phys = virt_to_bus(info->buffer_list); + info->buffer_list_phys = isa_virt_to_bus(info->buffer_list); } /* We got the memory for the buffer entry lists. */ @@ -4096,7 +4090,7 @@ int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList, kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA); if ( BufferList[i].virt_addr == NULL ) return -ENOMEM; - phys_addr = virt_to_bus(BufferList[i].virt_addr); + phys_addr = isa_virt_to_bus(BufferList[i].virt_addr); } BufferList[i].phys_addr = phys_addr; } @@ -7991,10 +7985,6 @@ void mgsl_sppp_init(struct mgsl_struct *info) d->tx_timeout = mgsl_sppp_tx_timeout; d->watchdog_timeo = 10*HZ; -#if LINUX_VERSION_CODE < VERSION(2,4,4) - dev_init_buffers(d); -#endif - if (register_netdev(d) == -1) { printk(KERN_WARNING "%s: register_netdev failed.\n", d->name); sppp_detach(info->netdev); diff --git a/drivers/char/wdt977.c b/drivers/char/wdt977.c index 0e32706edf895f048b54790fa3a803eb7c257428..525bd23e8de42ce94d85cb1d6396ea06229f9758 100644 --- a/drivers/char/wdt977.c +++ b/drivers/char/wdt977.c @@ -90,7 +90,7 @@ int kick_wdog(void) static int wdt977_open(struct inode *inode, struct file *file) { - if(timer_alive) + if( test_and_set_bit(0,&timer_alive) ) return -EBUSY; /* convert seconds to minutes, rounding up */ @@ -104,7 +104,6 @@ static int wdt977_open(struct inode *inode, struct file *file) /* do not permit disabling the watchdog by writing 0 to reg. 0xF2 */ if (!timeoutM) timeoutM = DEFAULT_TIMEOUT; } - timer_alive++; if (machine_is_netwinder()) { @@ -165,8 +164,6 @@ static int wdt977_release(struct inode *inode, struct file *file) */ if (!nowayout) { - lock_kernel(); - /* unlock the SuperIO chip */ outb(0x87,0x370); outb(0x87,0x370); @@ -196,8 +193,7 @@ static int wdt977_release(struct inode *inode, struct file *file) /* lock the SuperIO chip */ outb(0xAA,0x370); - timer_alive=0; - unlock_kernel(); + clear_bit(0,&timer_alive); printk(KERN_INFO "Wdt977 Watchdog: shutdown\n"); } diff --git a/drivers/fc4/Makefile b/drivers/fc4/Makefile index 0a94cdf2716cb9a22dbe53a771647169899278e5..6fc3c1f58004de3273407591750a4ab955a85dc4 100644 --- a/drivers/fc4/Makefile +++ b/drivers/fc4/Makefile @@ -1,4 +1,3 @@ -# File: drivers/fc4/Makefile # # Makefile for the Linux Fibre Channel device drivers. # @@ -7,7 +6,6 @@ L_TARGET := fc4.a export-objs := fc_syms.o -list-multi := fc4.o fc4-objs := fc.o fc_syms.o obj-$(CONFIG_FC4) += fc4.o @@ -15,6 +13,3 @@ obj-$(CONFIG_FC4_SOC) += soc.o obj-$(CONFIG_FC4_SOCAL) += socal.o include $(TOPDIR)/Rules.make - -fc4.o: $(fc4-objs) - $(LD) -r -o $@ $(fc4-objs) diff --git a/drivers/ide/Config.help b/drivers/ide/Config.help index 0f1a67ef1fe9d24eb11adef3455b80aa6e8de2a2..2553f3a4d39d51c56ecf0494a67fde3a06364599 100644 --- a/drivers/ide/Config.help +++ b/drivers/ide/Config.help @@ -160,7 +160,7 @@ CONFIG_BLK_DEV_IDESCSI you can then use this emulation together with an appropriate SCSI device driver. In order to do this, say Y here and to "SCSI support" and "SCSI generic support", below. You must then provide the kernel - command line "hdx=scsi" (try "man bootparam" or see the + command line "hdx=ide-scsi" (try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time) for devices if you want the native EIDE sub-drivers to skip over the native support, so that @@ -543,13 +543,13 @@ CONFIG_BLK_DEV_HT6560B CONFIG_BLK_DEV_PDC4030 This driver provides support for the secondary IDE interface and - cache of Promise IDE chipsets, e.g. DC4030 and DC5030. This driver - is known to incur timeouts/retries during heavy I/O to drives - attached to the secondary interface. CD-ROM and TAPE devices are - not supported yet. This driver is enabled at runtime using the - "ide0=dc4030" kernel boot parameter. See the - <file:Documentation/ide.txt> and <file:drivers/ide/pdc4030.c> files - for more info. + cache of the original Promise IDE chipsets, e.g. DC4030 and DC5030. + It is nothing to do with the later range of Promise UDMA chipsets - + see the PDC_202XX support for these. CD-ROM and TAPE devices are not + supported (and probably never will be since I don't think the cards + support them). This driver is enabled at runtime using the "ide0=dc4030" + or "ide1=dc4030" kernel boot parameter. See the + <file:drivers/ide/pdc4030.c> file for more info. CONFIG_BLK_DEV_QD65XX This driver is enabled at runtime using the "ide0=qd65xx" kernel @@ -629,6 +629,11 @@ CONFIG_BLK_DEV_MAC_IDE devices (hard disks, CD-ROM drives, etc.) that are connected to the builtin IDE interface. +CONFIG_BLK_DEV_Q40IDE + Enable the on-board IDE controller in the Q40/Q60. This should + normally be on; disable it only if you are running a custom hard + drive subsystem through an expansion card. + CONFIG_BLK_DEV_IDE_ICSIDE On Acorn systems, say Y here if you wish to use the ICS IDE interface card. This is not required for ICS partition support. diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 949946d1003915e4e0b58d0a2dfded4bd21b3100..7bf39a68e7c28e0ee9f00e044605773a3dba0648 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -11,7 +11,6 @@ O_TARGET := idedriver.o export-objs := ide-taskfile.o ide.o ide-features.o ide-probe.o ataraid.o -list-multi := ide-mod.o obj-y := obj-m := @@ -78,6 +77,3 @@ ide-obj-$(CONFIG_PROC_FS) += ide-proc.o ide-mod-objs := ide-taskfile.o ide.o ide-probe.o ide-geometry.o ide-features.o ata-timing.o $(ide-obj-y) include $(TOPDIR)/Rules.make - -ide-mod.o: $(ide-mod-objs) - $(LD) -r -o $@ $(ide-mod-objs) diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 8ba903287a7060f450abd44c12ce5555387e88e0..e3c66cb17ad9cdb91092c7dfc10684dfbc485d0f 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -337,7 +337,9 @@ void cdrom_analyze_sense_data(ide_drive_t *drive, struct request *rq) int log = 0; /* FIXME --mdcki */ struct packet_command *pc = (struct packet_command *) rq->special; - struct packet_command *failed_command = (struct packet_command *) pc->sense; + struct packet_command *failed_command = pc->failed_command; + + /* Decode sense data from drive */ struct request_sense *sense = (struct request_sense *) (pc->buffer - rq->cmd[4]); unsigned char fail_cmd; @@ -526,10 +528,10 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, if (sense == NULL) sense = &info->sense_data; - memset(pc, 0, sizeof(struct packet_command)); + memset(pc, 0, sizeof(*pc)); pc->buffer = (void *) sense; pc->buflen = 18; - pc->sense = (struct request_sense *) failed_command; + pc->failed_command = failed_command; /* stuff the sense request in front of our current request */ rq = &info->request_sense_request; @@ -598,6 +600,7 @@ static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, pc->stat = 1; cdrom_end_request(drive, 1); *startstop = ide_error (drive, "request sense failure", stat); + return 1; } else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) { /* All other functions, except for READ. */ @@ -635,6 +638,10 @@ static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, pc->stat = 1; cdrom_end_request(drive, 1); + /* FIXME: this is the only place where pc->sense get's used. + * Think hard about how to get rid of it... + */ + if ((stat & ERR_STAT) != 0) cdrom_queue_request_sense(drive, wait, pc->sense, pc); } else if (rq->flags & REQ_CMD) { @@ -728,27 +735,26 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, return startstop; if (info->dma) { - if (info->cmd == READ) { + if (info->cmd == READ) info->dma = !drive->channel->dmaproc(ide_dma_read, drive); - } else if (info->cmd == WRITE) { + else if (info->cmd == WRITE) info->dma = !drive->channel->dmaproc(ide_dma_write, drive); - } else { + else printk("ide-cd: DMA set, but not allowed\n"); - } } /* Set up the controller registers. */ - OUT_BYTE (info->dma, IDE_FEATURE_REG); - OUT_BYTE (0, IDE_NSECTOR_REG); - OUT_BYTE (0, IDE_SECTOR_REG); + OUT_BYTE(info->dma, IDE_FEATURE_REG); + OUT_BYTE(0, IDE_NSECTOR_REG); + OUT_BYTE(0, IDE_SECTOR_REG); - OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG); - OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG); + OUT_BYTE(xferlen & 0xff, IDE_LCYL_REG); + OUT_BYTE(xferlen >> 8 , IDE_HCYL_REG); if (IDE_CONTROL_REG) OUT_BYTE (drive->ctl, IDE_CONTROL_REG); if (info->dma) - (void) drive->channel->dmaproc(ide_dma_begin, drive); + drive->channel->dmaproc(ide_dma_begin, drive); if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { BUG_ON(HWGROUP(drive)->handler); @@ -791,7 +797,7 @@ static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, ide_set_handler(drive, handler, timeout, cdrom_timer_expiry); /* Send the command to the device. */ - atapi_output_bytes(drive, cmd, CDROM_PACKET_SIZE); + atapi_write(drive, cmd, CDROM_PACKET_SIZE); return ide_started; } @@ -830,7 +836,7 @@ static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, /* Read the data into the buffer. */ dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE; while (sectors_to_buffer > 0) { - atapi_input_bytes (drive, dest, SECTOR_SIZE); + atapi_read(drive, dest, SECTOR_SIZE); --sectors_to_buffer; --sectors_to_transfer; ++info->nsectors_buffered; @@ -840,7 +846,7 @@ static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, /* Throw away any remaining data. */ while (sectors_to_transfer > 0) { char dum[SECTOR_SIZE]; - atapi_input_bytes (drive, dum, sizeof (dum)); + atapi_read(drive, dum, sizeof (dum)); --sectors_to_transfer; } } @@ -866,7 +872,7 @@ int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason) and quit this request. */ while (len > 0) { int dum = 0; - atapi_output_bytes (drive, &dum, sizeof (dum)); + atapi_write(drive, &dum, sizeof (dum)); len -= sizeof (dum); } } else if (ireason == 1) { @@ -963,7 +969,7 @@ static ide_startstop_t cdrom_read_intr(ide_drive_t *drive) while (nskip > 0) { /* We need to throw away a sector. */ char dum[SECTOR_SIZE]; - atapi_input_bytes (drive, dum, sizeof (dum)); + atapi_read(drive, dum, SECTOR_SIZE); --rq->current_nr_sectors; --nskip; @@ -994,7 +1000,7 @@ static ide_startstop_t cdrom_read_intr(ide_drive_t *drive) /* Read this_transfer sectors into the current buffer. */ while (this_transfer > 0) { - atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE); + atapi_read(drive, rq->buffer, SECTOR_SIZE); rq->buffer += SECTOR_SIZE; --rq->nr_sectors; --rq->current_nr_sectors; @@ -1290,13 +1296,14 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) /* The drive wants to be written to. */ if ((ireason & 3) == 0) { /* Transfer the data. */ - atapi_output_bytes (drive, pc->buffer, thislen); + atapi_write(drive, pc->buffer, thislen); /* If we haven't moved enough data to satisfy the drive, add some padding. */ while (len > thislen) { int dum = 0; - atapi_output_bytes (drive, &dum, sizeof (dum)); + + atapi_write(drive, &dum, sizeof (dum)); len -= sizeof (dum); } @@ -1307,15 +1314,14 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) /* Same drill for reading. */ else if ((ireason & 3) == 2) { - /* Transfer the data. */ - atapi_input_bytes (drive, pc->buffer, thislen); + atapi_read(drive, pc->buffer, thislen); /* If we haven't moved enough data to satisfy the drive, add some padding. */ while (len > thislen) { int dum = 0; - atapi_input_bytes (drive, &dum, sizeof (dum)); + atapi_read(drive, &dum, sizeof (dum)); len -= sizeof (dum); } @@ -1386,17 +1392,13 @@ void cdrom_sleep (int time) } static -int cdrom_queue_packet_command(ide_drive_t *drive, unsigned char *cmd, struct packet_command *pc) +int cdrom_queue_packet_command(ide_drive_t *drive, unsigned char *cmd, + struct request_sense *sense, struct packet_command *pc) { - struct request_sense sense; struct request rq; int retries = 10; memcpy(rq.cmd, cmd, CDROM_PACKET_SIZE); - - if (pc->sense == NULL) - pc->sense = &sense; - /* Start of retry loop. */ do { ide_init_drive_cmd(&rq); @@ -1408,18 +1410,25 @@ int cdrom_queue_packet_command(ide_drive_t *drive, unsigned char *cmd, struct pa if (ide_do_drive_cmd(drive, &rq, ide_wait)) { printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n", drive->name, rq.buffer[0], rq.buffer[1]); + /* FIXME: we should probably abort/retry or something */ + if (sense) { + + /* Decode the error here at least for error + * reporting to upper layers.! + */ + + } } if (pc->stat != 0) { /* The request failed. Retry if it was due to a unit attention status (usually means media was changed). */ - struct request_sense *reqbuf = pc->sense; - if (reqbuf->sense_key == UNIT_ATTENTION) + if (sense && sense->sense_key == UNIT_ATTENTION) cdrom_saw_media_change (drive); - else if (reqbuf->sense_key == NOT_READY && - reqbuf->asc == 4 && reqbuf->ascq != 4) { + else if (sense && sense->sense_key == NOT_READY && + sense->asc == 4 && sense->ascq != 4) { /* The drive is in the process of loading a disk. Retry, but wait a little to give the drive time to complete the load. */ @@ -1458,7 +1467,7 @@ static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ire and quit this request. */ while (len > 0) { int dum = 0; - atapi_output_bytes(drive, &dum, sizeof(dum)); + atapi_write(drive, &dum, sizeof(dum)); len -= sizeof(dum); } } else { @@ -1549,7 +1558,7 @@ static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) this_transfer = MIN(sectors_to_transfer,rq->current_nr_sectors); while (this_transfer > 0) { - atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE); + atapi_write(drive, rq->buffer, SECTOR_SIZE); rq->buffer += SECTOR_SIZE; --rq->nr_sectors; --rq->current_nr_sectors; @@ -1753,7 +1762,7 @@ static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense) cmd[7] = cdi->sanyo_slot % 3; #endif - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, sense, &pc); } @@ -1774,7 +1783,7 @@ cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense) pc.sense = sense; cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; cmd[4] = lockflag ? 1 : 0; - stat = cdrom_queue_packet_command(drive, cmd, &pc); + stat = cdrom_queue_packet_command(drive, cmd, sense, &pc); } /* If we got an illegal field error, the drive @@ -1819,7 +1828,7 @@ static int cdrom_eject(ide_drive_t *drive, int ejectflag, cmd[0] = GPCMD_START_STOP_UNIT; cmd[4] = 0x02 + (ejectflag != 0); - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, sense, &pc); } static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, @@ -1840,7 +1849,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, cmd[0] = GPCMD_READ_CDVD_CAPACITY; pc.buffer = (char *)&capbuf; pc.buflen = sizeof(capbuf); - stat = cdrom_queue_packet_command(drive, cmd, &pc); + stat = cdrom_queue_packet_command(drive, cmd, sense, &pc); if (stat == 0) *capacity = 1 + be32_to_cpu(capbuf.lba); @@ -1869,7 +1878,7 @@ static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, cmd[8] = (buflen & 0xff); cmd[9] = (format << 6); - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, sense, &pc); } @@ -2046,7 +2055,7 @@ static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf, cmd[7] = (buflen >> 8); cmd[8] = (buflen & 0xff); - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, sense, &pc); } /* ATAPI cdrom drives are free to select the speed you request or any slower @@ -2078,7 +2087,7 @@ static int cdrom_select_speed(ide_drive_t *drive, int speed, cmd[5] = speed & 0xff; } - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, sense, &pc); } static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end) @@ -2094,7 +2103,7 @@ static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end) lba_to_msf(lba_start, &cmd[3], &cmd[4], &cmd[5]); lba_to_msf(lba_end-1, &cmd[6], &cmd[7], &cmd[8]); - return cdrom_queue_packet_command(drive, cmd, &pc); + return cdrom_queue_packet_command(drive, cmd, &sense, &pc); } static int cdrom_get_toc_entry(ide_drive_t *drive, int track, @@ -2143,7 +2152,7 @@ static int ide_cdrom_packet(struct cdrom_device_info *cdi, pc.quiet = cgc->quiet; pc.timeout = cgc->timeout; pc.sense = cgc->sense; - cgc->stat = cdrom_queue_packet_command(drive, cgc->cmd, &pc); + cgc->stat = cdrom_queue_packet_command(drive, cgc->cmd, cgc->sense, &pc); if (!cgc->stat) cgc->buflen -= pc.buflen; diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index eb86e051ec2f46ffc67c4857ffe5c66a5afaf26c..6ade07b09f4b4dd0855113a9b62c3ca130db1a4b 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -104,6 +104,15 @@ struct packet_command { int quiet; int timeout; struct request_sense *sense; + + /* This is currently used to pass failed commands through the request + * queue. Is this for asynchronos error reporting? + * + * Can we always be sure that this didn't valish from stack beneath us + * - we can't! + */ + + struct packet_command *failed_command; }; /* Structure of a MSF cdrom address. */ diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index d6d0ef2853af5b932245efbb56533eb759143f27..26c07c5baf9f819bfff4c4c7c6f78c072a76c38c 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -1051,7 +1051,6 @@ static void idedisk_add_settings(ide_drive_t *drive) ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); ide_add_setting(drive, "address", SETTING_RW, HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, TYPE_INTA, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); - ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 1, &drive->mult_count, set_multcount); ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); ide_add_setting(drive, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); @@ -1198,11 +1197,11 @@ static void idedisk_setup(ide_drive_t *drive) if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && (!drive->forced_geom) && drive->bios_sect && drive->bios_head) drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; - printk (KERN_INFO "%s: %ld sectors", drive->name, capacity); + printk(KERN_INFO "%s: %ld sectors", drive->name, capacity); /* Give size in megabytes (MB), not mebibytes (MiB). */ /* We compute the exact rounded value, avoiding overflow. */ - printk (" (%ld MB)", (capacity - capacity/625 + 974)/1950); + printk(" (%ld MB)", (capacity - capacity/625 + 974)/1950); /* Only print cache size when it was specified */ if (id->buf_size) @@ -1213,7 +1212,7 @@ static void idedisk_setup(ide_drive_t *drive) #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma) (void) drive->channel->dmaproc(ide_dma_verbose, drive); -#endif /* CONFIG_BLK_DEV_IDEDMA */ +#endif printk("\n"); drive->mult_count = 0; @@ -1223,15 +1222,17 @@ static void idedisk_setup(ide_drive_t *drive) id->multsect_valid = id->multsect ? 1 : 0; drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT; drive->special.b.set_multmode = drive->mult_req ? 1 : 0; -#else /* original, pre IDE-NFG, per request of AC */ +#else + /* original, pre IDE-NFG, per request of AC */ drive->mult_req = INITIAL_MULT_COUNT; if (drive->mult_req > id->max_multsect) drive->mult_req = id->max_multsect; if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect)) drive->special.b.set_multmode = 1; -#endif /* CONFIG_IDEDISK_MULTI_MODE */ +#endif } drive->no_io_32bit = id->dword_io ? 1 : 0; + if (drive->id->cfs_enable_2 & 0x3000) write_cache(drive, (id->cfs_enable_2 & 0x3000)); probe_lba_addressing(drive, 1); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 79bb86ec86b3ad7f3681f17eaf52251e334965fc..666aff1d1fe74a455c9c5e383268ca54f3466f59 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -372,10 +372,10 @@ int ide_build_dmatable(ide_drive_t *drive, struct request *rq, } /* Teardown mappings after DMA has completed. */ -void ide_destroy_dmatable (ide_drive_t *drive) +void ide_destroy_dmatable(struct ata_device *d) { - struct pci_dev *dev = drive->channel->pci_dev; - struct ata_request *ar = IDE_CUR_AR(drive); + struct pci_dev *dev = d->channel->pci_dev; + struct ata_request *ar = IDE_CUR_AR(d); pci_unmap_sg(dev, ar->ar_sg_table, ar->ar_sg_nents, ar->ar_sg_ddir); } diff --git a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c index a0543dc81162d681a9903be6d148c3d75429f5b2..fdede6a2d27e396a5d68059e39749d8ffc932d02 100644 --- a/drivers/ide/ide-features.c +++ b/drivers/ide/ide-features.c @@ -130,8 +130,8 @@ int ide_driveid_update (ide_drive_t *drive) __restore_flags(flags); /* local CPU only */ return 0; } - ata_input_data(drive, id, SECTOR_WORDS); - (void) GET_STAT(); /* clear drive IRQ */ + ata_read(drive, id, SECTOR_WORDS); + GET_STAT(); /* clear drive IRQ */ ide__sti(); /* local CPU only */ __restore_flags(flags); /* local CPU only */ ide_fix_driveid(id); @@ -285,7 +285,7 @@ int ide_config_drive_speed (ide_drive_t *drive, byte speed) enable_irq(hwif->irq); if (error) { - (void) ide_dump_status(drive, "set_drive_speed_status", stat); + ide_dump_status(drive, "set_drive_speed_status", stat); return error; } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index e988e17cee6935be0c77722a3ebb6bc1fd158f32..0bffd4f386aca8847bb281416af1412d30ba5de8 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -1,8 +1,8 @@ /* - * linux/drivers/ide/ide-floppy.c Version 0.97.sv Jan 14 2001 + * linux/drivers/ide/ide-floppy.c Version 0.99 Feb 24 2002 * * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il> - * Copyright (C) 2000 - 2001 Paul Bristow <paul@paulbristow.net> + * Copyright (C) 2000 - 2002 Paul Bristow <paul@paulbristow.net> */ /* @@ -13,7 +13,7 @@ * * This driver supports the following IDE floppy drives: * - * LS-120 SuperDisk + * LS-120/240 SuperDisk * Iomega Zip 100/250 * Iomega PC Card Clik!/PocketZip * @@ -76,9 +76,11 @@ * bit was being deasserted by my IOMEGA ATAPI ZIP 100 * drive before the drive was actually ready. * Ver 0.98a Oct 29 01 Expose delay value so we can play. + * Ver 0.99 Feb 24 02 Remove duplicate code, modify clik! detection code + * to support new PocketZip drives */ -#define IDEFLOPPY_VERSION "0.98a" +#define IDEFLOPPY_VERSION "0.99" #include <linux/config.h> #include <linux/module.h> @@ -717,7 +719,7 @@ static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, uns return; } count = IDEFLOPPY_MIN (bio->bi_size - pc->b_count, bcount); - atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count); + atapi_read(drive, bio_data(bio) + pc->b_count, count); bcount -= count; pc->b_count += count; } } @@ -744,7 +746,7 @@ static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, un return; } count = IDEFLOPPY_MIN (pc->b_count, bcount); - atapi_output_bytes (drive, pc->b_data, count); + atapi_write(drive, pc->b_data, count); bcount -= count; pc->b_data += count; pc->b_count -= count; } } @@ -979,12 +981,12 @@ static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive) } if (test_bit (PC_WRITING, &pc->flags)) { if (pc->buffer != NULL) - atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */ + atapi_write(drive,pc->current_position,bcount.all); /* Write the current buffer */ else idefloppy_output_buffers (drive, pc, bcount.all); } else { if (pc->buffer != NULL) - atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */ + atapi_read(drive,pc->current_position,bcount.all); /* Read the current buffer */ else idefloppy_input_buffers (drive, pc, bcount.all); } @@ -1020,7 +1022,7 @@ static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) BUG_ON(HWGROUP(drive)->handler); ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* Set the interrupt routine */ - atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + atapi_write(drive, floppy->pc->c, 12); /* Send the actual packet */ return ide_started; } @@ -1042,7 +1044,7 @@ static int idefloppy_transfer_pc2 (ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; - atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + atapi_write(drive, floppy->pc->c, 12); /* Send the actual packet */ return IDEFLOPPY_WAIT_CMD; /* Timeout for the packet command */ } @@ -1070,8 +1072,8 @@ static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive) */ BUG_ON(HWGROUP(drive)->handler); ide_set_handler (drive, - &idefloppy_pc_intr, /* service routine for packet command */ - floppy->ticks, /* wait this long before "failing" */ + &idefloppy_pc_intr, /* service routine for packet command */ + floppy->ticks, /* wait this long before "failing" */ &idefloppy_transfer_pc2); /* fail == transfer_pc2 */ return ide_started; @@ -2005,7 +2007,7 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) * above fix. It makes nasty clicking noises without * it, so please don't remove this. */ - if (strcmp(drive->id->model, "IOMEGA Clik! 40 CZ ATAPI") == 0) { + if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(&drive->queue, 64); set_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags); } diff --git a/drivers/ide/ide-m8xx.c b/drivers/ide/ide-m8xx.c index 33158b8579ecc106de20b843b0598d58dad03eff..e71832c49df3e89db77604c07e383050d1f6760c 100644 --- a/drivers/ide/ide-m8xx.c +++ b/drivers/ide/ide-m8xx.c @@ -1,8 +1,14 @@ /* - * - * * linux/drivers/ide/ide-m8xx.c * + * Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de + * Modified for direct IDE interface + * by Thomas Lange, thomas@corelatus.com + * Modified for direct IDE interface on 8xx without using the PCMCIA + * controller + * by Steven.Scholz@imc-berlin.de + * Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups + * by Mathew Locke <mattl@mvista.com> */ #include <linux/config.h> diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 20b37da028646f156580e1a952dc60ee7032497e..3bfe703e69efd89ceb82f71b78cd3dfbbbdb9047 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -57,9 +57,18 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) printk(KERN_WARNING "(ide-probe::do_identify) Out of memory.\n"); goto err_kmalloc; } - /* read 512 bytes of id info */ + + /* Read 512 bytes of id info. + * + * Please note that it is well known that some *very* old drives are + * able to provide only 256 of them, since this was the amount read by + * DOS. + * + * However let's try to get away with this... + */ + #if 1 - ata_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */ + ata_read(drive, id, SECTOR_WORDS); #else { unsigned long *ptr = (unsigned long *)id ; @@ -580,10 +589,10 @@ static void probe_hwif(struct ata_channel *hwif) __restore_flags(flags); /* local CPU only */ for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; - if (drive->present) { - ide_tuneproc_t *tuneproc = drive->channel->tuneproc; - if (tuneproc != NULL && drive->autotune == 1) - tuneproc(drive, 255); /* auto-tune PIO mode */ + + if (drive->present && (drive->autotune == 1)) { + if (drive->channel->tuneproc != NULL) + drive->channel->tuneproc(drive, 255); /* auto-tune PIO mode */ } } } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 3509b40371408ae05df982bc96c0ed30901c581d..e82e9415af39866749b62863c28e74eb8d9f1c89 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -422,7 +422,6 @@ void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) static void create_proc_ide_drives(struct ata_channel *hwif) { int d; - struct proc_dir_entry *ent; struct proc_dir_entry *parent = hwif->proc; char name[64]; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 804c0f9dc19f6afe40e3992bdf80380bed42e6ed..4c4e1a1641c6e7b5c92676430e4b2f6caf743691 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1503,9 +1503,9 @@ static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigne idetape_discard_data (drive, bcount); return; } -#endif /* IDETAPE_DEBUG_BUGS */ +#endif count = min(bio->bi_size - pc->b_count, bcount); - atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count); + atapi_read(drive, bio_data(bio) + pc->b_count, count); bcount -= count; pc->b_count += bio->bi_size; if (pc->b_count == bio->bi_size) { @@ -1530,7 +1530,7 @@ static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsign } #endif /* IDETAPE_DEBUG_BUGS */ count = min(pc->b_count, bcount); - atapi_output_bytes (drive, bio_data(bio), count); + atapi_write(drive, bio_data(bio), count); bcount -= count; pc->b_data += count; pc->b_count -= count; @@ -2169,12 +2169,12 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive) if (pc->bio != NULL) idetape_output_buffers (drive, pc, bcount.all); else - atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */ + atapi_write(drive,pc->current_position,bcount.all); /* Write the current buffer */ } else { if (pc->bio != NULL) idetape_input_buffers (drive, pc, bcount.all); else - atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */ + atapi_read(drive,pc->current_position,bcount.all); /* Read the current buffer */ } pc->actually_transferred += bcount.all; /* Update the current position */ pc->current_position+=bcount.all; @@ -2259,7 +2259,7 @@ static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) tape->cmd_start_time = jiffies; BUG_ON(HWGROUP(drive)->handler); ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* Set the interrupt routine */ - atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ + atapi_write(drive,pc->c,12); /* Send the actual packet */ return ide_started; } diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index e86e4487fa3552e6d0f7459971390d8e37b03798..d073d42262c30675d3737ff9cab5c99a4d17033f 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2002 Marcin Dalecki <martin@dalecki.de> * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> * @@ -33,7 +34,7 @@ #define DEBUG_TASKFILE 0 /* unset when fixed */ #if DEBUG_TASKFILE -#define DTF(x...) printk(x) +#define DTF(x...) printk(##x) #else #define DTF(x...) #endif @@ -58,15 +59,9 @@ static inline void ide_unmap_rq(struct request *rq, char *to, bio_kunmap_irq(to, flags); } -static void bswap_data (void *buffer, int wcount) -{ - u16 *p = buffer; - - while (wcount--) { - *p = *p << 8 | *p >> 8; p++; - *p = *p << 8 | *p >> 8; p++; - } -} +/* + * Data transfer functions for polled IO. + */ #if SUPPORT_VLB_SYNC /* @@ -76,30 +71,88 @@ static void bswap_data (void *buffer, int wcount) * of the sector count register location, with interrupts disabled * to ensure that the reads all happen together. */ -static inline void task_vlb_sync(ide_drive_t *drive) +static void ata_read_vlb(struct ata_device *drive, void *buffer, unsigned int wcount) +{ + unsigned long flags; + + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_NSECTOR_REG); + insl(IDE_DATA_REG, buffer, wcount); + __restore_flags(flags); /* local CPU only */ +} + +static void ata_write_vlb(struct ata_device *drive, void *buffer, unsigned int wcount) +{ + unsigned long flags; + + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_NSECTOR_REG); + outsl(IDE_DATA_REG, buffer, wcount); + __restore_flags(flags); /* local CPU only */ +} +#endif + +static void ata_read_32(struct ata_device *drive, void *buffer, unsigned int wcount) +{ + insl(IDE_DATA_REG, buffer, wcount); +} + +static void ata_write_32(struct ata_device *drive, void *buffer, unsigned int wcount) +{ + outsl(IDE_DATA_REG, buffer, wcount); +} + +#if SUPPORT_SLOW_DATA_PORTS +static void ata_read_slow(struct ata_device *drive, void *buffer, unsigned int wcount) +{ + unsigned short *ptr = (unsigned short *) buffer; + + while (wcount--) { + *ptr++ = inw_p(IDE_DATA_REG); + *ptr++ = inw_p(IDE_DATA_REG); + } +} + +static void ata_write_slow(struct ata_device *drive, void *buffer, unsigned int wcount) { - ide_ioreg_t port = IDE_NSECTOR_REG; + unsigned short *ptr = (unsigned short *) buffer; - IN_BYTE(port); - IN_BYTE(port); - IN_BYTE(port); + while (wcount--) { + outw_p(*ptr++, IDE_DATA_REG); + outw_p(*ptr++, IDE_DATA_REG); + } } #endif +static void ata_read_16(ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + insw(IDE_DATA_REG, buffer, wcount<<1); +} + +static void ata_write_16(ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + outsw(IDE_DATA_REG, buffer, wcount<<1); +} + /* - * This is used for most PIO data transfers *from* the IDE interface + * This is used for most PIO data transfers *from* the device. */ -void ata_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount) +void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount) { - byte io_32bit; + int io_32bit; /* - * first check if this controller has defined a special function - * for handling polled ide transfers + * First check if this controller has defined a special function + * for handling polled ide transfers. */ - - if (drive->channel->ideproc) { - drive->channel->ideproc(ideproc_ide_input_data, drive, buffer, wcount); + if (drive->channel->ata_read) { + drive->channel->ata_read(drive, buffer, wcount); return; } @@ -107,39 +160,30 @@ void ata_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount) if (io_32bit) { #if SUPPORT_VLB_SYNC - if (io_32bit & 2) { - unsigned long flags; - __save_flags(flags); /* local CPU only */ - __cli(); /* local CPU only */ - task_vlb_sync(drive); - insl(IDE_DATA_REG, buffer, wcount); - __restore_flags(flags); /* local CPU only */ - } else + if (io_32bit & 2) + ata_read_vlb(drive, buffer, wcount); + else #endif - insl(IDE_DATA_REG, buffer, wcount); + ata_read_32(drive, buffer, wcount); } else { #if SUPPORT_SLOW_DATA_PORTS - if (drive->slow) { - unsigned short *ptr = (unsigned short *) buffer; - while (wcount--) { - *ptr++ = inw_p(IDE_DATA_REG); - *ptr++ = inw_p(IDE_DATA_REG); - } - } else + if (drive->slow) + ata_read_slow(drive, buffer, wcount); + else #endif - insw(IDE_DATA_REG, buffer, wcount<<1); + ata_read_16(drive, buffer, wcount); } } /* - * This is used for most PIO data transfers *to* the IDE interface + * This is used for most PIO data transfers *to* the device interface. */ -void ata_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount) +void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount) { - byte io_32bit; + int io_32bit; - if (drive->channel->ideproc) { - drive->channel->ideproc(ideproc_ide_output_data, drive, buffer, wcount); + if (drive->channel->ata_write) { + drive->channel->ata_write(drive, buffer, wcount); return; } @@ -147,27 +191,18 @@ void ata_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount) if (io_32bit) { #if SUPPORT_VLB_SYNC - if (io_32bit & 2) { - unsigned long flags; - __save_flags(flags); /* local CPU only */ - __cli(); /* local CPU only */ - task_vlb_sync(drive); - outsl(IDE_DATA_REG, buffer, wcount); - __restore_flags(flags); /* local CPU only */ - } else + if (io_32bit & 2) + ata_write_vlb(drive, buffer, wcount); + else #endif - outsl(IDE_DATA_REG, buffer, wcount); + ata_write_32(drive, buffer, wcount); } else { #if SUPPORT_SLOW_DATA_PORTS - if (drive->slow) { - unsigned short *ptr = (unsigned short *) buffer; - while (wcount--) { - outw_p(*ptr++, IDE_DATA_REG); - outw_p(*ptr++, IDE_DATA_REG); - } - } else + if (drive->slow) + ata_write_slow(drive, buffer, wcount); + else #endif - outsw(IDE_DATA_REG, buffer, wcount<<1); + ata_write_16(drive, buffer, wcount<<1); } } @@ -178,10 +213,10 @@ void ata_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount) * so if an odd bytecount is specified, be sure that there's at least one * extra byte allocated for the buffer. */ -void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount) { - if (drive->channel->ideproc) { - drive->channel->ideproc(ideproc_atapi_input_bytes, drive, buffer, bytecount); + if (drive->channel->atapi_read) { + drive->channel->atapi_read(drive, buffer, bytecount); return; } @@ -193,15 +228,15 @@ void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount return; } #endif - ata_input_data (drive, buffer, bytecount / 4); + ata_read(drive, buffer, bytecount / 4); if ((bytecount & 0x03) >= 2) - insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); + insw(IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); } -void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount) { - if (drive->channel->ideproc) { - drive->channel->ideproc(ideproc_atapi_output_bytes, drive, buffer, bytecount); + if (drive->channel->atapi_write) { + drive->channel->atapi_write(drive, buffer, bytecount); return; } @@ -213,29 +248,11 @@ void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecoun return; } #endif - ata_output_data (drive, buffer, bytecount / 4); + ata_write(drive, buffer, bytecount / 4); if ((bytecount & 0x03) >= 2) outsw(IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); } -void taskfile_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount) -{ - ata_input_data(drive, buffer, wcount); - if (drive->bswap) - bswap_data(buffer, wcount); -} - -void taskfile_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount) -{ - if (drive->bswap) { - bswap_data(buffer, wcount); - ata_output_data(drive, buffer, wcount); - bswap_data(buffer, wcount); - } else { - ata_output_data(drive, buffer, wcount); - } -} - /* * Needed for PCI irq sharing */ @@ -311,8 +328,6 @@ static ide_startstop_t pre_task_mulout_intr(ide_drive_t *drive, struct request * static ide_startstop_t task_mulout_intr (ide_drive_t *drive) { byte stat = GET_STAT(); - /* FIXME: this should go possible as well */ - byte io_32bit = drive->io_32bit; struct request *rq = &HWGROUP(drive)->wrq; ide_hwgroup_t *hwgroup = HWGROUP(drive); int mcount = drive->mult_count; @@ -378,14 +393,13 @@ static ide_startstop_t task_mulout_intr (ide_drive_t *drive) } /* - * Ok, we're all setup for the interrupt - * re-entering us on the last transfer. + * Ok, we're all setup for the interrupt re-entering us on the + * last transfer. */ - taskfile_output_data(drive, buffer, nsect * SECTOR_WORDS); + ata_write(drive, buffer, nsect * SECTOR_WORDS); bio_kunmap_irq(buffer, &flags); } while (mcount); - drive->io_32bit = io_32bit; rq->errors = 0; if (hwgroup->handler == NULL) ide_set_handler(drive, task_mulout_intr, WAIT_CMD, NULL); @@ -542,7 +556,6 @@ ide_startstop_t task_no_data_intr (ide_drive_t *drive) static ide_startstop_t task_in_intr (ide_drive_t *drive) { byte stat = GET_STAT(); - byte io_32bit = drive->io_32bit; struct request *rq = HWGROUP(drive)->rq; char *pBuf = NULL; unsigned long flags; @@ -561,10 +574,8 @@ static ide_startstop_t task_in_intr (ide_drive_t *drive) pBuf = ide_map_rq(rq, &flags); DTF("Read: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_input_data(drive, pBuf, SECTOR_WORDS); + ata_read(drive, pBuf, SECTOR_WORDS); ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; if (--rq->current_nr_sectors <= 0) { /* (hs): swapped next 2 lines */ @@ -597,7 +608,7 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq) unsigned long flags; char *buf = ide_map_rq(rq, &flags); /* For Write_sectors we need to stuff the first sector */ - taskfile_output_data(drive, buf, SECTOR_WORDS); + ata_write(drive, buf, SECTOR_WORDS); rq->current_nr_sectors--; ide_unmap_rq(rq, buf, &flags); } else { @@ -613,8 +624,6 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq) static ide_startstop_t task_out_intr(ide_drive_t *drive) { byte stat = GET_STAT(); - /* FIXME: this should go possible as well */ - byte io_32bit = drive->io_32bit; struct request *rq = HWGROUP(drive)->rq; char *pBuf = NULL; unsigned long flags; @@ -631,9 +640,8 @@ static ide_startstop_t task_out_intr(ide_drive_t *drive) pBuf = ide_map_rq(rq, &flags); DTF("write: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors); - taskfile_output_data(drive, pBuf, SECTOR_WORDS); + ata_write(drive, pBuf, SECTOR_WORDS); ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; rq->errors = 0; rq->current_nr_sectors--; } @@ -649,8 +657,6 @@ static ide_startstop_t task_mulin_intr(ide_drive_t *drive) { unsigned int msect, nsect; byte stat = GET_STAT(); - /* FIXME: this should go possible as well */ - byte io_32bit = drive->io_32bit; struct request *rq = HWGROUP(drive)->rq; char *pBuf = NULL; unsigned long flags; @@ -676,10 +682,8 @@ static ide_startstop_t task_mulin_intr(ide_drive_t *drive) DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %d\n", pBuf, nsect, rq->current_nr_sectors); - drive->io_32bit = 0; - taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + ata_read(drive, pBuf, nsect * SECTOR_WORDS); ide_unmap_rq(rq, pBuf, &flags); - drive->io_32bit = io_32bit; rq->errors = 0; rq->current_nr_sectors -= nsect; msect -= nsect; @@ -897,20 +901,25 @@ void init_taskfile_request(struct request *rq) int ide_raw_taskfile(ide_drive_t *drive, struct ata_taskfile *args, byte *buf) { struct request rq; - struct ata_request ar; + struct ata_request star; + + ata_ar_init(drive, &star); + + /* Don't put this request on free_req list after usage. + */ + star.ar_flags |= ATA_AR_STATIC; - ata_ar_init(drive, &ar); init_taskfile_request(&rq); rq.buffer = buf; - memcpy(&ar.ar_task, args, sizeof(*args)); + memcpy(&star.ar_task, args, sizeof(*args)); if (args->command_type != IDE_DRIVE_TASK_NO_DATA) rq.current_nr_sectors = rq.nr_sectors = (args->hobfile.sector_count << 8) | args->taskfile.sector_count; - rq.special = &ar; + rq.special = ☆ return ide_do_drive_cmd(drive, &rq, ide_wait); } @@ -1025,12 +1034,11 @@ int ide_task_ioctl(ide_drive_t *drive, unsigned long arg) } EXPORT_SYMBOL(drive_is_ready); -EXPORT_SYMBOL(ata_input_data); -EXPORT_SYMBOL(ata_output_data); -EXPORT_SYMBOL(atapi_input_bytes); -EXPORT_SYMBOL(atapi_output_bytes); -EXPORT_SYMBOL(taskfile_input_data); -EXPORT_SYMBOL(taskfile_output_data); + +EXPORT_SYMBOL(ata_read); +EXPORT_SYMBOL(ata_write); +EXPORT_SYMBOL(atapi_read); +EXPORT_SYMBOL(atapi_write); EXPORT_SYMBOL(ata_taskfile); EXPORT_SYMBOL(recal_intr); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 001fef7c93c934fc6994fc32525e9a0bb240ed2b..caf72ef867c9487984d79a2f2014592d6324b60a 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -383,7 +383,7 @@ void ide_end_queued_request(ide_drive_t *drive, int uptodate, struct request *rq spin_lock_irqsave(&ide_lock, flags); if ((jiffies - ar->ar_time > ATA_AR_MAX_TURNAROUND) && drive->queue_depth > 1) { - printk("%s: exceeded max command turn-around time (%d seconds)\n", drive->name, ATA_AR_MAX_TURNAROUND / HZ); + printk(KERN_INFO "%s: exceeded max command turn-around time (%d seconds)\n", drive->name, ATA_AR_MAX_TURNAROUND / HZ); drive->queue_depth >>= 1; } @@ -474,7 +474,7 @@ void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, spin_unlock_irqrestore(&ide_lock, flags); } -static void ata_pre_reset (ide_drive_t *drive) +static void ata_pre_reset(ide_drive_t *drive) { if (ata_ops(drive) && ata_ops(drive)->pre_reset) ata_ops(drive)->pre_reset(drive); @@ -528,10 +528,9 @@ static ide_startstop_t ata_special (ide_drive_t *drive) printk("%s: ata_special: 0x%02x\n", drive->name, s->all); #endif if (s->b.set_tune) { - ide_tuneproc_t *tuneproc = drive->channel->tuneproc; s->b.set_tune = 0; - if (tuneproc != NULL) - tuneproc(drive, drive->tune_req); + if (drive->channel->tuneproc != NULL) + drive->channel->tuneproc(drive, drive->tune_req); } else if (drive->driver != NULL) { if (ata_ops(drive)->special) return ata_ops(drive)->special(drive); @@ -899,23 +898,24 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat) } /* - * try_to_flush_leftover_data() is invoked in response to a drive - * unexpectedly having its DRQ_STAT bit set. As an alternative to - * resetting the drive, this routine tries to clear the condition - * by read a sector's worth of data from the drive. Of course, + * This gets invoked in response to a drive unexpectedly having its DRQ_STAT + * bit set. As an alternative to resetting the drive, it tries to clear the + * condition by reading a sector's worth of data from the drive. Of course, * this may not help if the drive is *waiting* for data from *us*. */ static void try_to_flush_leftover_data (ide_drive_t *drive) { - int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; + int i = (drive->mult_count ? drive->mult_count : 1); if (drive->type != ATA_DISK) return; + while (i > 0) { - u32 buffer[16]; - unsigned int wcount = (i > 16) ? 16 : i; - i -= wcount; - ata_input_data (drive, buffer, wcount); + u32 buffer[SECTOR_WORDS]; + unsigned int count = (i > 1) ? 1 : i; + + ata_read(drive, buffer, count * SECTOR_WORDS); + i -= count; } } @@ -1002,16 +1002,18 @@ void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler) static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; - byte *args = (byte *) rq->buffer; - byte stat = GET_STAT(); + u8 *args = rq->buffer; + u8 stat = GET_STAT(); int retries = 10; ide__sti(); /* local CPU only */ if ((stat & DRQ_STAT) && args && args[3]) { - byte io_32bit = drive->io_32bit; + int io_32bit = drive->io_32bit; + drive->io_32bit = 0; - ata_input_data(drive, &args[4], args[3] * SECTOR_WORDS); + ata_read(drive, &args[4], args[3] * SECTOR_WORDS); drive->io_32bit = io_32bit; + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) udelay(100); } @@ -1019,6 +1021,7 @@ static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; } @@ -1266,31 +1269,26 @@ void ide_stall_queue(ide_drive_t *drive, unsigned long timeout) /* * Select the next drive which will be serviced. */ -static inline ide_drive_t *choose_drive(ide_hwgroup_t *hwgroup) +static ide_drive_t *choose_drive(ide_hwgroup_t *hwgroup) { - ide_drive_t *drive, *best; + ide_drive_t *tmp; + ide_drive_t *drive = NULL; + unsigned long sleep = 0; - best = NULL; - drive = hwgroup->drive; + tmp = hwgroup->drive; do { - if (!list_empty(&drive->queue.queue_head) - && (!drive->PADAM_sleep || time_after_eq(drive->PADAM_sleep, jiffies))) { - if (!best - || (drive->PADAM_sleep && (!best->PADAM_sleep || time_after(best->PADAM_sleep, drive->PADAM_sleep))) - || (!best->PADAM_sleep && time_after(best->PADAM_service_start + 2 * best->PADAM_service_time, drive->PADAM_service_start + 2 * drive->PADAM_service_time))) + if (!list_empty(&tmp->queue.queue_head) + && (!tmp->PADAM_sleep || time_after_eq(tmp->PADAM_sleep, jiffies))) { + if (!drive + || (tmp->PADAM_sleep && (!drive->PADAM_sleep || time_after(drive->PADAM_sleep, tmp->PADAM_sleep))) + || (!drive->PADAM_sleep && time_after(drive->PADAM_service_start + 2 * drive->PADAM_service_time, tmp->PADAM_service_start + 2 * tmp->PADAM_service_time))) { - if (!blk_queue_plugged(&drive->queue)) - best = drive; + if (!blk_queue_plugged(&tmp->queue)) + drive = tmp; } } - } while ((drive = drive->next) != hwgroup->drive); - return best; -} - -static ide_drive_t *ide_choose_drive(ide_hwgroup_t *hwgroup) -{ - ide_drive_t *drive = choose_drive(hwgroup); - unsigned long sleep = 0; + tmp = tmp->next; + } while (tmp != hwgroup->drive); if (drive) return drive; @@ -1495,7 +1493,7 @@ static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq) /* * will clear IDE_BUSY, if appropriate */ - if ((drive = ide_choose_drive(hwgroup)) == NULL) + if ((drive = choose_drive(hwgroup)) == NULL) break; hwif = drive->channel; @@ -1731,20 +1729,23 @@ static void unexpected_intr(int irq, ide_hwgroup_t *hwgroup) * handle the unexpected interrupt */ do { - if (hwif->irq == irq) { - stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); - if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { - /* Try to not flood the console with msgs */ - static unsigned long last_msgtime, count; - ++count; - if (0 < (signed long)(jiffies - (last_msgtime + HZ))) { - last_msgtime = jiffies; - printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n", - hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count); - } + if (hwif->irq != irq) + continue; + + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + /* Try to not flood the console with msgs */ + static unsigned long last_msgtime; + static int count; + ++count; + if (time_after(jiffies, last_msgtime + HZ)) { + last_msgtime = jiffies; + printk("%s%s: unexpected interrupt, status=0x%02x, count=%d\n", + hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count); } } - } while ((hwif = hwif->next) != hwgroup->hwif); + hwif = hwif->next; + } while (hwif != hwgroup->hwif); } /* @@ -1766,7 +1767,8 @@ void ide_intr(int irq, void *dev_id, struct pt_regs *regs) goto out_lock; if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) { - printk("ide: unexpected interrupt\n"); + printk(KERN_INFO "ide: unexpected interrupt %d %d\n", hwif->unit, irq); + /* * Not expecting an interrupt from this drive. * That means this could be: @@ -2298,7 +2300,10 @@ void ide_unregister(struct ata_channel *channel) channel->maskproc = old_hwif.maskproc; channel->quirkproc = old_hwif.quirkproc; channel->rwproc = old_hwif.rwproc; - channel->ideproc = old_hwif.ideproc; + channel->ata_read = old_hwif.ata_read; + channel->ata_write = old_hwif.ata_write; + channel->atapi_read = old_hwif.atapi_read; + channel->atapi_write = old_hwif.atapi_write; channel->dmaproc = old_hwif.dmaproc; channel->busproc = old_hwif.busproc; channel->bus_state = old_hwif.bus_state; @@ -2565,13 +2570,17 @@ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) return 0; } -static int set_io_32bit(ide_drive_t *drive, int arg) +static int set_io_32bit(struct ata_device *drive, int arg) { + if (drive->no_io_32bit) + return -EIO; + drive->io_32bit = arg; #ifdef CONFIG_BLK_DEV_DTC2278 if (drive->channel->chipset == ide_dtc2278) drive->channel->drives[!drive->select.b.unit].io_32bit = arg; -#endif /* CONFIG_BLK_DEV_DTC2278 */ +#endif + return 0; } @@ -3018,8 +3027,6 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m * "hdx=slow" : insert a huge pause after each access to the data * port. Should be used only as a last resort. * - * "hdx=swapdata" : when the drive is a disk, byte swap all data - * "hdx=bswap" : same as above.......... * "hdxlun=xx" : set the drive last logical unit. * "hdx=flash" : allows for more than one ata_flash disk to be * registered. In most cases, only one device @@ -3127,8 +3134,7 @@ int __init ide_setup (char *s) if (!strncmp(s, "hd", 2) && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", "serialize", "autotune", "noautotune", - "slow", "swapdata", "bswap", "flash", - "remap", "noremap", "scsi", NULL}; + "slow", "flash", "remap", "noremap", "scsi", NULL}; unit = s[2] - 'a'; hw = unit / MAX_DRIVES; unit = unit % MAX_DRIVES; @@ -3178,20 +3184,16 @@ int __init ide_setup (char *s) case -8: /* "slow" */ drive->slow = 1; goto done; - case -9: /* "swapdata" or "bswap" */ - case -10: - drive->bswap = 1; - goto done; - case -11: /* "flash" */ + case -9: /* "flash" */ drive->ata_flash = 1; goto done; - case -12: /* "remap" */ + case -10: /* "remap" */ drive->remap_0_to_1 = 1; goto done; - case -13: /* "noremap" */ + case -11: /* "noremap" */ drive->remap_0_to_1 = 2; goto done; - case -14: /* "scsi" */ + case -12: /* "scsi" */ #if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) drive->scsi = 1; goto done; diff --git a/drivers/ide/it8172.c b/drivers/ide/it8172.c index 6e6a86439fa02ad8e6ad16521d652a4b9f2bc8e9..86633459aeb1f04c234294fed9629b92bb32073e 100644 --- a/drivers/ide/it8172.c +++ b/drivers/ide/it8172.c @@ -50,7 +50,7 @@ static void it8172_tune_drive (ide_drive_t *drive, byte pio); #if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_IT8172_TUNING) static byte it8172_dma_2_pio (byte xfer_rate); static int it8172_tune_chipset (ide_drive_t *drive, byte speed); -static int it8172_config_drive_for_dma (ide_drive_t *drive); +static int it8172_config_chipset_for_dma (ide_drive_t *drive); static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive); #endif void __init ide_init_it8172(struct ata_channel *channel); @@ -59,16 +59,14 @@ void __init ide_init_it8172(struct ata_channel *channel); static void it8172_tune_drive (ide_drive_t *drive, byte pio) { unsigned long flags; - u16 master_data; - u32 slave_data; + u16 drive_enables; + u32 drive_timing; int is_slave = (&drive->channel->drives[1] == drive); - int master_port = 0x40; - int slave_port = 0x44; - - if (pio == 255) - pio = ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0; - else - pio = min_t(byte, pio, 4); + + if (pio == 255) + pio = ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0; + else + pio = min_t(byte, pio, 4); pci_read_config_word(drive->channel->pci_dev, master_port, &master_data); pci_read_config_dword(drive->channel->pci_dev, slave_port, &slave_data); @@ -77,19 +75,27 @@ static void it8172_tune_drive (ide_drive_t *drive, byte pio) * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44 * are being left at the default values of 8 PCI clocks (242 nsec * for a 33 MHz clock). These can be safely shortened at higher - * PIO modes. + * PIO modes. The DIOR/DIOW pulse width and recovery times only + * apply to PIO modes, not to the DMA modes. */ + /* + * Enable port 0x44. The IT8172G spec is confused; it calls + * this register the "Slave IDE Timing Register", but in fact, + * it controls timing for both master and slave drives. + */ + drive_enables |= 0x4000; + if (is_slave) { - master_data |= 0x4000; + drive_enables &= 0xc006; if (pio > 1) - /* enable PPE and IE */ - master_data |= 0x0060; + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0060; } else { - master_data &= 0xc060; + drive_enables &= 0xc060; if (pio > 1) - /* enable PPE and IE */ - master_data |= 0x0006; + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0006; } save_flags(flags); @@ -163,6 +169,7 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed) case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; case XFER_MW_DMA_2: case XFER_MW_DMA_1: + case XFER_MW_DMA_0: case XFER_SW_DMA_2: break; default: return -1; } @@ -185,7 +192,7 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed) return err; } -static int it8172_config_drive_for_dma(ide_drive_t *drive) +static int it8172_config_chipset_for_dma(ide_drive_t *drive) { struct hd_driveid *id = drive->id; byte speed; @@ -205,7 +212,7 @@ static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive) { switch (func) { case ide_dma_check: - return ide_dmaproc((ide_dma_action_t)it8172_config_drive_for_dma(drive), + return ide_dmaproc((ide_dma_action_t)it8172_config_chipset_for_dma(drive), drive); default : break; diff --git a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c index 127e15642b3823101dae948addc42bae9f4c4b8c..716517eac8f5141c5a9e8fb4da8425a814644faa 100644 --- a/drivers/ide/pdc202xx.c +++ b/drivers/ide/pdc202xx.c @@ -1270,9 +1270,10 @@ void __init ide_init_pdc202xx(struct ata_channel *hwif) #ifdef CONFIG_PDC202XX_32_UNMASK hwif->drives[0].io_32bit = 1; hwif->drives[1].io_32bit = 1; + hwif->drives[0].unmask = 1; hwif->drives[1].unmask = 1; -#endif /* CONFIG_PDC202XX_32_UNMASK */ +#endif #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->dma_base) { @@ -1285,9 +1286,9 @@ void __init ide_init_pdc202xx(struct ata_channel *hwif) hwif->drives[1].autotune = 1; hwif->autodma = 0; } -#else /* !CONFIG_BLK_DEV_IDEDMA */ +#else hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; hwif->autodma = 0; -#endif /* CONFIG_BLK_DEV_IDEDMA */ +#endif } diff --git a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c index f5763139d3c35648066af9f8ea56eef252e44fe3..ef041e13a752f4bf8e7e474f9d8d5f5f6992eded 100644 --- a/drivers/ide/pdc4030.c +++ b/drivers/ide/pdc4030.c @@ -1,7 +1,7 @@ /* -*- linux-c -*- - * linux/drivers/ide/pdc4030.c Version 0.90 May 27, 1999 + * linux/drivers/ide/pdc4030.c Version 0.92 Jan 15, 2002 * - * Copyright (C) 1995-1999 Linus Torvalds & authors (see below) + * Copyright (C) 1995-2002 Linus Torvalds & authors (see below) */ /* @@ -37,6 +37,8 @@ * Autodetection code added. * * Version 0.90 Transition to BETA code. No lost/unexpected interrupts + * Version 0.91 Bring in line with new bio code in 2.5.1 + * Version 0.92 Update for IDE driver taskfile changes */ /* @@ -72,8 +74,8 @@ * effects. */ -#define DEBUG_READ -#define DEBUG_WRITE +#undef DEBUG_READ +#undef DEBUG_WRITE #include <linux/types.h> #include <linux/kernel.h> @@ -91,6 +93,10 @@ #include "pdc4030.h" +#if SUPPORT_VLB_SYNC != 1 +#error This driver will not work unless SUPPORT_VLB_SYNC is 1 +#endif + /* * promise_selectproc() is invoked by ide.c * in preparation for access to the specified drive. @@ -183,7 +189,7 @@ int __init setup_pdc4030(struct ata_channel *hwif) "%s: Failed Promise read config!\n",hwif->name); return 0; } - ata_input_data(drive, &ident, SECTOR_WORDS); + ata_read(drive, &ident, SECTOR_WORDS); if (ident.id[1] != 'P' || ident.id[0] != 'T') { return 0; } @@ -229,12 +235,12 @@ int __init setup_pdc4030(struct ata_channel *hwif) hwif->selectproc = hwif2->selectproc = &promise_selectproc; hwif->serialized = hwif2->serialized = 1; -/* Shift the remaining interfaces down by one */ +/* Shift the remaining interfaces up by one */ for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { struct ata_channel *h = &ide_hwifs[i]; #ifdef DEBUG - printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i); + printk(KERN_DEBUG "pdc4030: Shifting i/f %d values to i/f %d\n",i-1,i); #endif ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL); memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports)); @@ -332,7 +338,7 @@ static ide_startstop_t promise_read_intr (ide_drive_t *drive) nsect = sectors_avail; sectors_avail -= nsect; to = bio_kmap_irq(rq->bio, &flags) + ide_rq_offset(rq); - ata_input_data(drive, to, nsect * SECTOR_WORDS); + ata_read(drive, to, nsect * SECTOR_WORDS); #ifdef DEBUG_READ printk(KERN_DEBUG "%s: promise_read: sectors(%ld-%ld), " "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector, @@ -460,7 +466,7 @@ int promise_multwrite (ide_drive_t *drive, unsigned int mcount) * Ok, we're all setup for the interrupt * re-entering us on the last transfer. */ - taskfile_output_data(drive, buffer, nsect<<7); + ata_write(drive, buffer, nsect << 7); bio_kunmap_irq(buffer, &flags); } while (mcount); @@ -652,8 +658,8 @@ ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigne are distinguished by writing the drive number (0-3) to the Feature register. FIXME: Is promise_selectproc now redundant?? - */ - taskfile.feature = (drive->channel->unit << 1) + drive->select.b.unit; + */ + taskfile.feature = (drive->channel->unit << 1) + drive->select.b.unit; taskfile.sector_count = rq->nr_sectors; taskfile.sector_number = block; taskfile.low_cylinder = (block>>=8); diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile index 9237b30b450ada7506a179323f5211a6922fc5b6..b80c81a2d6d81519e6ace375e28f1c8a31d88ac9 100644 --- a/drivers/ieee1394/Makefile +++ b/drivers/ieee1394/Makefile @@ -6,7 +6,6 @@ O_TARGET := ieee1394drv.o export-objs := ieee1394_core.o ohci1394.o cmp.o -list-multi := ieee1394.o ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ highlevel.o csr.o nodemgr.o @@ -22,6 +21,3 @@ obj-$(CONFIG_IEEE1394_AMDTP) += amdtp.o obj-$(CONFIG_IEEE1394_CMP) += cmp.o include $(TOPDIR)/Rules.make - -ieee1394.o: $(ieee1394-objs) - $(LD) -r -o $@ $(ieee1394-objs) diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 66541f5223183bce98aaf817f79c8db4d4de0f71..61e60fa447357619ae4b1150ea17d5ca2156820e 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -5,7 +5,6 @@ O_TARGET := mddev.o export-objs := md.o xor.o -list-multi := lvm-mod.o lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o # Note: link order is important. All raid personalities @@ -22,6 +21,3 @@ obj-$(CONFIG_BLK_DEV_MD) += md.o obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o include $(TOPDIR)/Rules.make - -lvm-mod.o: $(lvm-mod-objs) - $(LD) -r -o $@ $(lvm-mod-objs) diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index ed1b138de796426d7bd0d4462b9fb555f6435c4b..f691d13ac565693b4d4c9341fb20e1d2a6394733 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -1,13 +1,6 @@ # # Makefile for the kernel character device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# # Object file lists. @@ -23,8 +16,6 @@ O_TARGET := radio.o export-objs := miropcm20-rds-core.o -list-multi := miropcm20.o - miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o @@ -44,6 +35,3 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o include $(TOPDIR)/Rules.make - -miropcm20.o: $(miropcm20-objs) - $(LD) -r -o $@ $(miropcm20-objs) diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 75d5bc7183c5f67085d613efa48ca3f26ee62e05..50c24aa1d0ac3ec1c11dfbf6556d44b347358855 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -1,13 +1,6 @@ # # Makefile for the kernel character device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# # Object file lists. @@ -16,10 +9,6 @@ obj-m := obj-n := obj- := -SUB_DIRS := -MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) - O_TARGET := video.o # All of the (potential) objects that export symbols. @@ -27,7 +16,6 @@ O_TARGET := video.o export-objs := i2c-old.o videodev.o bttv-if.o cpia.o video-buf.o -list-multi := bttv.o zoran.o bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ bttv-risc.o bttv-vbi.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o @@ -59,29 +47,4 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o -# Extract lists of the multi-part drivers. -# The 'int-*' lists are the intermediate files used to build the multi's. - -multi-y := $(filter $(list-multi), $(obj-y)) -multi-m := $(filter $(list-multi), $(obj-m)) -int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) -int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) - -# Files that are both resident and modular: remove from modular. - -obj-m := $(filter-out $(obj-y), $(obj-m)) -int-m := $(filter-out $(int-y), $(int-m)) - -# Take multi-part drivers out of obj-y and put components in. - -obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) - include $(TOPDIR)/Rules.make - -fastdep: - -zoran.o: zr36120.o zr36120_i2c.o zr36120_mem.o - $(LD) $(LD_RFLAG) -r -o $@ zr36120.o zr36120_i2c.o zr36120_mem.o - -bttv.o: $(bttv-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(bttv-objs) diff --git a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile index a1ddc8e105dffd5b72ec560d8c51e5285ccdbc7a..601a0cc2583f98f3fbaa72e6f1b0d92de2f28b1d 100644 --- a/drivers/message/fusion/Makefile +++ b/drivers/message/fusion/Makefile @@ -1,14 +1,7 @@ # # Makefile for the LSI Logic Fusion MPT (Message Passing Technology) drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now inherited from the -# parent makefile. -# -# Note 3! If you want to turn on various debug defines for an extended period of +# Note! If you want to turn on various debug defines for an extended period of # time but don't want them lingering around in the Makefile when you pass it on # to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer @@ -54,19 +47,11 @@ O_TARGET := fusion.o export-objs := mptbase.o mptscsih.o mptlan.o mptctl.o isense.o -# ? what's list-multi for? -#list-multi := fusion.o mptscsih.o - obj-$(CONFIG_FUSION) += mptbase.o mptscsih.o obj-$(CONFIG_FUSION_ISENSE) += isense.o obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) - include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index b20666e650eb87f6b6b8bb95dbdc1ec11ce3d80b..fb2a81aafdddc31eb0748691b6236738b3936d05 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -1,12 +1,6 @@ # # Makefile for the memory technology device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. # # $Id: Makefile,v 1.63 2001/06/13 09:43:07 dwmw2 Exp $ @@ -20,7 +14,6 @@ obj- := O_TARGET := mtdlink.o export-objs := mtdcore.o mtdpart.o redboot.o bootldr.o afs.o -list-multi := nftl.o mod-subdirs := subdir-y := chips maps devices nand @@ -59,7 +52,3 @@ obj-$(CONFIG_NFTL) += nftl.o nftl-objs := nftlcore.o nftlmount.o include $(TOPDIR)/Rules.make - -nftl.o: $(nftl-objs) - $(LD) -r -o $@ $(nftl-objs) - diff --git a/drivers/parport/Makefile b/drivers/parport/Makefile index ed1f6d1a053c93cafc6ef31e26583a2e5307a8d2..33f94ed7b63fb2809124b5257d3385c185ef7011 100644 --- a/drivers/parport/Makefile +++ b/drivers/parport/Makefile @@ -13,7 +13,6 @@ O_TARGET := driver.o export-objs := init.o parport_pc.o -list-multi := parport.o parport-objs := share.o ieee1284.o ieee1284_ops.o init.o procfs.o ifeq ($(CONFIG_PARPORT_1284),y) @@ -23,7 +22,7 @@ endif obj-$(CONFIG_PARPORT) += parport.o obj-$(CONFIG_PARPORT_PC) += parport_pc.o obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o -obj-$(CONFIG_PARPORT_PC_PCMCIA)+= parport_cs.o +obj-$(CONFIG_PARPORT_PC_PCMCIA) += parport_cs.o obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o @@ -31,6 +30,3 @@ obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o include $(TOPDIR)/Rules.make - -parport.o: $(parport-objs) - $(LD) -r -o $@ $(parport-objs) diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids index 4d8e32e5b1c42ccb52e26976fb8792db981d1d84..8779c56a86f034a9466c3bda3634bcaf654eb7a9 100644 --- a/drivers/pci/pci.ids +++ b/drivers/pci/pci.ids @@ -931,6 +931,9 @@ 121a NetServer SMIC Controller 121b NetServer Legacy COM Port Decoder 121c NetServer PCI COM Port Decoder + 1229 zx1 System Bus Adapter + 122a zx1 I/O Controller + 122e zx1 Local Bus Adapter 2910 E2910A 2925 E2925A 103e Solliday Engineering diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index dda91ec562e0370a455ed19f06658d8f6103cd04..a7b90586c5def3e911a8f57e99b74d9cc06ed5ac 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -1,19 +1,11 @@ # # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. O_TARGET := pcmcia.o export-objs := ds.o cs.o yenta.o pci_socket.o -list-multi := pcmcia_core.o yenta_socket.o - yenta_socket-objs := pci_socket.o yenta.o pcmcia_core-objs := cistpl.o rsrc_mgr.o bulkmem.o cs.o @@ -83,12 +75,3 @@ sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o sa1111_generic.o sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o include $(TOPDIR)/Rules.make - -pcmcia_core.o: $(pcmcia_core-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs) - -sa1100_cs.o: $(sa1100_cs-objs-y) - $(LD) -r -o $@ $(sort $(sa1100_cs-objs-y)) - -yenta_socket.o: $(yenta_socket-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs) diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile index f0ab45ce35b3c079c55c015edc93c08a98016633..84b49924f679294316a11b93f9922fa888280fcb 100644 --- a/drivers/pnp/Makefile +++ b/drivers/pnp/Makefile @@ -1,17 +1,10 @@ # # Makefile for the kernel Plug-and-Play device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. O_TARGET := pnp.o export-objs := isapnp.o pnpbios_core.o -list-multi := isa-pnp.o pnpbios.o isa-pnp-proc-$(CONFIG_PROC_FS) = isapnp_proc.o pnpbios-proc-$(CONFIG_PROC_FS) = pnpbios_proc.o @@ -23,9 +16,3 @@ obj-$(CONFIG_ISAPNP) += isa-pnp.o obj-$(CONFIG_PNPBIOS) += pnpbios.o include $(TOPDIR)/Rules.make - -isa-pnp.o: $(isa-pnp-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) - -pnpbios.o: $(pnpbios-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs) diff --git a/drivers/pnp/pnpbios_core.c b/drivers/pnp/pnpbios_core.c index 5c39209bb52ba2c7eaebe05026b48e77b10c6bd7..e4405e647f3c493c1e4e07b3c8e0864864aee5c1 100644 --- a/drivers/pnp/pnpbios_core.c +++ b/drivers/pnp/pnpbios_core.c @@ -1,10 +1,19 @@ /* - * PnP BIOS services + * pnpbios -- PnP BIOS driver + * + * This driver provides access to Plug-'n'-Play services provided by + * the PnP BIOS firmware, described in the following documents: + * Plug and Play BIOS Specification, Version 1.0A, 5 May 1994 + * Plug and Play BIOS Clarification Paper, 6 October 1994 + * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corp. * * Originally (C) 1998 Christian Schmidt <schmidt@digadd.de> - * Modifications (c) 1998 Tom Lees <tom@lpsg.demon.co.uk> + * Modifications (C) 1998 Tom Lees <tom@lpsg.demon.co.uk> * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net> - * Modifications (c) 2001,2002 by Thomas Hood <jdthood@mail.com> + * Further modifications (C) 2001, 2002 by: + * Alan Cox <alan@redhat.com> + * Thomas Hood <jdthood@mail.com> + * Brian Gerst <bgerst@didntduck.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 @@ -19,12 +28,6 @@ * 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 - * - * References: - * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corporation - * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 - * Plug and Play BIOS Clarification Paper, October 6, 1994 - * */ #include <linux/types.h> @@ -141,7 +144,9 @@ u32 pnp_bios_is_utter_crap = 0; static spinlock_t pnp_bios_lock; static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, - u16 arg4, u16 arg5, u16 arg6, u16 arg7) + u16 arg4, u16 arg5, u16 arg6, u16 arg7, + void *ts1_base, u32 ts1_size, + void *ts2_base, u32 ts2_size) { unsigned long flags; u16 status; @@ -155,7 +160,12 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, /* On some boxes IRQ's during PnP BIOS calls are deadly. */ spin_lock_irqsave(&pnp_bios_lock, flags); - __cli(); + + if (ts1_size) + Q2_SET_SEL(PNP_TS1, ts1_base, ts1_size); + if (ts2_size) + Q2_SET_SEL(PNP_TS2, ts2_base, ts2_size); + __asm__ __volatile__( "pushl %%ebp\n\t" "pushl %%edi\n\t" @@ -258,8 +268,8 @@ static int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info)); - status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0); + status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0, + data, sizeof(struct pnp_dev_node_info), 0, 0); data->no_nodes &= 0xff; return status; } @@ -293,9 +303,8 @@ static int __pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node return PNP_FUNCTION_NOT_SUPPORTED; if ( !boot & pnpbios_dont_use_current_config ) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char)); - Q2_SET_SEL(PNP_TS2, data, 64 * 1024); - status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0); + status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0, + nodenum, sizeof(char), data, 65536); return status; } @@ -322,8 +331,8 @@ static int __pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node * return PNP_FUNCTION_NOT_SUPPORTED; if ( !boot & pnpbios_dont_use_current_config ) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536); - status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0); + status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0, + data, 65536, 0, 0); return status; } @@ -354,8 +363,8 @@ static int pnp_bios_get_event(u16 *event) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, event, sizeof(u16)); - status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0); + status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0, + event, sizeof(u16), 0, 0); return status; } #endif @@ -369,7 +378,7 @@ static int pnp_bios_send_message(u16 message) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0); + status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0, 0, 0, 0, 0); return status; } #endif @@ -383,8 +392,8 @@ static int pnp_bios_dock_station_info(struct pnp_docking_station_info *data) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_docking_station_info)); - status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + data, sizeof(struct pnp_docking_station_info), 0, 0); return status; } #endif @@ -399,8 +408,8 @@ static int pnp_bios_set_stat_res(char *info) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, info, *((u16 *) info)); - status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + info, *((u16 *) info), 0, 0); return status; } #endif @@ -414,8 +423,8 @@ static int __pnp_bios_get_stat_res(char *info) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, info, 64 * 1024); - status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + info, 65536, 0, 0); return status; } @@ -437,9 +446,8 @@ static int pnp_bios_apm_id_table(char *table, u16 *size) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, table, *size); - Q2_SET_SEL(PNP_TS2, size, sizeof(u16)); - status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0); + status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0, + table, *size, size, sizeof(u16)); return status; } #endif @@ -452,8 +460,8 @@ static int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) u16 status; if (!pnp_bios_present()) return PNP_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_isa_config_struc)); - status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0); + status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + data, sizeof(struct pnp_isa_config_struc), 0, 0); return status; } @@ -474,8 +482,8 @@ static int __pnp_bios_escd_info(struct escd_info_struc *data) u16 status; if (!pnp_bios_present()) return ESCD_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, sizeof(struct escd_info_struc)); - status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS); + status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS, + data, sizeof(struct escd_info_struc), 0, 0); return status; } @@ -497,10 +505,8 @@ static int __pnp_bios_read_escd(char *data, u32 nvram_base) u16 status; if (!pnp_bios_present()) return ESCD_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, 64 * 1024); - set_base(gdt[PNP_TS2 >> 3], nvram_base); - set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); - status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0, + data, 65536, (void *)nvram_base, 65536); return status; } @@ -522,10 +528,8 @@ static int pnp_bios_write_escd(char *data, u32 nvram_base) u16 status; if (!pnp_bios_present()) return ESCD_FUNCTION_NOT_SUPPORTED; - Q2_SET_SEL(PNP_TS1, data, 64 * 1024); - set_base(gdt[PNP_TS2 >> 3], nvram_base); - set_limit(gdt[PNP_TS2 >> 3], 64 * 1024); - status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0); + status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0, + data, 65536, nvram_base, 65536); return status; } #endif diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile index bd352622f45876c322fb2cc74110d9ccc165c340..2f051aa0b12ebf4207c50b77a1ac51f16115aa67 100644 --- a/drivers/s390/block/Makefile +++ b/drivers/s390/block/Makefile @@ -4,7 +4,6 @@ O_TARGET := s390-block.o -list-multi := dasd_mod.o dasd_eckd_mod.o dasd_fba_mod.o dasd_diag_mod.o export-objs := dasd.o dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o @@ -19,16 +18,3 @@ obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o include $(TOPDIR)/Rules.make - -dasd_mod.o: $(dasd_mod-objs) - $(LD) -r -o $@ $(dasd_mod-objs) - -dasd_eckd_mod.o: $(dasd_eckd_mod-objs) - $(LD) -r -o $@ $(dasd_eckd_mod-objs) - -dasd_fba_mod.o: $(dasd_fba_mod-objs) - $(LD) -r -o $@ $(dasd_fba_mod-objs) - -dasd_diag_mod.o: $(dasd_diag_mod-objs) - $(LD) -r -o $@ $(dasd_diag_mod-objs) - diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 7fc15b395eb8bf631b4cece6c691234b2652df94..ecdd02990d5e7118a56a7b64b6352a52bcdcd1fb 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -4,7 +4,6 @@ O_TARGET := s390-char.o -list-multi := tub3270.o tape390.o export-objs := hwc_rw.o tub3270-objs := tuball.o tubfs.o tubtty.o \ @@ -25,10 +24,3 @@ obj-$(CONFIG_TN3270) += tub3270.o obj-$(CONFIG_S390_TAPE) += tape390.o include $(TOPDIR)/Rules.make - -tub3270.o: $(tub3270-objs) - $(LD) -r -o $@ $(tub3270-objs) - -tape390.o: $(tape390-objs) - $(LD) -r -o $@ $(tape390-objs) - diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 5300da79f37183f049e4a4b4d5b010445b7e12a1..7ec7e86396b778c46a58700fae8335f767126f1a 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile @@ -4,7 +4,6 @@ O_TARGET := s390-net.o -list-multi := ctc.o export-objs := iucv.o fsm.o ctc-objs := ctcmain.o ctctty.o @@ -14,7 +13,3 @@ obj-$(CONFIG_CTC) += ctc.o obj-$(CONFIG_IUCV) += netiucv.o include $(TOPDIR)/Rules.make - -ctc.o: $(ctc-objs) - $(LD) -r -o $@ $(ctc-objs) - diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 12ee27686e6cc853b37a461d8d1fbdece00cd383..e7fa4a8c20f68dfe52454e5e8d7c3d91330af2c2 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -12,7 +12,6 @@ O_TARGET := sunchar.o export-objs := su.o bbc_i2c.o obj-y := sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o -list-multi := vfc.o bbc.o vfc-objs := vfc_dev.o vfc_i2c.o bbc-objs := bbc_i2c.o bbc_envctrl.o @@ -36,9 +35,3 @@ obj-$(CONFIG_BBC_I2C) += bbc.o include $(TOPDIR)/Rules.make sunkbdmap.o: sunkeymap.c - -vfc.o: $(vfc-objs) - $(LD) -r -o $@ $(vfc-objs) - -bbc.o: $(bbc-objs) - $(LD) -r -o $@ $(bbc-objs) diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2d5af93c55816ee8bb3ad091df3f4866d950c93f..b909c50a85fd8f2a9fb99d5a53638fa97f467b4a 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -132,7 +132,6 @@ obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o obj-$(CONFIG_CHR_DEV_SG) += sg.o -list-multi := scsi_mod.o sd_mod.o sr_mod.o initio.o a100u2w.o cpqfc.o scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o scsicam.o \ scsi_proc.o scsi_error.o scsi_queue.o scsi_lib.o \ scsi_merge.o scsi_scan.o scsi_syms.o @@ -145,25 +144,6 @@ cpqfc-objs := cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o \ include $(TOPDIR)/Rules.make - -scsi_mod.o: $(scsi_mod-objs) - $(LD) -r -o $@ $(scsi_mod-objs) - -sd_mod.o: $(sd_mod-objs) - $(LD) -r -o $@ $(sd_mod-objs) - -sr_mod.o: $(sr_mod-objs) - $(LD) -r -o $@ $(sr_mod-objs) - -initio.o: $(initio-objs) - $(LD) -r -o $@ $(initio-objs) - -a100u2w.o: $(a100u2w-objs) - $(LD) -r -o $@ $(a100u2w-objs) - -cpqfc.o: $(cpqfc-objs) - $(LD) -r -o $@ $(cpqfc-objs) - 53c8xx_d.h: 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake8.c $(CPP) $(CPPFLAGS) -traditional -DCHIP=810 fake8.c | grep -v '^#' | $(PERL) script_asm.pl diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index b4568351d1e36fb8062efded49c6af6f66a1a467..107fae08cb44c19b164c4d793463e7f852dd1675 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -1,4 +1,4 @@ -#define ASC_VERSION "3.3GG" /* AdvanSys Driver Version */ +#define ASC_VERSION "3.3GJ" /* AdvanSys Driver Version */ /* * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters @@ -670,7 +670,7 @@ 1. Return an error from narrow boards if passed a 16 byte CDB. The wide board can already handle 16 byte CDBs. - 3.3GG (01/02/02): + 3.3GJ (4/15/02): 1. hacks for lk 2.5 series (D. Gilbert) I. Known Problems/Fix List (XXX) @@ -752,7 +752,6 @@ */ -#error Please convert me to Documentation/DMA-mapping.txt /* * --- Linux Version @@ -3614,23 +3613,6 @@ typedef struct { #define ASC_MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif /* CONFIG_PROC_FS */ -/* - * XXX - Release and acquire the io_request_lock. These macros are needed - * because the 2.4 kernel SCSI mid-level driver holds the 'io_request_lock' - * on entry to SCSI low-level drivers. - * - * These definitions and all code that uses code should be removed when the - * SCSI mid-level driver no longer holds the 'io_request_lock' on entry to - * SCSI low-level driver detect, queuecommand, and reset entrypoints. - * - * The interrupt flags values doesn't matter in the macros because the - * SCSI mid-level will save and restore the flags values before and after - * calling advansys_detect, advansys_queuecommand, and advansys_reset where - * these macros are used. We do want interrupts enabled after the lock is - * released so an explicit sti() is done. The driver only needs interrupts - * disabled when it acquires the per board lock. - */ - /* Asc Library return codes */ #define ASC_TRUE 1 #define ASC_FALSE 0 @@ -4822,7 +4804,7 @@ advansys_detect(Scsi_Host_Template *tpnt) boardp->id = asc_board_count - 1; /* Initialize spinlock. */ - boardp->lock = SPIN_LOCK_UNLOCKED; /* replaced by host_lock dpg */ + boardp->lock = SPIN_LOCK_UNLOCKED; /* * Handle both narrow and wide boards. @@ -5872,7 +5854,7 @@ advansys_queuecommand(Scsi_Cmnd *scp, void (*done)(Scsi_Cmnd *)) /* host_lock taken by mid-level prior to call but need to protect */ /* against own ISR */ - spin_lock_irqsave(boardp->lock, flags); + spin_lock_irqsave(&boardp->lock, flags); /* * Block new commands while handling a reset or abort request. @@ -6751,8 +6733,7 @@ asc_build_req(asc_board_t *boardp, Scsi_Cmnd *scp) slp = (struct scatterlist *) scp->request_buffer; for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) { asc_sg_head.sg_list[sgcnt].addr = - cpu_to_le32(virt_to_bus(slp->address ? - (unsigned char *)slp->address : + cpu_to_le32(virt_to_bus( (unsigned char *)page_address(slp->page) + slp->offset)); asc_sg_head.sg_list[sgcnt].bytes = cpu_to_le32(slp->length); ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512)); @@ -7011,9 +6992,8 @@ adv_get_sglist(asc_board_t *boardp, adv_req_t *reqp, Scsi_Cmnd *scp) for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) { sg_block->sg_list[i].sg_addr = - cpu_to_le32(virt_to_bus(slp->address ? - (unsigned char *)slp->address : - (unsigned char *)page_address(slp->page) + slp->offset)); + cpu_to_le32(virt_to_bus( + (unsigned char *)page_address(slp->page) + slp->offset)); sg_block->sg_list[i].sg_count = cpu_to_le32(slp->length); ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512)); @@ -9415,8 +9395,8 @@ asc_prt_scsi_host(struct Scsi_Host *s) printk("Scsi_Host at addr 0x%lx\n", (ulong) s); printk( -" next 0x%lx, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n", - (ulong) s->next, s->extra_bytes, s->host_busy, s->host_no, +" next 0x%lx, host_busy %u, host_no %d, last_reset %d,\n", + (ulong) s->next, s->host_busy, s->host_no, (unsigned) s->last_reset); #if ASC_LINUX_KERNEL24 diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile index 512ee5cbd043bf75068a9b7b18a18a867349e4f1..d74035d8c4d4082decb42b932b84e751107ae073 100644 --- a/drivers/scsi/aic7xxx/Makefile +++ b/drivers/scsi/aic7xxx/Makefile @@ -1,13 +1,9 @@ # -# drivers/scsi/aic7xxx/Makefile -# # Makefile for the Linux aic7xxx SCSI driver. # O_TARGET := aic7xxx_drv.o -list-multi := aic7xxx_mod.o - obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx_mod.o #EXTRA_CFLAGS += -g @@ -31,9 +27,6 @@ MOD_TARGET = aic7xxx.o include $(TOPDIR)/Rules.make -aic7xxx_mod.o: aic7xxx_seq.h aic7xxx_reg.h $(AIC7XXX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ $(AIC7XXX_OBJS) - ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y) aic7xxx_seq.h aic7xxx_reg.h: aic7xxx.seq aic7xxx.reg aicasm/aicasm aicasm/aicasm -I. -r aic7xxx_reg.h -o aic7xxx_seq.h aic7xxx.seq diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index c076125c87e9500fc44368f219a68fab2ce83510..46a2b1ca3f1a63f0da4c570f56f200cb631fa1dc 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -140,7 +140,7 @@ static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigne } count = min(pc->sg->length - pc->b_count, bcount); buf = page_address(pc->sg->page) + pc->sg->offset; - atapi_input_bytes (drive, buf + pc->b_count, count); + atapi_read(drive, buf + pc->b_count, count); bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { pc->sg++; @@ -162,7 +162,7 @@ static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsign } count = min(pc->sg->length - pc->b_count, bcount); buf = page_address(pc->sg->page) + pc->sg->offset; - atapi_output_bytes (drive, buf + pc->b_count, count); + atapi_write(drive, buf + pc->b_count, count); bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { pc->sg++; @@ -363,7 +363,7 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) if (pc->sg) idescsi_input_buffers(drive, pc, temp); else - atapi_input_bytes(drive, pc->current_position, temp); + atapi_read(drive, pc->current_position, temp); printk(KERN_ERR "ide-scsi: transferred %d of %d bytes\n", temp, bcount); } pc->actually_transferred += temp; @@ -382,13 +382,13 @@ static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) if (pc->sg) idescsi_input_buffers (drive, pc, bcount); else - atapi_input_bytes (drive,pc->current_position,bcount); + atapi_read(drive,pc->current_position,bcount); } else { set_bit(PC_WRITING, &pc->flags); if (pc->sg) idescsi_output_buffers (drive, pc, bcount); else - atapi_output_bytes (drive,pc->current_position,bcount); + atapi_write(drive,pc->current_position,bcount); } pc->actually_transferred+=bcount; /* Update the current position */ pc->current_position+=bcount; @@ -414,7 +414,7 @@ static ide_startstop_t idescsi_transfer_pc (ide_drive_t *drive) return ide_stopped; } ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); /* Set the interrupt routine */ - atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */ + atapi_write(drive, scsi->pc->c, 12); /* Send the actual packet */ return ide_started; } diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 0c53c0d6b9da3e2a7e3b8790018e8e600f45dce4..000a6fcb5a790518e6c785f19317a6e4ad33be53 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -140,7 +140,6 @@ * ioctlsize - Initial size of the IOCTL buffer */ -#error Please convert me to Documentation/DMA-mapping.txt #include <asm/io.h> #include <asm/byteorder.h> @@ -848,6 +847,13 @@ ips_detect(Scsi_Host_Template *SHT) { /* found a controller */ sh = scsi_register(SHT, sizeof(ips_ha_t)); + /* + * Set pci_dev and dma_mask + */ + pci_set_dma_mask(dev[i], (u64) 0xffffffff); + + scsi_set_pci_device(sh, dev[i]); + if (sh == NULL) { printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n", ips_name, ips_next_controller); @@ -1178,6 +1184,8 @@ ips_detect(Scsi_Host_Template *SHT) { sh = ips_sh[i]; if (!ha->active) { + printk(KERN_WARNING "(%s%d) controller not active\n", + ips_name, i); scsi_unregister(sh); ips_ha[i] = NULL; ips_sh[i] = NULL; @@ -2833,7 +2841,7 @@ ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { /* FIX stuff that might be wrong */ scb->sg_list = sg_list; - scb->scb_busaddr = VIRT_TO_BUS(scb); + scb->scb_busaddr = virt_to_bus(scb); scb->bus = scb->scsi_cmd->channel; scb->target_id = scb->scsi_cmd->target; scb->lun = scb->scsi_cmd->lun; @@ -2852,13 +2860,13 @@ ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { return (0); if (pt->CmdBSize) { - scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t)); + scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t)); } else { scb->data_busaddr = 0L; } if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) - scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb)); + scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb)); if (pt->CmdBSize) { if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) @@ -2916,7 +2924,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { /* FIX stuff that might be wrong */ scb->sg_list = sg_list; - scb->scb_busaddr = VIRT_TO_BUS(scb); + scb->scb_busaddr = virt_to_bus(scb); scb->bus = scb->scsi_cmd->channel; scb->target_id = scb->scsi_cmd->target; scb->lun = scb->scsi_cmd->lun; @@ -2959,7 +2967,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { } - scb->data_busaddr = VIRT_TO_BUS(ha->ioctl_data); + scb->data_busaddr = virt_to_bus(ha->ioctl_data); /* Attempt to copy in the data */ user_area = *((char **) &scb->scsi_cmd->cmnd[4]); @@ -2978,7 +2986,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) { } if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) - scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb)); + scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb)); if (pt->CmdBSize) { if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB) @@ -3393,7 +3401,7 @@ ips_get_bios_version(ips_ha_t *ha, int intr) { scb->cmd.flashfw.type = 1; scb->cmd.flashfw.direction = 0; scb->cmd.flashfw.count = cpu_to_le32(0x800); - scb->cmd.flashfw.buffer_addr = cpu_to_le32(VIRT_TO_BUS(buffer)); + scb->cmd.flashfw.buffer_addr = cpu_to_le32(virt_to_bus(buffer)); scb->cmd.flashfw.total_packets = 1; scb->cmd.flashfw.packet_num = 0; @@ -3569,7 +3577,7 @@ ips_next(ips_ha_t *ha, int intr) { Scsi_Cmnd *p; Scsi_Cmnd *q; ips_copp_wait_item_t *item; - int ret; + int ret, sg_entries = 0; int intr_status; unsigned long cpu_flags; unsigned long cpu_flags2; @@ -3771,6 +3779,7 @@ ips_next(ips_ha_t *ha, int intr) { int i; sg = SC->request_buffer; + sg_entries = pci_map_sg(ha->pcidev, sg, SC->use_sg, scsi_to_pci_dma_dir(SC->sc_data_direction)); if (SC->use_sg == 1) { if (sg[0].length > ha->max_xfer) { @@ -3780,12 +3789,12 @@ ips_next(ips_ha_t *ha, int intr) { scb->data_len = sg[0].length; scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(sg[0].address); + scb->data_busaddr = sg_dma_address(&sg[0]); scb->sg_len = 0; } else { /* Check for the first Element being bigger than MAX_XFER */ if (sg[0].length > ha->max_xfer) { - scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(sg[0].address)); + scb->sg_list[0].address = cpu_to_le32(sg_dma_address(&sg[0])); scb->sg_list[0].length = ha->max_xfer; scb->data_len = ha->max_xfer; scb->breakup = 0; @@ -3794,7 +3803,7 @@ ips_next(ips_ha_t *ha, int intr) { } else { for (i = 0; i < SC->use_sg; i++) { - scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address)); + scb->sg_list[i].address = cpu_to_le32(sg_dma_address(&sg[i])); scb->sg_list[i].length = cpu_to_le32(sg[i].length); if (scb->data_len + sg[i].length > ha->max_xfer) { @@ -3809,13 +3818,13 @@ ips_next(ips_ha_t *ha, int intr) { } if (!scb->breakup) - scb->sg_len = SC->use_sg; + scb->sg_len = sg_entries; else scb->sg_len = scb->breakup; } scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); + scb->data_busaddr = virt_to_bus(scb->sg_list); } } else { if (SC->request_bufflen) { @@ -3830,7 +3839,7 @@ ips_next(ips_ha_t *ha, int intr) { } scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(SC->request_buffer); + scb->data_busaddr = virt_to_bus(SC->request_buffer); scb->sg_len = 0; } else { scb->data_busaddr = 0L; @@ -4469,11 +4478,11 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { if (sg[0].length - (bk_save * ha->max_xfer) > ha->max_xfer) { /* Further breakup required */ scb->data_len = ha->max_xfer; - scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer)); + scb->data_busaddr = sg_dma_address(&sg[0] + (bk_save * ha->max_xfer)); scb->breakup = bk_save + 1; } else { scb->data_len = sg[0].length - (bk_save * ha->max_xfer); - scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer)); + scb->data_busaddr = sg_dma_address(&sg[0] + (bk_save * ha->max_xfer)); } scb->dcdb.transfer_length = scb->data_len; @@ -4490,7 +4499,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { /* pointed to by bk_save */ if (scb->sg_break) { scb->sg_len = 1; - scb->sg_list[0].address = VIRT_TO_BUS(sg[bk_save].address+ha->max_xfer*scb->sg_break); + scb->sg_list[0].address = sg_dma_address(&sg[bk_save] + ha->max_xfer*scb->sg_break); if (ha->max_xfer > sg[bk_save].length-ha->max_xfer * scb->sg_break) scb->sg_list[0].length = sg[bk_save].length-ha->max_xfer * scb->sg_break; else @@ -4508,7 +4517,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { } else { /* ( sg_break == 0 ), so this is our first look at a new sg piece */ if (sg[bk_save].length > ha->max_xfer) { - scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(sg[bk_save].address)); + scb->sg_list[0].address = cpu_to_le32(sg_dma_address(&sg[bk_save])); scb->sg_list[0].length = ha->max_xfer; scb->breakup = bk_save; scb->sg_break = 1; @@ -4521,7 +4530,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { scb->sg_break = 0; /* We're only doing full units here */ for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) { - scb->sg_list[i - bk_save].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address)); + scb->sg_list[i - bk_save].address = cpu_to_le32(sg_dma_address(&sg[i])); scb->sg_list[i - bk_save].length = cpu_to_le32(sg[i].length); if (scb->data_len + sg[i].length > ha->max_xfer) { scb->breakup = i; /* sneaky, if not more work, than breakup is 0 */ @@ -4536,7 +4545,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { /* Also, we need to be sure we don't queue work ( breakup != 0 ) if no more sg units for next time */ scb->dcdb.transfer_length = scb->data_len; - scb->data_busaddr = VIRT_TO_BUS(scb->sg_list); + scb->data_busaddr = virt_to_bus(scb->sg_list); } } else { @@ -4544,11 +4553,11 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { if ((scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) > ha->max_xfer) { /* Further breakup required */ scb->data_len = ha->max_xfer; - scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer)); + scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer)); scb->breakup = bk_save + 1; } else { scb->data_len = scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer); - scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer)); + scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer)); } scb->dcdb.transfer_length = scb->data_len; @@ -4589,6 +4598,8 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { break; } /* end case */ + pci_unmap_sg(ha->pcidev, (struct scatterlist *)scb->scsi_cmd->request_buffer, scb->sg_len, + scsi_to_pci_dma_dir(scb->scsi_cmd->sc_data_direction)); return ; } #ifndef NO_IPS_CMDLINE @@ -4851,7 +4862,7 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { } else { scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO; scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb); - scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info)); + scb->cmd.logical_info.buffer_addr = cpu_to_le32(virt_to_bus(&ha->adapt->logical_drive_info)); scb->cmd.logical_info.reserved = 0; scb->cmd.logical_info.reserved2 = 0; ret = IPS_SUCCESS; @@ -4944,14 +4955,14 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { case MODE_SENSE: scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY; scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); - scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq)); + scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->enq)); ret = IPS_SUCCESS; break; case READ_CAPACITY: scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO; scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb); - scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info)); + scb->cmd.logical_info.buffer_addr = cpu_to_le32(virt_to_bus(&ha->adapt->logical_drive_info)); scb->cmd.logical_info.reserved = 0; scb->cmd.logical_info.reserved2 = 0; scb->cmd.logical_info.reserved3 = 0; @@ -5007,7 +5018,7 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) { ha->dcdb_active[scb->bus-1] |= (1 << scb->target_id); scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb); - scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb)); + scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb)); scb->cmd.dcdb.reserved = 0; scb->cmd.dcdb.reserved2 = 0; scb->cmd.dcdb.reserved3 = 0; @@ -5532,16 +5543,16 @@ ips_init_scb(ips_ha_t *ha, ips_scb_t *scb) { /* Initialize dummy command bucket */ ha->dummy->op_code = 0xFF; - ha->dummy->ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy)); + ha->dummy->ccsar = cpu_to_le32(virt_to_bus(ha->dummy)); ha->dummy->command_id = IPS_MAX_CMDS; /* set bus address of scb */ - scb->scb_busaddr = VIRT_TO_BUS(scb); + scb->scb_busaddr = virt_to_bus(scb); scb->sg_list = sg_list; /* Neptune Fix */ scb->cmd.basic_io.cccr = cpu_to_le32((u_int32_t) IPS_BIT_ILE); - scb->cmd.basic_io.ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy)); + scb->cmd.basic_io.ccsar = cpu_to_le32(virt_to_bus(ha->dummy)); } /****************************************************************************/ @@ -6151,7 +6162,7 @@ ips_statinit(ips_ha_t *ha) { ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; ha->adapt->p_status_tail = ha->adapt->status; - phys_status_start = VIRT_TO_BUS(ha->adapt->status); + phys_status_start = virt_to_bus(ha->adapt->status); outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQSR); outl(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE), ha->io_addr + IPS_REG_SQER); outl(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE), ha->io_addr + IPS_REG_SQHR); @@ -6180,7 +6191,7 @@ ips_statinit_memio(ips_ha_t *ha) { ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS; ha->adapt->p_status_tail = ha->adapt->status; - phys_status_start = VIRT_TO_BUS(ha->adapt->status); + phys_status_start = virt_to_bus(ha->adapt->status); writel(cpu_to_le32(phys_status_start), ha->mem_ptr + IPS_REG_SQSR); writel(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE), ha->mem_ptr + IPS_REG_SQER); writel(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE), ha->mem_ptr + IPS_REG_SQHR); @@ -6734,7 +6745,7 @@ ips_read_adapter_status(ips_ha_t *ha, int intr) { scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY; scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.basic_io.sg_count = 0; - scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq)); + scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->enq)); scb->cmd.basic_io.lba = 0; scb->cmd.basic_io.sector_count = 0; scb->cmd.basic_io.log_drv = 0; @@ -6775,7 +6786,7 @@ ips_read_subsystem_parameters(ips_ha_t *ha, int intr) { scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS; scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.basic_io.sg_count = 0; - scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->subsys)); + scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->subsys)); scb->cmd.basic_io.lba = 0; scb->cmd.basic_io.sector_count = 0; scb->cmd.basic_io.log_drv = 0; @@ -6820,7 +6831,7 @@ ips_read_config(ips_ha_t *ha, int intr) { scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF; scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb); - scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->conf)); + scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->conf)); /* send command */ if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) || @@ -6866,7 +6877,7 @@ ips_readwrite_page5(ips_ha_t *ha, int write, int intr) { scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.nvram.page = 5; scb->cmd.nvram.write = write; - scb->cmd.nvram.buffer_addr = cpu_to_le32(VIRT_TO_BUS(ha->nvram)); + scb->cmd.nvram.buffer_addr = cpu_to_le32(virt_to_bus(ha->nvram)); scb->cmd.nvram.reserved = 0; scb->cmd.nvram.reserved2 = 0; diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index b0431ced95686dc1154709c2090a1b937628ae7f..4d569e1b86fc1e22c60019c094a442d015b93433 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -443,7 +443,8 @@ present : 0, \ unchecked_isa_dma : 0, \ use_clustering : ENABLE_CLUSTERING, \ - use_new_eh_code : 1 \ + use_new_eh_code : 1, \ + highmem_io : 1 \ } #else #define IPS { \ diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile index b52924dd507736e810ae7c38fb90f1fb1da21835..e189f1a25b87b1a5e1365c12244626e2afec2281 100644 --- a/drivers/scsi/pcmcia/Makefile +++ b/drivers/scsi/pcmcia/Makefile @@ -1,6 +1,4 @@ # -# drivers/scsi/pcmcia/Makefile -# # Makefile for the Linux PCMCIA SCSI drivers. # @@ -21,18 +19,8 @@ obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o -list-multi := qlogic_cs.o fdomain_cs.o aha152x_cs.o aha152x_cs-objs := aha152x_stub.o aha152x.o fdomain_cs-objs := fdomain_stub.o fdomain.o qlogic_cs-objs := qlogic_stub.o qlogicfas.o include $(TOPDIR)/Rules.make - -aha152x_cs.o: $(aha152x_cs-objs) - $(LD) -r -o $@ $(aha152x_cs-objs) - -fdomain_cs.o: $(fdomain_cs-objs) - $(LD) -r -o $@ $(fdomain_cs-objs) - -qlogic_cs.o: $(qlogic_cs-objs) - $(LD) -r -o $@ $(qlogic_cs-objs) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 48b82933be05a67ea6db80007f5b05d2450307ef..1bc09134ac799c863f42f00fceca5126889823ae 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -14,8 +14,8 @@ * * D. Gilbert (dpg) work for MOD device test [20010421] * dpg, work for devfs large number of disks [20010809] - * dpg, make more generic [20011123] * dpg, forked for lk 2.5 series [20011216, 20020101] + * dpg, use vmalloc() more inquiry+mode_sense [20020302] */ #include <linux/config.h> @@ -46,7 +46,7 @@ #include <linux/version.h> #endif -static char scsi_debug_version_str[] = "Version: 1.57 (20011216)"; +static char scsi_debug_version_str[] = "Version: 1.59 (20020302)"; #ifndef SCSI_CMD_READ_16 #define SCSI_CMD_READ_16 0x88 @@ -60,22 +60,26 @@ static char scsi_debug_version_str[] = "Version: 1.57 (20011216)"; #define DEF_DEV_SIZE_MB 8 #define DEF_FAKE_BLK0 0 +#define DEF_OPTS 0 +#define SCSI_DEBUG_OPT_NOISE 1 +#define SCSI_DEBUG_OPT_MEDIUM_ERR 2 + +#define OPT_MEDIUM_ERR_ADDR 0x1234 + static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS; +static int scsi_debug_opts = DEF_OPTS; #define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1) #define N_HEAD 8 #define N_SECTOR 32 -#define DISK_READONLY(TGT) (0) -#define DISK_REMOVEABLE(TGT) (0) +#define DEV_READONLY(TGT) (0) +#define DEV_REMOVEABLE(TGT) (0) #define DEVICE_TYPE(TGT) (TYPE_DISK); #define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1) static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; #define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024) -#define STORE_ELEM_ORDER 1 -#define STORE_ELEM_SIZE (PAGE_SIZE * (1 << STORE_ELEM_ORDER)) -#define STORE_ELEMENTS ((STORE_SIZE / STORE_ELEM_SIZE) + 1) /* default sector size is 512 bytes, 2**9 bytes */ #define POW2_SECT_SIZE 9 @@ -83,34 +87,24 @@ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; #define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD)) -static int scsi_debug_fake_blk0 = DEF_FAKE_BLK0; - /* Do not attempt to use a timer to simulate a real disk with latency */ /* Only use this in the actual kernel, not in the simulator. */ #define IMMEDIATE -#define SDEBUG_SG_ADDRESS - #define START_PARTITION 4 /* Time to wait before completing a command */ #define DISK_SPEED (HZ/10) /* 100ms */ #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER) #define SECT_SIZE_PER(TGT) SECT_SIZE -#define SECT_PER_ELEM (STORE_ELEM_SIZE / SECT_SIZE) static int starts[] = {N_SECTOR, N_HEAD * N_SECTOR, /* Single cylinder */ N_HEAD * N_SECTOR * 4, 0 /* CAPACITY */, 0}; -static int npart = 0; - -typedef struct scsi_debug_store_elem { - unsigned char * p; -} Sd_store_elem; -static Sd_store_elem * store_arr = 0; +static unsigned char * fake_storep; typedef struct sdebug_dev_info { Scsi_Device * sdp; @@ -135,13 +129,18 @@ typedef void (*done_fct_t) (Scsi_Cmnd *); static volatile done_fct_t * do_done = 0; -struct Scsi_Host * SHpnt = NULL; +static struct Scsi_Host * SHpnt = NULL; +static int scsi_debug_inquiry(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + Sdebug_dev_info * devip); +static int scsi_debug_mode_sense(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + Sdebug_dev_info * devip); static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num, int * errstsp, Sdebug_dev_info * devip); static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num, int * errstsp, Sdebug_dev_info * devip); -static void scsi_debug_send_self_command(struct Scsi_Host * shpnt); static void scsi_debug_intr_handle(unsigned long); static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp); static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, @@ -160,49 +159,23 @@ static int scsi_debug_lockup = 0; #define SENSE_BUFF_LEN 32 static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN]; -static int made_block0 = 0; -static void scsi_debug_mkblock0(unsigned char * buff, int bufflen, - Scsi_Cmnd * SCpnt) +static inline +unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp) { - int i; - struct partition *p; - - memset(buff, 0, bufflen); - *((unsigned short *) (buff + 510)) = 0xAA55; - p = (struct partition *) (buff + 0x1be); - i = 0; - while (starts[i + 1]) { - int start_cyl, end_cyl; - - start_cyl = starts[i] / N_HEAD / N_SECTOR; - end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR; - p->boot_ind = 0; - - p->head = (i == 0 ? 1 : 0); - p->sector = 1 | ((start_cyl >> 8) << 6); - p->cyl = (start_cyl & 0xff); - - p->end_head = N_HEAD - 1; - p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6); - p->end_cyl = (end_cyl & 0xff); - - p->start_sect = starts[i]; - p->nr_sects = starts[i + 1] - starts[i]; - p->sys_ind = 0x83; /* Linux ext2 partition */ - p++; - i++; - } - if (!npart) - npart = i; - made_block0 = 1; - i = (bufflen > STORE_ELEM_SIZE) ? STORE_ELEM_SIZE : bufflen; - memcpy(store_arr[0].p, buff, i); + if (NULL == sclp) + return NULL; + else if (sclp->page) + return (unsigned char *)page_address(sclp->page) + + sclp->offset; + else + return NULL; } +static int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) { - unchar *cmd = (unchar *) SCpnt->cmnd; + unsigned char *cmd = (unsigned char *) SCpnt->cmnd; int block; int upper_blk; unsigned char *buff; @@ -214,6 +187,12 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) Sdebug_dev_info * devip = NULL; char * sbuff; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { + printk(KERN_INFO "scsi_debug: queue_command: cmd "); + for (i = 0, num = SCpnt->cmd_len; i < num; ++i) + printk("%02x ", cmd[i]); + printk(" use_sg=%d\n", SCpnt->use_sg); + } /* * If we are being notified of the mid-level reposessing a command * due to timeout, just return. @@ -222,14 +201,23 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) return 0; } - buff = (unsigned char *) SCpnt->request_buffer; + if (SCpnt->use_sg) { /* just use first element */ + struct scatterlist *sgpnt = (struct scatterlist *) + SCpnt->request_buffer; + + buff = sdebug_scatg2virt(&sgpnt[0]); + bufflen = sgpnt[0].length; + /* READ and WRITE process scatterlist themselves */ + } + else + buff = (unsigned char *) SCpnt->request_buffer; /* * If a command comes for the ID of the host itself, just print * a silly message and return. */ if(target == 7) { - printk("How do you do!\n"); + printk(KERN_WARNING "How do you do!\n"); SCpnt->result = 0; done(SCpnt); return 0; @@ -243,7 +231,7 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) #if 0 printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n", (int)SCpnt->device->host->host_no, (int)SCpnt->device->id, - SCpnt->device, (int)(unsigned char)*cmd); + SCpnt->device, (int)*cmd); #endif if (NULL == SCpnt->device->hostdata) { devip = devInfoReg(SCpnt->device); @@ -257,6 +245,13 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) devip = SCpnt->device->hostdata; switch (*cmd) { + case INQUIRY: /* mandatory */ + scsi_debug_errsts = scsi_debug_inquiry(cmd, target, buff, + bufflen, devip); + /* assume INQUIRY called first so setup max_cmd_len */ + if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) + SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; + break; case REQUEST_SENSE: /* mandatory */ SCSI_LOG_LLQUEUE(3, printk("Request sense...\n")); if (devip) { @@ -294,23 +289,6 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) } scsi_debug_errsts = 0; break; - case INQUIRY: /* mandatory */ - SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen)); - memset(buff, 0, bufflen); - buff[0] = DEVICE_TYPE(target); - buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0; - /* Removable disk */ - buff[2] = 2; /* claim SCSI 2 */ - buff[4] = 36 - 5; - memcpy(&buff[8], "Linux ", 8); - memcpy(&buff[16], "scsi_debug ", 16); - memcpy(&buff[32], "0002", 4); - scsi_debug_errsts = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) - if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) - SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; -#endif - break; case SEND_DIAGNOSTIC: /* mandatory */ SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n")); if (buff) @@ -318,7 +296,8 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) scsi_debug_errsts = 0; break; case TEST_UNIT_READY: /* mandatory */ - SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", buff, bufflen)); + SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", + buff, bufflen)); if (buff) memset(buff, 0, bufflen); scsi_debug_errsts = 0; @@ -424,19 +403,15 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) (done) (SCpnt); return 0; case MODE_SENSE: - /* - * Used to detect write protected status. - */ - scsi_debug_errsts = 0; - memset(buff, 0, 6); + case MODE_SENSE_10: + scsi_debug_errsts = + scsi_debug_mode_sense(cmd, target, buff, bufflen, devip); break; default: #if 0 - SCSI_LOG_LLQUEUE(3, printk("Unknown command %d\n", *cmd)); - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; -#else + printk(KERN_INFO "scsi_debug: Unsupported command, " + "opcode=0x%x\n", (int)cmd[0]); +#endif if (check_reset(SCpnt, devip)) { done(SCpnt); return 0; @@ -445,7 +420,6 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) (CHECK_CONDITION << 1); mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14); break; -#endif } spin_lock_irqsave(&mailbox_lock, iflags); @@ -485,10 +459,11 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) spin_unlock_irqrestore(&mailbox_lock, iflags); add_timer(&timeout[i]); if (!done) - printk("scsi_debug_queuecommand: done can't be NULL\n"); + printk(KERN_ERR "scsi_debug_queuecommand: " + "done can't be NULL\n"); #if 0 - printk("Sending command (%d %x %d %d)...", i, done, + printk(KERN_INFO "Sending command (%d %x %d %d)...", i, done, timeout[i].expires, jiffies); #endif #endif @@ -496,6 +471,14 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) return 0; } +static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg) +{ + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { + printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd); + } + return -ENOTTY; +} + static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip) { if (devip->reset) { @@ -508,21 +491,188 @@ static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip) return 0; } -static inline -unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp) +#define SDEBUG_MAX_INQ_SZ 58 + +static int scsi_debug_inquiry(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + Sdebug_dev_info * devip) { - if (NULL == sclp) - return NULL; - else if (sclp->page) - return (unsigned char *)page_address(sclp->page) + - sclp->offset; + unsigned char pq_pdt; + unsigned char arr[SDEBUG_MAX_INQ_SZ]; + int min_len = bufflen > SDEBUG_MAX_INQ_SZ ? + SDEBUG_MAX_INQ_SZ : bufflen; + + SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen)); + if (bufflen < cmd[4]) + printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d " + "< alloc_length=%d\n", bufflen, (int)cmd[4]); + memset(buff, 0, bufflen); + memset(arr, 0, SDEBUG_MAX_INQ_SZ); + pq_pdt = DEVICE_TYPE(target); + arr[0] = pq_pdt; + if (0x2 & cmd[1]) { /* CMDDT bit set */ + mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); + } + else if (0x1 & cmd[1]) { /* EVPD bit set */ + if (0 == cmd[2]) { /* supported vital product data pages */ + arr[3] = 1; + arr[4] = 0x80; /* ... only unit serial number */ + } + else if (0x80 == cmd[2]) { /* unit serial number */ + arr[1] = 0x80; + arr[3] = 4; + arr[4] = '1'; arr[5] = '2'; arr[6] = '3'; + arr[7] = '4'; + } + else { + /* Illegal request, invalid field in cdb */ + mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); + } + memcpy(buff, arr, min_len); + return 0; + } + /* drops through here for a standard inquiry */ + arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ + arr[2] = 3; /* claim SCSI 3 */ + arr[4] = SDEBUG_MAX_INQ_SZ - 5; + arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */ + memcpy(&arr[8], "Linux ", 8); + memcpy(&arr[16], "scsi_debug ", 16); + memcpy(&arr[32], "0003", 4); + memcpy(buff, arr, min_len); + return 0; +} + +/* <<Following mode page info copied from ST318451LW>> */ + +static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target) +{ /* Read-Write Error Recovery page for mode_sense */ + unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, + 5, 0, 0xff, 0xff}; + + memcpy(p, err_recov_pg, sizeof(err_recov_pg)); + if (1 == pcontrol) + memset(p + 2, 0, sizeof(err_recov_pg) - 2); + return sizeof(err_recov_pg); +} + +static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target) +{ /* Disconnect-Reconnect page for mode_sense */ + unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + + memcpy(p, disconnect_pg, sizeof(disconnect_pg)); + if (1 == pcontrol) + memset(p + 2, 0, sizeof(disconnect_pg) - 2); + return sizeof(disconnect_pg); +} + +static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target) +{ /* Caching page for mode_sense */ + unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; + + memcpy(p, caching_pg, sizeof(caching_pg)); + if (1 == pcontrol) + memset(p + 2, 0, sizeof(caching_pg) - 2); + return sizeof(caching_pg); +} + +static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target) +{ /* Control mode page for mode_sense */ + unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, + 0, 0, 0x2, 0x4b}; + + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); + if (1 == pcontrol) + memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); + return sizeof(ctrl_m_pg); +} + + +#define SDEBUG_MAX_MSENSE_SZ 256 + +static int scsi_debug_mode_sense(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + Sdebug_dev_info * devip) +{ + unsigned char dbd; + int pcontrol, pcode; + unsigned char dev_spec; + int alloc_len, msense_6, offset, len; + unsigned char * ap; + unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; + int min_len = bufflen > SDEBUG_MAX_MSENSE_SZ ? + SDEBUG_MAX_MSENSE_SZ : bufflen; + + SCSI_LOG_LLQUEUE(3, printk("Mode sense ...(%p %d)\n", buff, bufflen)); + dbd = cmd[1] & 0x8; + pcontrol = (cmd[2] & 0xc0) >> 6; + pcode = cmd[2] & 0x3f; + msense_6 = (MODE_SENSE == cmd[0]); + alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[6]); + /* printk(KERN_INFO "msense: dbd=%d pcontrol=%d pcode=%d " + "msense_6=%d alloc_len=%d\n", dbd, pcontrol, pcode, " + "msense_6, alloc_len); */ + if (bufflen < alloc_len) + printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d " + "< alloc_length=%d\n", bufflen, alloc_len); + memset(buff, 0, bufflen); + memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); + if (0x3 == pcontrol) { /* Saving values not supported */ + mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x39, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); + } + dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; + if (msense_6) { + arr[2] = dev_spec; + offset = 4; + } else { -#ifdef SDEBUG_SG_ADDRESS - return sclp->address; -#else - return NULL; -#endif + arr[3] = dev_spec; + offset = 8; } + ap = arr + offset; + + switch (pcode) { + case 0x1: /* Read-Write error recovery page, direct access */ + len = sdebug_err_recov_pg(ap, pcontrol, target); + offset += len; + break; + case 0x2: /* Disconnect-Reconnect page, all devices */ + len = sdebug_disconnect_pg(ap, pcontrol, target); + offset += len; + break; + case 0x8: /* Caching page, direct access */ + len = sdebug_caching_pg(ap, pcontrol, target); + offset += len; + break; + case 0xa: /* Control Mode page, all devices */ + len = sdebug_ctrl_m_pg(ap, pcontrol, target); + offset += len; + break; + case 0x3f: /* Read all Mode pages */ + len = sdebug_err_recov_pg(ap, pcontrol, target); + len += sdebug_disconnect_pg(ap + len, pcontrol, target); + len += sdebug_caching_pg(ap + len, pcontrol, target); + len += sdebug_ctrl_m_pg(ap + len, pcontrol, target); + offset += len; + break; + default: + mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); + } + if (msense_6) + arr[0] = offset - 1; + else { + offset -= 2; + arr[0] = (offset >> 8) & 0xff; + arr[1] = offset & 0xff; + } + memcpy(buff, arr, min_len); + return 0; } static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, @@ -549,11 +699,19 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, usleep(delay); } #endif - + if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && + (block >= OPT_MEDIUM_ERR_ADDR) && + (block < (OPT_MEDIUM_ERR_ADDR + num))) { + *errstsp = (COMMAND_COMPLETE << 8) | + (CHECK_CONDITION << 1); + mk_sense_buffer(devip, 1, MEDIUM_ERROR, 0x11, 0, 14); + /* claim unrecoverable read error */ + return 1; + } read_lock_irqsave(&sdebug_atomic_rw, iflags); sgcount = 0; nbytes = bufflen; - /* printk("scsi_debug_read: block=%d, tot_bufflen=%d\n", + /* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n", block, bufflen); */ if (SCpnt->use_sg) { sgcount = 0; @@ -563,30 +721,7 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, } *errstsp = 0; do { - int resid, k, off, len, rem, blk; - unsigned char * bp; - - /* If this is block 0, then we want to read the partition - * table for this device. Let's make one up */ - if (scsi_debug_fake_blk0 && (block == 0) && (! made_block0)) { - scsi_debug_mkblock0(buff, bufflen, SCpnt); - *errstsp = 0; - break; - } - bp = buff; - blk = block; - for (resid = bufflen; resid > 0; resid -= len) { - k = blk / SECT_PER_ELEM; - off = (blk % SECT_PER_ELEM) * SECT_SIZE; - rem = STORE_ELEM_SIZE - off; - len = (resid > rem) ? rem : resid; -/* printk("sdr: blk=%d k=%d off=%d rem=%d resid" - "=%d len=%d sgcount=%d\n", blk, k, - off, rem, resid, len, sgcount); */ - memcpy(bp, store_arr[k].p + off, len); - bp += len; - blk += len / SECT_SIZE; - } + memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen); #if 0 /* Simulate a disk change */ if (block == 0xfff0) { @@ -612,7 +747,8 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, } } else if (nbytes > 0) - printk("sdebug_read: unexpected nbytes=%d\n", nbytes); + printk(KERN_WARNING "sdebug_read: unexpected " + "nbytes=%d\n", nbytes); } while (nbytes); read_unlock_irqrestore(&sdebug_atomic_rw, iflags); return 0; @@ -645,20 +781,7 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, } *errstsp = 0; do { - int resid, k, off, len, rem, blk; - unsigned char * bp; - - bp = buff; - blk = block; - for (resid = bufflen; resid > 0; resid -= len) { - k = blk / SECT_PER_ELEM; - off = (blk % SECT_PER_ELEM) * SECT_SIZE; - rem = STORE_ELEM_SIZE - off; - len = (resid > rem) ? rem : resid; - memcpy(store_arr[k].p + off, bp, len); - bp += len; - blk += len / SECT_SIZE; - } + memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen); nbytes -= bufflen; if (SCpnt->use_sg) { @@ -670,52 +793,13 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, } } else if (nbytes > 0) - printk("sdebug_write: unexpected nbytes=%d\n", nbytes); + printk(KERN_WARNING "sdebug_write: " + "unexpected nbytes=%d\n", nbytes); } while (nbytes); write_unlock_irqrestore(&sdebug_atomic_rw, iflags); return 0; } -static void scsi_debug_send_self_command(struct Scsi_Host * shpnt) -{ - static unsigned char cmd[6] = - {TEST_UNIT_READY, 0, 0, 0, 0, 0}; - - Scsi_Request * scp; - Scsi_Device * sdev; - - printk("Allocating host dev\n"); - sdev = scsi_get_host_dev(shpnt); - if(sdev==NULL) - { - printk("Out of memory.\n"); - return; - } - - printk("Got %p. Allocating command block\n", sdev); - scp = scsi_allocate_request(sdev); - printk("Got %p\n", scp); - - if(scp==NULL) - { - printk("Out of memory.\n"); - goto bail; - } - - scp->sr_cmd_len = 6; - scp->sr_use_sg = 0; - - printk("Sending command\n"); - scsi_wait_req (scp, (void *) cmd, (void *) NULL, - 0, 100, 3); - - printk("Releasing command\n"); - scsi_release_request(scp); -bail: - printk("Freeing device\n"); - scsi_free_host_dev(sdev); -} - /* A "high" level interrupt handler. This should be called once per jiffy * to simulate a regular scsi disk. We use a timer to do this. */ @@ -734,18 +818,19 @@ static void scsi_debug_intr_handle(unsigned long indx) SCint[indx] = NULL; if (!my_done) { - printk("scsi_debug_intr_handle: Unexpected interrupt\n"); + printk(KERN_ERR "scsi_debug_intr_handle: Unexpected " + "interrupt\n"); return; } #if 0 - printk("In intr_handle..."); - printk("...done %d %x %d %d\n", i, my_done, to, jiffies); - printk("In intr_handle: %d %x %x\n", i, SCtmp, my_done); + printk(KERN_INFO "In intr_handle..."); + printk(KERN_INFO "...done %d %x %d %d\n", i, my_done, to, jiffies); + printk(KERN_INFO "In intr_handle: %d %x %x\n", i, SCtmp, my_done); #endif my_done(SCtmp); #if 0 - printk("Called done.\n"); + printk(KERN_INFO "Called done.\n"); #endif } @@ -753,14 +838,13 @@ static int initialized = 0; static int do_init(void) { - int sz; + int sz = STORE_SIZE; starts[3] = CAPACITY; - sz = sizeof(Sd_store_elem) * STORE_ELEMENTS; - store_arr = kmalloc(sz, GFP_ATOMIC); - if (NULL == store_arr) + fake_storep = vmalloc(sz); + if (NULL == fake_storep) return 1; - memset(store_arr, 0, sz); + memset(fake_storep, 0, sz); sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES; do_done = kmalloc(sz, GFP_ATOMIC); @@ -783,8 +867,8 @@ static int do_init(void) return 0; out: - if (store_arr) - kfree(store_arr); + if (fake_storep) + vfree(fake_storep); if (do_done) kfree((void *)do_done); if (timeout) @@ -799,34 +883,31 @@ static void do_end(void) kfree(SCint); kfree(timeout); kfree((void *)do_done); - kfree(store_arr); + vfree(fake_storep); } -int scsi_debug_detect(Scsi_Host_Template * tpnt) +static int scsi_debug_detect(Scsi_Host_Template * tpnt) { - int k, num, sz; + int k, sz; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: detect\n"); if (0 == initialized) { ++initialized; sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs; devInfop = kmalloc(sz, GFP_ATOMIC); if (NULL == devInfop) { - printk("scsi_debug_detect: out of memory, 0.5\n"); + printk(KERN_ERR "scsi_debug_detect: out of " + "memory, 0.5\n"); return 0; } memset(devInfop, 0, sz); if (do_init()) { - printk("scsi_debug_detect: out of memory, 0\n"); + printk(KERN_ERR "scsi_debug_detect: out of memory" + ", 0\n"); return 0; } - for (k = 0; k < STORE_ELEMENTS; ++k) { - store_arr[k].p = (unsigned char *) - __get_free_pages(GFP_ATOMIC, STORE_ELEM_ORDER); - if (0 == store_arr[k].p) - goto detect_err; - memset(store_arr[k].p, 0, STORE_ELEM_SIZE); - } for (k = 0; k < NUM_SENSE_BUFFS; ++k) sense_buffers[k][0] = 0x70; for (k = 0; k < NR_HOSTS_PRESENT; k++) { @@ -836,43 +917,21 @@ int scsi_debug_detect(Scsi_Host_Template * tpnt) return NR_HOSTS_PRESENT; } else { - printk("scsi_debug_detect: called again\n"); + printk(KERN_WARNING "scsi_debug_detect: called again\n"); return 0; } - -detect_err: - num = k; - for (k = 0; k < STORE_ELEMENTS; ++k) { - if (0 != store_arr[k].p) { - free_pages((unsigned long)store_arr[k].p, - STORE_ELEM_ORDER); - store_arr[k].p = NULL; - } - } - printk("scsi_debug_detect: out of memory: %d out of %d bytes\n", - (int)(num * STORE_ELEM_SIZE), - (int)(scsi_debug_dev_size_mb * 1024 * 1024)); - return 0; } static int num_releases = 0; -int scsi_debug_release(struct Scsi_Host * hpnt) +static int scsi_debug_release(struct Scsi_Host * hpnt) { - int k; - + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: release\n"); scsi_unregister(hpnt); if (++num_releases != NR_HOSTS_PRESENT) return 0; - - for (k = 0; k < STORE_ELEMENTS; ++k) { - if (0 != store_arr[k].p) { - free_pages((unsigned long)store_arr[k].p, - STORE_ELEM_ORDER); - store_arr[k].p = NULL; - } - } do_end(); kfree(devInfop); return 0; @@ -922,8 +981,10 @@ static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, sbuff[13] = asq; } -int scsi_debug_abort(Scsi_Cmnd * SCpnt) +static int scsi_debug_abort(Scsi_Cmnd * SCpnt) { + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: abort\n"); #if 1 ++num_aborts; return SUCCESS; @@ -947,8 +1008,10 @@ int scsi_debug_abort(Scsi_Cmnd * SCpnt) #endif } -int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info) +static int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info) { + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: biosparam\n"); /* int size = disk->capacity; */ info[0] = N_HEAD; info[1] = N_SECTOR; @@ -958,36 +1021,13 @@ int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info) return 0; } -#if 0 -int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why) -{ - int i; - unsigned long iflags; - - void (*my_done) (Scsi_Cmnd *); - printk("Bus unlocked by reset - %d\n", why); - scsi_debug_lockup = 0; - for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) { - if (SCint[i] == NULL) - continue; - SCint[i]->result = DID_RESET << 16; - my_done = do_done[i]; - my_done(SCint[i]); - spin_lock_irqsave(&mailbox_lock, iflags); - SCint[i] = NULL; - do_done[i] = NULL; - timeout[i].function = NULL; - spin_unlock_irqrestore(&mailbox_lock, iflags); - } - return SCSI_RESET_SUCCESS; -} -#endif - -int scsi_debug_device_reset(Scsi_Cmnd * SCpnt) +static int scsi_debug_device_reset(Scsi_Cmnd * SCpnt) { Scsi_Device * sdp; int k; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: device_reset\n"); ++num_dev_resets; if (SCpnt && ((sdp = SCpnt->device))) { for (k = 0; k < scsi_debug_num_devs; ++k) { @@ -1000,12 +1040,14 @@ int scsi_debug_device_reset(Scsi_Cmnd * SCpnt) return SUCCESS; } -int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt) +static int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt) { Scsi_Device * sdp; struct Scsi_Host * hp; int k; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: bus_reset\n"); ++num_bus_resets; if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) { for (k = 0; k < scsi_debug_num_devs; ++k) { @@ -1016,10 +1058,12 @@ int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt) return SUCCESS; } -int scsi_debug_host_reset(Scsi_Cmnd * SCpnt) +static int scsi_debug_host_reset(Scsi_Cmnd * SCpnt) { int k; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: host_reset\n"); ++num_host_resets; for (k = 0; k < scsi_debug_num_devs; ++k) devInfop[k].reset = 1; @@ -1037,7 +1081,7 @@ static int __init scsi_debug_num_devs_setup(char *str) scsi_debug_num_devs = tmp; return 1; } else { - printk("scsi_debug_num_devs: usage scsi_debug_num_devs=<n> " + printk(KERN_INFO "scsi_debug_num_devs: usage scsi_debug_num_devs=<n> " "(<n> can be from 1 to around 2000)\n"); return 0; } @@ -1054,13 +1098,32 @@ static int __init scsi_debug_dev_size_mb_setup(char *str) scsi_debug_dev_size_mb = tmp; return 1; } else { - printk("scsi_debug_dev_size_mb: usage scsi_debug_dev_size_mb=<n>\n" + printk(KERN_INFO "scsi_debug_dev_size_mb: usage " + "scsi_debug_dev_size_mb=<n>\n" " (<n> is number of MB ram shared by all devs\n"); return 0; } } __setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup); + +static int __init scsi_debug_opts_setup(char *str) +{ + int tmp; + + if (get_option(&str, &tmp) == 1) { + if (tmp > 0) + scsi_debug_opts = tmp; + return 1; + } else { + printk(KERN_INFO "scsi_debug_opts: usage " + "scsi_debug_opts=<n>\n" + " (1->noise, 2->medium_error, 4->... (can be or-ed)\n"); + return 0; + } +} + +__setup("scsi_debug_opts=", scsi_debug_opts_setup); #endif MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); @@ -1069,6 +1132,8 @@ MODULE_PARM(scsi_debug_num_devs, "i"); MODULE_PARM_DESC(scsi_debug_num_devs, "number of SCSI devices to simulate"); MODULE_PARM(scsi_debug_dev_size_mb, "i"); MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs"); +MODULE_PARM(scsi_debug_opts, "i"); +MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->..."); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); @@ -1076,19 +1141,20 @@ MODULE_LICENSE("GPL"); static char sdebug_info[256]; -const char * scsi_debug_info(struct Scsi_Host * shp) +static const char * scsi_debug_info(struct Scsi_Host * shp) { sprintf(sdebug_info, "scsi_debug, %s, num_devs=%d, " - "dev_size_mb=%d\n", scsi_debug_version_str, - scsi_debug_num_devs, scsi_debug_dev_size_mb); + "dev_size_mb=%d, opts=0x%x", scsi_debug_version_str, + scsi_debug_num_devs, scsi_debug_dev_size_mb, + scsi_debug_opts); return sdebug_info; } /* scsi_debug_proc_info * Used if the driver currently has no own support for /proc/scsi */ -int scsi_debug_proc_info(char *buffer, char **start, off_t offset, - int length, int inode, int inout) +static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, + int length, int inode, int inout) { int len, pos, begin; int orig_length; @@ -1096,55 +1162,28 @@ int scsi_debug_proc_info(char *buffer, char **start, off_t offset, orig_length = length; if (inout == 1) { - /* First check for the Signature */ - if (length >= 10 && strncmp(buffer, "scsi_debug", 10) == 0) { - buffer += 11; - length -= 11; - - if (buffer[length - 1] == '\n') { - buffer[length - 1] = '\0'; - length--; - } - /* - * OK, we are getting some kind of command. Figure out - * what we are supposed to do here. Simulate bus lockups - * to test our reset capability. - */ - if (length == 4 && strncmp(buffer, "test", length) == 0) { - printk("Testing send self command %p\n", SHpnt); - scsi_debug_send_self_command(SHpnt); - return orig_length; - } - if (length == 6 && strncmp(buffer, "lockup", length) == 0) { - scsi_debug_lockup = 1; - return orig_length; - } - if (length == 6 && strncmp(buffer, "unlock", length) == 0) { - scsi_debug_lockup = 0; - return orig_length; - } - printk("Unknown command:%s (%d)\n", buffer, length); - } else - printk("Wrong Signature:%10s\n", (char *) buffer); - - return -EINVAL; - + char arr[16]; + int minLen = length > 15 ? 15 : length; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + memcpy(arr, buffer, minLen); + arr[minLen] = '\0'; + if (1 != sscanf(arr, "%d", &pos)) + return -EINVAL; + scsi_debug_opts = pos; + return length; } begin = 0; pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n" - "num_devs=%d, shared (ram) size=%d MB, sector_size=%d bytes\n" - "cylinders=%d, heads=%d, sectors=%d\n" + "num_devs=%d, shared (ram) size=%d MB, opts=0x%x\n" + "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n" "number of aborts=%d, device_reset=%d, bus_resets=%d, " "host_resets=%d\n", scsi_debug_version_str, scsi_debug_num_devs, - scsi_debug_dev_size_mb, SECT_SIZE, + scsi_debug_dev_size_mb, scsi_debug_opts, SECT_SIZE, N_CYLINDER, N_HEAD, N_SECTOR, num_aborts, num_dev_resets, num_bus_resets, num_host_resets); -#if 0 - "This driver is not a real scsi driver, but it plays one on TV.\n" - "It is very handy for debugging specific problems because you\n" - "can simulate a variety of error conditions\n"); -#endif if (pos < offset) { len = 0; begin = pos; diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h index bf3815b62641e40d5c30c554cab605a1d07b2838..d1eb11c93fd697965c3a0a98a4f3ed6a0d0c3f45 100644 --- a/drivers/scsi/scsi_debug.h +++ b/drivers/scsi/scsi_debug.h @@ -3,16 +3,16 @@ #include <linux/types.h> #include <linux/kdev_t.h> -int scsi_debug_detect(Scsi_Host_Template *); -int scsi_debug_command(Scsi_Cmnd *); -int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -int scsi_debug_abort(Scsi_Cmnd *); -int scsi_debug_biosparam(Disk *, kdev_t, int[]); -int scsi_debug_bus_reset(Scsi_Cmnd *); -int scsi_debug_dev_reset(Scsi_Cmnd *); -int scsi_debug_host_reset(Scsi_Cmnd *); -int scsi_debug_proc_info(char *, char **, off_t, int, int, int); -const char * scsi_debug_info(struct Scsi_Host *); +static int scsi_debug_detect(Scsi_Host_Template *); +/* static int scsi_debug_command(Scsi_Cmnd *); */ +static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); +static int scsi_debug_abort(Scsi_Cmnd *); +static int scsi_debug_biosparam(Disk *, kdev_t, int[]); +static int scsi_debug_bus_reset(Scsi_Cmnd *); +static int scsi_debug_device_reset(Scsi_Cmnd *); +static int scsi_debug_host_reset(Scsi_Cmnd *); +static int scsi_debug_proc_info(char *, char **, off_t, int, int, int); +static const char * scsi_debug_info(struct Scsi_Host *); #ifndef NULL #define NULL 0 @@ -31,6 +31,7 @@ const char * scsi_debug_info(struct Scsi_Host *); info: scsi_debug_info, \ detect: scsi_debug_detect, \ release: scsi_debug_release, \ + ioctl: scsi_debug_ioctl, \ queuecommand: scsi_debug_queuecommand, \ eh_abort_handler: scsi_debug_abort, \ eh_bus_reset_handler: scsi_debug_bus_reset, \ diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c index 3582d44e09d47cb672388dec0eb83d883fc0d2d5..9b653682e1bdcc88964180d608265cb018c9c194 100644 --- a/drivers/scsi/scsicam.c +++ b/drivers/scsi/scsicam.c @@ -126,7 +126,7 @@ int scsi_partsize(unsigned char *buf, unsigned long capacity, unsigned int logical_end, physical_end, ext_physical_end; - if (*(unsigned short *) (buf + 66) == 0xAA55) { + if (*(unsigned short *) (buf + 64) == 0xAA55) { for (largest_cyl = -1, i = 0; i < 4; ++i, ++p) { if (!p->sys_ind) continue; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index dde07870e33fdc60bbd355fc42deba818f44fe4b..2e4496fec5481dd803ad6bc29496244f47b6f450 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -924,8 +924,8 @@ static int st_open(struct inode *inode, struct file *filp) int dev = TAPE_NR(inode->i_rdev); write_lock(&st_dev_arr_lock); - STp = scsi_tapes[dev]; - if (dev >= st_template.dev_max || STp == NULL) { + if (dev >= st_template.dev_max || scsi_tapes == NULL || + ((STp = scsi_tapes[dev]) == NULL)) { write_unlock(&st_dev_arr_lock); return (-ENXIO); } diff --git a/drivers/scsi/sym53c8xx_2/Makefile b/drivers/scsi/sym53c8xx_2/Makefile index b352b03861e2976918ce94322fe457a622b393ef..3417dea52d835cbb32bf2da46380c69c7977c0ed 100644 --- a/drivers/scsi/sym53c8xx_2/Makefile +++ b/drivers/scsi/sym53c8xx_2/Makefile @@ -1,15 +1,10 @@ -# File: drivers/sym53c8xx/Makefile # Makefile for the NCR/SYMBIOS/LSI 53C8XX PCI SCSI controllers driver. -list-multi := sym53c8xx.o sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_misc.o sym_nvram.o obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o EXTRA_CFLAGS += -I. -sym53c8xx.o: $(sym53c8xx-objs) - $(LD) -r -o $@ $(sym53c8xx-objs) - include $(TOPDIR)/Rules.make clean: diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index a30d939ef3a8816f22a715809269a7ea8cbade7a..b02a3aee8489dafd7a9637afe4220264d6679832 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -3523,7 +3523,7 @@ static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unit unsigned char *p1; unsigned int i, j; - if (test_and_set_bit(unitid, &state->unitbitmap)) { + if (test_and_set_bit(unitid, state->unitbitmap)) { printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid); return; } @@ -3571,7 +3571,7 @@ static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unit return; case PROCESSING_UNIT: - if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) { + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid); return; } @@ -3613,7 +3613,7 @@ static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *b state.buffer = buffer; state.buflen = buflen; state.ctrlif = ctrlif; - set_bit(oterm[3], &state.unitbitmap); /* mark terminal ID as visited */ + set_bit(oterm[3], state.unitbitmap); /* mark terminal ID as visited */ printk(KERN_DEBUG "usbaudio: constructing mixer for Terminal %u type 0x%04x\n", oterm[3], oterm[4] | (oterm[5] << 8)); usb_audio_recurseunit(&state, oterm[7]); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 381b4be68364fd54f5d37595be35ee197cf5c3f8..ad6a32776e8276e1d38de75199f292b7fc079091 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -144,7 +144,7 @@ struct acm { struct usb_device *dev; /* the coresponding usb device */ struct usb_interface *iface; /* the interfaces - +0 control +1 data */ struct tty_struct *tty; /* the coresponding tty */ - struct urb ctrlurb, readurb, writeurb; /* urbs */ + struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct acm_line line; /* line coding (bits, stop, parity) */ struct tq_struct tqueue; /* task queue for line discipline waking up */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ @@ -316,12 +316,12 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) unlock_kernel(); - acm->ctrlurb.dev = acm->dev; - if (usb_submit_urb(&acm->ctrlurb, GFP_KERNEL)) + acm->ctrlurb->dev = acm->dev; + if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) dbg("usb_submit_urb(ctrl irq) failed"); - acm->readurb.dev = acm->dev; - if (usb_submit_urb(&acm->readurb, GFP_KERNEL)) + acm->readurb->dev = acm->dev; + if (usb_submit_urb(acm->readurb, GFP_KERNEL)) dbg("usb_submit_urb(read bulk) failed"); acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); @@ -342,12 +342,15 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) if (!--acm->used) { if (acm->dev) { acm_set_control(acm, acm->ctrlout = 0); - usb_unlink_urb(&acm->ctrlurb); - usb_unlink_urb(&acm->writeurb); - usb_unlink_urb(&acm->readurb); + usb_unlink_urb(acm->ctrlurb); + usb_unlink_urb(acm->writeurb); + usb_unlink_urb(acm->readurb); } else { tty_unregister_devfs(&acm_tty_driver, acm->minor); acm_table[acm->minor] = NULL; + usb_free_urb(acm->ctrlurb); + usb_free_urb(acm->readurb); + usb_free_urb(acm->writeurb); kfree(acm); } } @@ -359,20 +362,20 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; - if (acm->writeurb.status == -EINPROGRESS) return 0; + if (acm->writeurb->status == -EINPROGRESS) return 0; if (!count) return 0; count = (count > acm->writesize) ? acm->writesize : count; if (from_user) - copy_from_user(acm->writeurb.transfer_buffer, buf, count); + copy_from_user(acm->writeurb->transfer_buffer, buf, count); else - memcpy(acm->writeurb.transfer_buffer, buf, count); + memcpy(acm->writeurb->transfer_buffer, buf, count); - acm->writeurb.transfer_buffer_length = count; - acm->writeurb.dev = acm->dev; + acm->writeurb->transfer_buffer_length = count; + acm->writeurb->dev = acm->dev; - if (usb_submit_urb(&acm->writeurb, GFP_KERNEL)) + if (usb_submit_urb(acm->writeurb, GFP_KERNEL)) dbg("usb_submit_urb(write bulk) failed"); return count; @@ -382,14 +385,14 @@ static int acm_tty_write_room(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; - return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize; + return acm->writeurb->status == -EINPROGRESS ? 0 : acm->writesize; } static int acm_tty_chars_in_buffer(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return -EINVAL; - return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0; + return acm->writeurb->status == -EINPROGRESS ? acm->writeurb->transfer_buffer_length : 0; } static void acm_tty_throttle(struct tty_struct *tty) @@ -404,8 +407,8 @@ static void acm_tty_unthrottle(struct tty_struct *tty) struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; acm->throttle = 0; - if (acm->readurb.status != -EINPROGRESS) - acm_read_bulk(&acm->readurb); + if (acm->readurb->status != -EINPROGRESS) + acm_read_bulk(acm->readurb); } static void acm_tty_break_ctl(struct tty_struct *tty, int state) @@ -585,16 +588,38 @@ static void *acm_probe(struct usb_device *dev, unsigned int ifnum, return NULL; } - FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->ctrlurb) { + err("out of memory"); + kfree(acm); + return NULL; + } + acm->readurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->readurb) { + err("out of memory"); + usb_free_urb(acm->ctrlurb); + kfree(acm); + return NULL; + } + acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); + if (!acm->writeurb) { + err("out of memory"); + usb_free_urb(acm->readurb); + usb_free_urb(acm->ctrlurb); + kfree(acm); + return NULL; + } + + usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); - FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf += ctrlsize, readsize, acm_read_bulk, acm); - acm->readurb.transfer_flags |= USB_NO_FSBR; + acm->readurb->transfer_flags |= USB_NO_FSBR; - FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf += readsize, acm->writesize, acm_write_bulk, acm); - acm->writeurb.transfer_flags |= USB_NO_FSBR; + acm->writeurb->transfer_flags |= USB_NO_FSBR; printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); @@ -625,11 +650,11 @@ static void acm_disconnect(struct usb_device *dev, void *ptr) acm->dev = NULL; - usb_unlink_urb(&acm->ctrlurb); - usb_unlink_urb(&acm->readurb); - usb_unlink_urb(&acm->writeurb); + usb_unlink_urb(acm->ctrlurb); + usb_unlink_urb(acm->readurb); + usb_unlink_urb(acm->writeurb); - kfree(acm->ctrlurb.transfer_buffer); + kfree(acm->ctrlurb->transfer_buffer); usb_driver_release_interface(&acm_driver, acm->iface + 0); usb_driver_release_interface(&acm_driver, acm->iface + 1); @@ -637,6 +662,9 @@ static void acm_disconnect(struct usb_device *dev, void *ptr) if (!acm->used) { tty_unregister_devfs(&acm_tty_driver, acm->minor); acm_table[acm->minor] = NULL; + usb_free_urb(acm->ctrlurb); + usb_free_urb(acm->readurb); + usb_free_urb(acm->writeurb); kfree(acm); return; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d6df3e30e60370fa6f993f8d801807979ba50c3e..3e06620b8bef24a9f4a176e124c288824090fc02 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -297,7 +297,9 @@ static void destroy_all_async(struct dev_state *ps) } /* - * interface claiming + * interface claims are made only at the request of user level code, + * which can also release them (explicitly or by closing files). + * they're also undone when devices disconnect. */ static void *driver_probe(struct usb_device *dev, unsigned int intf, @@ -310,8 +312,20 @@ static void driver_disconnect(struct usb_device *dev, void *context) { struct dev_state *ps = (struct dev_state *)context; - if (ps) - ps->ifclaimed = 0; + if (!ps) + return; + + /* this waits till synchronous requests complete */ + down_write (&ps->devsem); + + /* prevent new I/O requests */ + ps->dev = 0; + ps->ifclaimed = 0; + + /* force async requests to complete */ + destroy_all_async (ps); + + up_write (&ps->devsem); } struct usb_driver usbdevfs_driver = { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 110c76926d6ecc568e0dd369be713332ce748b3b..8f2caebe87a310b33ee6121ce792470ab113e45c 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -310,7 +310,7 @@ static int rh_string ( // serial number } else if (id == 1) { - strcpy (buf, hcd->bus_name); + strcpy (buf, hcd->self.bus_name); // product description } else if (id == 2) { @@ -406,7 +406,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case DeviceOutRequest | USB_REQ_SET_ADDRESS: // wValue == urb->dev->devaddr dbg ("%s root hub device address %d", - hcd->bus_name, wValue); + hcd->self.bus_name, wValue); break; /* INTERFACE REQUESTS (no defined feature/status flags) */ @@ -520,7 +520,7 @@ static void rh_report_status (unsigned long ptr) && rh_status_urb (hcd, urb) != 0) { /* another driver snuck in? */ dbg ("%s, can't resubmit roothub status urb?", - hcd->bus_name); + hcd->self.bus_name); spin_unlock_irqrestore (&hcd_data_lock, flags); BUG (); } @@ -1051,8 +1051,7 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) usb_init_bus (&hcd->self); hcd->self.op = &hcd_operations; hcd->self.hcpriv = (void *) hcd; - hcd->bus = &hcd->self; - hcd->bus_name = dev->slot_name; + hcd->self.bus_name = dev->slot_name; hcd->product_desc = dev->name; INIT_LIST_HEAD (&hcd->dev_list); @@ -1089,16 +1088,15 @@ void usb_hcd_pci_remove (struct pci_dev *dev) hcd = pci_get_drvdata(dev); if (!hcd) return; - info ("remove: %s, state %x", hcd->bus_name, hcd->state); + info ("remove: %s, state %x", hcd->self.bus_name, hcd->state); if (in_interrupt ()) BUG (); - hub = hcd->bus->root_hub; + hub = hcd->self.root_hub; hcd->state = USB_STATE_QUIESCING; - dbg ("%s: roothub graceful disconnect", hcd->bus_name); + dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); usb_disconnect (&hub); - // usb_disconnect (&hcd->bus->root_hub); hcd->driver->stop (hcd); hcd->state = USB_STATE_HALT; @@ -1113,10 +1111,9 @@ void usb_hcd_pci_remove (struct pci_dev *dev) pci_resource_len (dev, hcd->region)); } - usb_deregister_bus (hcd->bus); + usb_deregister_bus (&hcd->self); if (atomic_read (&hcd->self.refcnt) != 1) - err ("usb_hcd_pci_remove %s, count != 1", hcd->bus_name); - hcd->bus = NULL; + err ("usb_hcd_pci_remove %s, count != 1", hcd->self.bus_name); hcd->driver->hcd_free (hcd); } @@ -1164,7 +1161,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) int retval; hcd = pci_get_drvdata(dev); - info ("suspend %s to state %d", hcd->bus_name, state); + info ("suspend %s to state %d", hcd->self.bus_name, state); pci_save_state (dev, hcd->pci_state); @@ -1193,12 +1190,12 @@ int usb_hcd_pci_resume (struct pci_dev *dev) int retval; hcd = pci_get_drvdata(dev); - info ("resume %s", hcd->bus_name); + info ("resume %s", hcd->self.bus_name); /* guard against multiple resumes (APM bug?) */ atomic_inc (&hcd->resume_count); if (atomic_read (&hcd->resume_count) != 1) { - err ("concurrent PCI resumes for %s", hcd->bus_name); + err ("concurrent PCI resumes for %s", hcd->self.bus_name); retval = 0; goto done; } @@ -1215,7 +1212,7 @@ int usb_hcd_pci_resume (struct pci_dev *dev) retval = hcd->driver->resume (hcd); if (!HCD_IS_RUNNING (hcd->state)) { - dbg ("resume %s failure, retval %d", hcd->bus_name, retval); + dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval); hc_died (hcd); // FIXME: recover, reset etc. } else { @@ -1290,7 +1287,7 @@ static void hc_died (struct usb_hcd *hcd) list_for_each (urblist, &dev->urb_list) { urb = list_entry (urblist, struct urb, urb_list); dbg ("shutdown %s urb %p pipe %x, current status %d", - hcd->bus_name, urb, urb->pipe, urb->status); + hcd->self.bus_name, urb, urb->pipe, urb->status); if (urb->status == -EINPROGRESS) urb->status = -ESHUTDOWN; } @@ -1534,7 +1531,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) * since we report some queuing/setup errors ourselves */ urb = usb_get_urb (urb); - if (urb->dev == hcd->bus->root_hub) + if (urb->dev == hcd->self.root_hub) status = rh_urb_enqueue (hcd, urb); else status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); @@ -1686,7 +1683,7 @@ if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval); && HCD_IS_RUNNING (hcd->state) && !retval) { dbg ("%s: wait for giveback urb %p", - hcd->bus_name, urb); + hcd->self.bus_name, urb); wait_for_completion (&splice.done); } else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) { return -EINPROGRESS; @@ -1698,7 +1695,7 @@ if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval); bye: if (retval) dbg ("%s: hcd_unlink_urb fail %d", - hcd ? hcd->bus_name : "(no bus?)", + hcd ? hcd->self.bus_name : "(no bus?)", retval); return retval; } @@ -1731,7 +1728,7 @@ static int hcd_free_dev (struct usb_device *udev) /* device driver problem with refcounts? */ if (!list_empty (&dev->urb_list)) { dbg ("free busy dev, %s devnum %d (bug!)", - hcd->bus_name, udev->devnum); + hcd->self.bus_name, udev->devnum); return -EINVAL; } diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 221145e0bbd944d2407aa3fcaa426895c91beb7f..30422107d18b5ff4016aaf00b726e749af75e32b 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -35,10 +35,8 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ /* * housekeeping */ - struct usb_bus *bus; /* FIXME only use "self" */ struct usb_bus self; /* hcd is-a bus */ - const char *bus_name; const char *product_desc; /* product/vendor string */ const char *description; /* "ehci-hcd" etc */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d77130aa0263c501c80ad3b246d98a743f4274a1..3d7efd659d75325573bec4c86d2e0908a4c0b1c8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -765,23 +765,23 @@ static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port, * devices by location for diagnostics, tools, etc. The * string is a path along hub ports, from the root. Each * device's id will be stable until USB is re-cabled, and - * hubs are often labled with these port numbers. + * hubs are often labeled with these port numbers. * - * Initial size: "/NN" times five hubs + NUL = 16 bytes max + * Initial size: ".NN" times five hubs + NUL = 16 bytes max * (quite rare, since most hubs have 4-6 ports). */ pdev = dev->parent; - if (pdev->devpath [1] != '\0') /* parent not root */ + if (pdev->devpath [0] != '/') /* parent not root */ len = snprintf (dev->devpath, sizeof dev->devpath, - "%s/%d", pdev->devpath, port + 1); - else /* root == "/", root port 2 == "/2" */ + "%s.%d", pdev->devpath, port + 1); + else /* root == "/", root port 2 == "2", port 3 that hub "/2.3" */ len = snprintf (dev->devpath, sizeof dev->devpath, - "/%d", port + 1); + "%d", port + 1); if (len == sizeof dev->devpath) warn ("devpath size! usb/%03d/%03d path %s", dev->bus->busnum, dev->devnum, dev->devpath); - info("new USB device on bus %d path %s, assigned address %d", - dev->bus->busnum, dev->devpath, dev->devnum); + info("new USB device %s-%s, assigned address %d", + dev->bus->bus_name, dev->devpath, dev->devnum); /* put the device in the global device tree */ dev->dev.parent = &dev->parent->dev; @@ -964,7 +964,7 @@ static int usb_hub_thread(void *__hub) /* Send me a signal to get me die (for debugging) */ do { usb_hub_events(); - interruptible_sleep_on(&khubd_wait); + wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); } while (!signal_pending(current)); dbg("usb_hub_thread exiting"); @@ -1122,7 +1122,7 @@ int usb_reset_device(struct usb_device *dev) dev->devpath, sizeof(dev->descriptor), ret); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return -EIO; } @@ -1131,7 +1131,7 @@ int usb_reset_device(struct usb_device *dev) if (ret < 0) { err("unable to get configuration (error=%d)", ret); usb_destroy_configuration(dev); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ee0a5adb0b14ee26d57efb8c7aa331e548ffd285..8fe9219e66967aada7efdbcc1a9288272eb55858 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1784,7 +1784,7 @@ void usb_disconnect(struct usb_device **pdev) /* Free the device number and remove the /proc/bus/usb entry */ if (dev->devnum > 0) { - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); usbfs_remove_device(dev); put_device(&dev->dev); } @@ -2407,56 +2407,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) return err; } -/** - * usb_make_path - returns device path in the hub tree - * @dev: the device whose path is being constructed - * @buf: where to put the string - * @size: how big is "buf"? - * Context: !in_interrupt () - * - * Returns length of the string (>= 0) or out of memory status (< 0). - * - * NOTE: prefer to use use dev->devpath directly. - */ -int usb_make_path(struct usb_device *dev, char *buf, size_t size) -{ - struct usb_device *pdev = dev->parent; - char *tmp; - char *port; - int i; - - if (!(port = kmalloc(size, GFP_KERNEL))) - return -ENOMEM; - if (!(tmp = kmalloc(size, GFP_KERNEL))) { - kfree(port); - return -ENOMEM; - } - - *port = 0; - while (pdev) { - for (i = 0; i < pdev->maxchild; i++) - if (pdev->children[i] == dev) - break; - - if (pdev->children[i] != dev) { - kfree(port); - kfree(tmp); - return -ENODEV; - } - - strcpy(tmp, port); - snprintf(port, size, strlen(port) ? "%d.%s" : "%d", i + 1, tmp); - - dev = pdev; - pdev = dev->parent; - } - - snprintf(buf, size, "usb%d:%s", dev->bus->busnum, port); - kfree(port); - kfree(tmp); - return strlen(buf); -} - /* * By the time we get here, the device has gotten a new device ID * and is in the default state. We need to identify the thing and @@ -2484,7 +2434,7 @@ int usb_new_device(struct usb_device *dev) if (err < 0) { err("USB device not accepting new address=%d (error=%d)", dev->devnum, err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -2497,7 +2447,7 @@ int usb_new_device(struct usb_device *dev) err("USB device not responding, giving up (error=%d)", err); else err("USB device descriptor short read (expected %i, got %i)", 8, err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -2512,7 +2462,7 @@ int usb_new_device(struct usb_device *dev) err("USB device descriptor short read (expected %Zi, got %i)", sizeof(dev->descriptor), err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -2521,7 +2471,7 @@ int usb_new_device(struct usb_device *dev) if (err < 0) { err("unable to get device %d configuration (error=%d)", dev->devnum, err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } @@ -2531,7 +2481,7 @@ int usb_new_device(struct usb_device *dev) if (err) { err("failed to set device %d default configuration (error=%d)", dev->devnum, err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 442bbe39a3c929d65f28b869b602c62d54fbce30..06b570373d14346b72971cd81d8c967ef14de3d3 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -55,7 +55,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) strcat(buf, tmp); } dbg ("%s: %s portroute %s", - ehci->hcd.bus_name, label, + ehci->hcd.self.bus_name, label, buf); } } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f7f9bf6a9022e4661409318ac071314f1f0c6ef4..212b61a84ea05850aa066c9bd7074b67fbd56bba 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -248,7 +248,7 @@ static int ehci_start (struct usb_hcd *hcd) ehci->tasklet.data = (unsigned long) ehci; /* wire up the root hub */ - hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus); + hcd->self.root_hub = udev = usb_alloc_dev (NULL, &hcd->self); if (!udev) { done2: ehci_mem_cleanup (ehci); @@ -288,8 +288,7 @@ static int ehci_start (struct usb_hcd *hcd) while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS)) udelay (100); ehci_reset (ehci); - // usb_disconnect (udev); - hcd->bus->root_hub = 0; + hcd->self.root_hub = 0; usb_free_dev (udev); retval = -ENODEV; goto done2; @@ -304,7 +303,7 @@ static void ehci_stop (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - dbg ("%s: stop", hcd->bus_name); + dbg ("%s: stop", hcd->self.bus_name); if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); @@ -339,7 +338,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) int ports; int i; - dbg ("%s: suspend to %d", hcd->bus_name, state); + dbg ("%s: suspend to %d", hcd->self.bus_name, state); ports = HCS_N_PORTS (ehci->hcs_params); @@ -356,7 +355,7 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) if ((temp & PORT_PE) == 0 || (temp & PORT_OWNER) != 0) continue; -dbg ("%s: suspend port %d", hcd->bus_name, i); +dbg ("%s: suspend port %d", hcd->self.bus_name, i); temp |= PORT_SUSPEND; writel (temp, &ehci->regs->port_status [i]); } @@ -380,7 +379,7 @@ static int ehci_resume (struct usb_hcd *hcd) int ports; int i; - dbg ("%s: resume", hcd->bus_name); + dbg ("%s: resume", hcd->self.bus_name); ports = HCS_N_PORTS (ehci->hcs_params); @@ -400,7 +399,7 @@ static int ehci_resume (struct usb_hcd *hcd) if ((temp & PORT_PE) == 0 || (temp & PORT_SUSPEND) != 0) continue; -dbg ("%s: resume port %d", hcd->bus_name, i); +dbg ("%s: resume port %d", hcd->self.bus_name, i); temp |= PORT_RESUME; writel (temp, &ehci->regs->port_status [i]); readl (&ehci->regs->command); /* unblock posted writes */ @@ -472,7 +471,7 @@ static void ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { - err ("%s: fatal error, state %x", hcd->bus_name, hcd->state); + err ("%s: fatal error, state %x", hcd->self.bus_name, hcd->state); ehci_reset (ehci); // generic layer kills/unlinks all urbs // then tasklet cleans up the rest @@ -547,7 +546,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) unsigned long flags; dbg ("%s urb_dequeue %p qh state %d", - hcd->bus_name, urb, qh->qh_state); + hcd->self.bus_name, urb, qh->qh_state); switch (usb_pipetype (urb->pipe)) { case PIPE_CONTROL: @@ -608,7 +607,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) /* ASSERT: nobody can be submitting urbs for this any more */ - dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum); + dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum); spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < 32; i++) { @@ -645,7 +644,7 @@ static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) spin_lock_irqsave (&ehci->lock, flags); } } - qh_unput (ehci, qh); + qh_put (ehci, qh); } } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 81343ece97c5920137ac3eb1a39942cc3704bd66..4b2b080663f8c111f8a3a33246fa4b5774242a16 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -41,14 +41,14 @@ static int check_reset_complete ( /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { dbg ("%s port %d full speed, give to companion, 0x%x", - ehci->hcd.bus_name, index + 1, port_status); + ehci->hcd.self.bus_name, index + 1, port_status); // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; writel (port_status, &ehci->regs->port_status [index]); } else - dbg ("%s port %d high speed", ehci->hcd.bus_name, index + 1); + dbg ("%s port %d high speed", ehci->hcd.self.bus_name, index + 1); return port_status; } @@ -306,11 +306,11 @@ static int ehci_hub_control ( if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { dbg ("%s port %d low speed, give to companion", - hcd->bus_name, wIndex + 1); + hcd->self.bus_name, wIndex + 1); temp |= PORT_OWNER; } else { vdbg ("%s port %d reset", - hcd->bus_name, wIndex + 1); + hcd->self.bus_name, wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 422f4e26b798098f0f0bb803a13ee109b70892aa..e4ee9d426a4ff81f9529f4d59235454057c4c4f3 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -98,16 +98,16 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) } /* to share a qh (cpu threads, or hc) */ -static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh) +static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) { - // dbg ("put %p (%d++)", qh, qh->refcount.counter); + // dbg ("get %p (%d++)", qh, qh->refcount.counter); atomic_inc (&qh->refcount); return qh; } -static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) { - // dbg ("unput %p (--%d)", qh, qh->refcount.counter); + // dbg ("put %p (--%d)", qh, qh->refcount.counter); if (!atomic_dec_and_test (&qh->refcount)) return; /* clean qtds first, and know this is not linked */ diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index ba90524d0eaaeb3b45887fb080fb0e6f3cde014b..8ee0a2321a42b8b0b3dc7bed8e62ddc494ec0316 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -194,7 +194,7 @@ static void ehci_urb_done ( ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (likely (urb->hcpriv != 0)) { - qh_unput (ehci, (struct ehci_qh *) urb->hcpriv); + qh_put (ehci, (struct ehci_qh *) urb->hcpriv); urb->hcpriv = 0; } @@ -733,7 +733,7 @@ submit_async ( epnum |= 0x10; vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", - ehci->hcd.bus_name, urb, urb->transfer_buffer_length, + ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length, epnum & 0x0f, (epnum & 0x10) ? "in" : "out", qtd, dev ? dev->ep [epnum] : (void *)~0); @@ -815,9 +815,9 @@ submit_async ( * the HC and TT handle it when the TT has a buffer ready. */ if (likely (qh != 0)) { - urb->hcpriv = qh_put (qh); + urb->hcpriv = qh_get (qh); if (likely (qh->qh_state == QH_STATE_IDLE)) - qh_link_async (ehci, qh_put (qh)); + qh_link_async (ehci, qh_get (qh)); } spin_unlock_irqrestore (&ehci->lock, flags); if (unlikely (!qh)) @@ -835,7 +835,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = 0; - qh_unput (ehci, qh); // refcount from reclaim + qh_put (ehci, qh); // refcount from reclaim ehci->reclaim = 0; ehci->reclaim_ready = 0; @@ -847,7 +847,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) && HCD_IS_RUNNING (ehci->hcd.state)) qh_link_async (ehci, qh); else - qh_unput (ehci, qh); // refcount from async list + qh_put (ehci, qh); // refcount from async list } @@ -872,7 +872,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) #endif qh->qh_state = QH_STATE_UNLINK; - ehci->reclaim = qh = qh_put (qh); + ehci->reclaim = qh = qh_get (qh); // dbg_qh ("start unlink", ehci, qh); @@ -937,14 +937,14 @@ static void scan_async (struct ehci_hcd *ehci) /* clean any finished work for this qh */ if (!list_empty (&qh->qtd_list)) { // dbg_qh ("scan_async", ehci, qh); - qh = qh_put (qh); + qh = qh_get (qh); spin_unlock_irqrestore (&ehci->lock, flags); /* concurrent unlink could happen here */ qh_completions (ehci, &qh->qtd_list, 1); spin_lock_irqsave (&ehci->lock, flags); - qh_unput (ehci, qh); + qh_put (ehci, qh); } /* unlink idle entries (reduces PCI usage) */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 83b6c229a93cb75ca42a8ae7878fa8954eae7960..396fa42a14c497754f1239b9aaf419bdd8113e1b 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -224,7 +224,7 @@ static void intr_deschedule ( do { periodic_unlink (ehci, frame, qh); - qh_unput (ehci, qh); + qh_put (ehci, qh); frame += period; } while (frame < ehci->periodic_size); @@ -345,7 +345,7 @@ static int intr_submit ( qh->hw_next = EHCI_LIST_END; qh->usecs = usecs; - urb->hcpriv = qh_put (qh); + urb->hcpriv = qh_get (qh); status = -ENOSPC; /* pick a set of schedule slots, link the QH into them */ @@ -393,7 +393,7 @@ static int intr_submit ( // AND handle it already being (implicitly) linked into this frame BUG (); } else { - ehci->pshadow [frame].qh = qh_put (qh); + ehci->pshadow [frame].qh = qh_get (qh); ehci->periodic [frame] = QH_NEXT (qh->qh_dma); } @@ -1113,8 +1113,8 @@ static void scan_periodic (struct ehci_hcd *ehci) temp = q.qh->qh_next; type = Q_NEXT_TYPE (q.qh->hw_next); flags = intr_complete (ehci, frame, - qh_put (q.qh), flags); - qh_unput (ehci, q.qh); + qh_get (q.qh), flags); + qh_put (ehci, q.qh); q = temp; break; case Q_TYPE_FSTN: diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index cbc1a2dd0ad4df17d8d56f64bacfa2dc503f1942..c3e79562190785218280a86f7f5cfe09b894e745 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -89,7 +89,7 @@ void ohci_dump_periodic (struct ohci_hcd *ohci, char *label) continue; printed = 1; printk (KERN_DEBUG "%s, ohci %s frame %2d:", - label, ohci->hcd.bus_name, i); + label, ohci->hcd.self.bus_name, i); while (*ed_p != 0 && j--) { struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); printk (" %p/%08x;", ed, ed->hwINFO); @@ -99,7 +99,7 @@ void ohci_dump_periodic (struct ohci_hcd *ohci, char *label) } if (!printed) printk (KERN_DEBUG "%s, ohci %s, empty periodic schedule\n", - label, ohci->hcd.bus_name); + label, ohci->hcd.self.bus_name); } #endif @@ -229,7 +229,7 @@ static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose) static void ohci_dump (struct ohci_hcd *controller, int verbose) { - dbg ("OHCI controller %s state", controller->hcd.bus_name); + dbg ("OHCI controller %s state", controller->hcd.self.bus_name); // dumps some of the state we know about ohci_dump_status (controller); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 0f42039193f7e51f6b9d03140cc6b83a31a8ef1d..88a4b024da01ef57a8468651d51053c11e113861 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -379,7 +379,7 @@ static int hc_reset (struct ohci_hcd *ohci) writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); dbg ("USB HC reset_hc %s: ctrl = 0x%x ;", - ohci->hcd.bus_name, + ohci->hcd.self.bus_name, readl (&ohci->regs->control)); /* Reset USB (needed by some controllers) */ @@ -449,7 +449,7 @@ static int hc_start (struct ohci_hcd *ohci) mdelay ((roothub_a (ohci) >> 23) & 0x1fe); /* connect the virtual root hub */ - ohci->hcd.bus->root_hub = udev = usb_alloc_dev (NULL, ohci->hcd.bus); + ohci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &ohci->hcd.self); ohci->hcd.state = USB_STATE_READY; if (!udev) { ohci->disabled = 1; @@ -491,7 +491,7 @@ static void ohci_irq (struct usb_hcd *hcd) if (ints & OHCI_INTR_UE) { ohci->disabled++; - err ("OHCI Unrecoverable Error, %s disabled", hcd->bus_name); + err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name); // e.g. due to PCI Master/Target Abort #ifdef DEBUG @@ -530,7 +530,7 @@ static void ohci_stop (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); dbg ("%s: stop %s controller%s", - hcd->bus_name, + hcd->self.bus_name, hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), ohci->disabled ? " (disabled)" : "" ); @@ -571,7 +571,7 @@ ohci_start (struct usb_hcd *hcd) && hcd->pdev->device == 0x740c) { ohci->flags = OHCI_QUIRK_AMD756; info ("%s: AMD756 erratum 4 workaround", - hcd->bus_name); + hcd->self.bus_name); } /* Apple's OHCI driver has a lot of bizarre workarounds @@ -581,7 +581,7 @@ ohci_start (struct usb_hcd *hcd) else if (hcd->pdev->vendor == 0x1045 && hcd->pdev->device == 0xc861) { info ("%s: WARNING: OPTi workarounds unavailable", - hcd->bus_name); + hcd->self.bus_name); } } #else @@ -601,7 +601,7 @@ ohci_start (struct usb_hcd *hcd) } if (hc_start (ohci) < 0) { - err ("can't start %s", ohci->hcd.bus_name); + err ("can't start %s", ohci->hcd.self.bus_name); ohci_stop (hcd); return -EBUSY; } @@ -623,13 +623,13 @@ static int ohci_suspend (struct usb_hcd *hcd, u32 state) u16 cmd; if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { - dbg ("can't suspend %s (state is %s)", hcd->bus_name, + dbg ("can't suspend %s (state is %s)", hcd->self.bus_name, hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); return -EIO; } /* act as if usb suspend can always be used */ - dbg ("%s: suspend to %d", hcd->bus_name, state); + dbg ("%s: suspend to %d", hcd->self.bus_name, state); ohci->sleeping = 1; /* First stop processing */ @@ -664,16 +664,16 @@ static int ohci_suspend (struct usb_hcd *hcd, u32 state) switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { case OHCI_USB_RESET: - dbg ("%s suspend->reset ?", hcd->bus_name); + dbg ("%s suspend->reset ?", hcd->self.bus_name); break; case OHCI_USB_RESUME: - dbg ("%s suspend->resume ?", hcd->bus_name); + dbg ("%s suspend->resume ?", hcd->self.bus_name); break; case OHCI_USB_OPER: - dbg ("%s suspend->operational ?", hcd->bus_name); + dbg ("%s suspend->operational ?", hcd->self.bus_name); break; case OHCI_USB_SUSPEND: - dbg ("%s suspended", hcd->bus_name); + dbg ("%s suspended", hcd->self.bus_name); break; } @@ -711,8 +711,8 @@ static int hc_restart (struct ohci_hcd *ohci) ohci->disabled = 1; ohci->sleeping = 0; - if (ohci->hcd.bus->root_hub) - usb_disconnect (&ohci->hcd.bus->root_hub); + if (ohci->hcd.self.root_hub) + usb_disconnect (&ohci->hcd.self.root_hub); /* empty the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0; @@ -728,10 +728,10 @@ static int hc_restart (struct ohci_hcd *ohci) ohci->ed_bulktail = NULL; if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { - err ("can't restart %s, %d", ohci->hcd.bus_name, temp); + err ("can't restart %s, %d", ohci->hcd.self.bus_name, temp); return temp; } else - dbg ("restart %s completed", ohci->hcd.bus_name); + dbg ("restart %s completed", ohci->hcd.self.bus_name); return 0; } @@ -767,13 +767,13 @@ static int ohci_resume (struct usb_hcd *hcd) switch (temp) { case OHCI_USB_RESET: // lost power - info ("USB restart: %s", hcd->bus_name); + info ("USB restart: %s", hcd->self.bus_name); retval = hc_restart (ohci); break; case OHCI_USB_SUSPEND: // host wakeup case OHCI_USB_RESUME: // remote wakeup - info ("USB continue: %s from %s wakeup", hcd->bus_name, + info ("USB continue: %s from %s wakeup", hcd->self.bus_name, (temp == OHCI_USB_SUSPEND) ? "host" : "remote"); ohci->hc_control = OHCI_USB_RESUME; @@ -786,7 +786,7 @@ static int ohci_resume (struct usb_hcd *hcd) temp = readl (&ohci->regs->control); temp = ohci->hc_control & OHCI_CTRL_HCFS; if (temp != OHCI_USB_RESUME) { - err ("controller %s won't resume", hcd->bus_name); + err ("controller %s won't resume", hcd->self.bus_name); ohci->disabled = 1; retval = -EIO; break; @@ -836,7 +836,7 @@ dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled); break; default: - warn ("odd PCI resume for %s", hcd->bus_name); + warn ("odd PCI resume for %s", hcd->self.bus_name); } return retval; } diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index ab6e751557fc95882cd0a304caa8542ac981d492..2aed53548bb54d7727e434c1e28f84367f827632 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -41,7 +41,7 @@ static u32 roothub_portstatus (struct ohci_hcd *hc, int i) #define dbg_port(hc,label,num,value) \ dbg ("%s: %s roothub.portstatus [%d] " \ "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", \ - hc->hcd.bus_name, label, num, temp, \ + hc->hcd.self.bus_name, label, num, temp, \ (temp & RH_PS_PRSC) ? " PRSC" : "", \ (temp & RH_PS_OCIC) ? " OCIC" : "", \ (temp & RH_PS_PSSC) ? " PSSC" : "", \ @@ -71,7 +71,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ports = roothub_a (ohci) & RH_A_NDP; if (ports > MAX_ROOT_PORTS) { - err ("%s: bogus NDP=%d", hcd->bus_name, ports); + err ("%s: bogus NDP=%d", hcd->self.bus_name, ports); err ("rereads as NDP=%d", readl (&ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ diff --git a/drivers/usb/host/uhci.c b/drivers/usb/host/uhci.c index 68c3e99043fd9c73ddf980341616ea49aeee63f8..7954f30e6c2f3e184b098d32e94f686483137eed 100644 --- a/drivers/usb/host/uhci.c +++ b/drivers/usb/host/uhci.c @@ -2799,6 +2799,7 @@ static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io uhci->bus = bus; bus->hcpriv = uhci; + bus->bus_name = dev->slot_name; usb_register_bus(uhci->bus); diff --git a/drivers/usb/host/usb-ohci.c b/drivers/usb/host/usb-ohci.c index 9baf1b62b985efe777218dcbb01b96aec44e2d09..433eaabbcd2250fad1e17eadfc50f25553396194 100644 --- a/drivers/usb/host/usb-ohci.c +++ b/drivers/usb/host/usb-ohci.c @@ -2400,6 +2400,7 @@ static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base) return NULL; } ohci->bus->hcpriv = (void *) ohci; + ohci->bus->bus_name = dev->slot_name; return ohci; } diff --git a/drivers/usb/host/usb-uhci.c b/drivers/usb/host/usb-uhci.c index 23d148434719dfa866a0730eab5e7ddc9aa1496e..55fea63c1715b6c006776021fd27b745cb92be15 100644 --- a/drivers/usb/host/usb-uhci.c +++ b/drivers/usb/host/usb-uhci.c @@ -2977,6 +2977,7 @@ _static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_ s->bus = bus; bus->hcpriv = s; + bus->bus_name = dev->slot_name; /* UHCI specs says devices must have 2 ports, but goes on to say */ /* they may have more but give no way to determine how many they */ diff --git a/drivers/usb/image/Config.help b/drivers/usb/image/Config.help index 0e9728c990efe437ca838646962ddf12bfe2973c..46666cdbbd65314664d93624576eead9ea1b0341 100644 --- a/drivers/usb/image/Config.help +++ b/drivers/usb/image/Config.help @@ -1,14 +1,3 @@ -CONFIG_USB_DC2XX - Say Y here if you want to connect this type of still camera to your - computer's USB port. See <file:Documentation/usb/dc2xx.txt> for - more information; some non-Kodak cameras may also work with this - driver, given application support (such as <http://www.gphoto.org/>). - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called dc2xx.o. If you want to compile it as a - module, say M here and read <file:Documentation/modules.txt>. - CONFIG_USB_MDC800 Say Y here if you want to connect this type of still camera to your computer's USB port. This driver can be used with gphoto 0.4.3 diff --git a/drivers/usb/image/Config.in b/drivers/usb/image/Config.in index 8ae0266caf3d0797869c015aed46a2741e86ea0c..e9e5c35235f50069b419efb166da52808a1a6e82 100644 --- a/drivers/usb/image/Config.in +++ b/drivers/usb/image/Config.in @@ -2,7 +2,6 @@ # USB Imageing devices configuration # comment 'USB Imaging devices' -dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI @@ -10,10 +9,10 @@ dep_tristate ' HP53xx USB scanner support (EXPERIMENTAL)' CONFIG_USB_HPUSBSCSI # Turn on CONFIG_USB_IMAGE if any of the drivers are compiled into the kernel to # make our Makefile logic a bit simpler -if [ "$CONFIG_USB_DC2XX" = "y" -o "$CONFIG_USB_MDC800" = "y" ]; then +if [ "$CONFIG_USB_MDC800" = "y" -o "$CONFIG_USB_SCANNER" = "y" ]; then define_bool CONFIG_USB_IMAGE y fi -if [ "$CONFIG_USB_SCANNER" = "y" -o "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then +if [ "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then define_bool CONFIG_USB_IMAGE y fi diff --git a/drivers/usb/image/Makefile b/drivers/usb/image/Makefile index ee46e08e411c7fc17bdd6922d543351e12a6389b..5ce0087855ccf0a2bd5ea45c3cee97c8bf2cba89 100644 --- a/drivers/usb/image/Makefile +++ b/drivers/usb/image/Makefile @@ -4,7 +4,6 @@ O_TARGET := usb-image.o -obj-$(CONFIG_USB_DC2XX) += dc2xx.o obj-$(CONFIG_USB_MDC800) += mdc800.o obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o obj-$(CONFIG_USB_MICROTEK) += microtek.o diff --git a/drivers/usb/image/dc2xx.c b/drivers/usb/image/dc2xx.c deleted file mode 100644 index 08c7485b537dd0ffdeac731f28b1a227615c1220..0000000000000000000000000000000000000000 --- a/drivers/usb/image/dc2xx.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright (C) 1999-2000 by David Brownell <dbrownell@users.sourceforge.net> - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -/* - * USB driver for Kodak DC-2XX series digital still cameras - * - * The protocol here is the same as the one going over a serial line, but - * it uses USB for speed. Set up /dev/kodak, get gphoto (www.gphoto.org), - * and have fun! - * - * This should also work for a number of other digital (non-Kodak) cameras, - * by adding the vendor and product IDs to the table below. They'll need - * to be the sort using USB just as a fast bulk data channel. - */ - -/* - * HISTORY - * - * 26 August, 1999 -- first release (0.1), works with my DC-240. - * The DC-280 (2Mpixel) should also work, but isn't tested. - * If you use gphoto, make sure you have the USB updates. - * Lives in a 2.3.14 or so Linux kernel, in drivers/usb. - * 31 August, 1999 -- minor update to recognize DC-260 and handle - * its endpoints being in a different order. Note that as - * of gPhoto 0.36pre, the USB updates are integrated. - * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0); - * added timeouts to bulk_msg calls. Minor updates, docs. - * 03 Nov, 1999 -- update for 2.3.25 kernel API changes. - * 08 Jan, 2000 .. multiple camera support - * 12 Aug, 2000 .. add some real locking, remove an Oops - * 10 Oct, 2000 .. usb_device_id table created. - * 01 Nov, 2000 .. usb_device_id support added by Adam J. Richter - * 08 Apr, 2001 .. Identify version on module load. gb - * - * Thanks to: the folk who've provided USB product IDs, sent in - * patches, and shared their successes! - */ - -#include <linux/config.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/random.h> -#include <linux/poll.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/devfs_fs_kernel.h> - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif -#include <linux/usb.h> - - -/* /dev/usb dir. */ -extern devfs_handle_t usb_devfs_handle; - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.0.0" -#define DRIVER_AUTHOR "David Brownell, <dbrownell@users.sourceforge.net>" -#define DRIVER_DESC "USB Camera Driver for Kodak DC-2xx series cameras" - - -/* current USB framework handles max of 16 USB devices per driver */ -#define MAX_CAMERAS 16 - -/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */ -#define USB_CAMERA_MINOR_BASE 80 - - -// XXX remove packet size limit, now that bulk transfers seem fixed - -/* Application protocol limit is 0x8002; USB has disliked that limit! */ -#define MAX_PACKET_SIZE 0x2000 /* e.g. image downloading */ - -#define MAX_READ_RETRY 5 /* times to retry reads */ -#define MAX_WRITE_RETRY 5 /* times to retry writes */ -#define RETRY_TIMEOUT (HZ) /* sleep between retries */ - - -/* table of cameras that work through this driver */ -static struct usb_device_id camera_table [] = { - /* These have the same application level protocol */ - { USB_DEVICE(0x040a, 0x0120) }, // Kodak DC-240 - { USB_DEVICE(0x040a, 0x0130) }, // Kodak DC-280 - { USB_DEVICE(0x040a, 0x0131) }, // Kodak DC-5000 - { USB_DEVICE(0x040a, 0x0132) }, // Kodak DC-3400 - - /* These have a different application level protocol which - * is part of the Flashpoint "DigitaOS". That supports some - * non-camera devices, and some non-Kodak cameras. - * Use this driver to get USB and "OpenDis" to talk. - */ - { USB_DEVICE(0x040a, 0x0100) }, // Kodak DC-220 - { USB_DEVICE(0x040a, 0x0110) }, // Kodak DC-260 - { USB_DEVICE(0x040a, 0x0111) }, // Kodak DC-265 - { USB_DEVICE(0x040a, 0x0112) }, // Kodak DC-290 - { USB_DEVICE(0xf003, 0x6002) }, // HP PhotoSmart C500 - { USB_DEVICE(0x03f0, 0x4102) }, // HP PhotoSmart C618 - { USB_DEVICE(0x0a17, 0x1001) }, // Pentax EI-200 - - /* Other USB devices may well work here too, so long as they - * just stick to half duplex bulk packet exchanges. That - * means, among other things, no iso or interrupt endpoints. - */ - - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, camera_table); - - -struct camera_state { - struct usb_device *dev; /* USB device handle */ - int inEP; /* read endpoint */ - int outEP; /* write endpoint */ - const struct usb_device_id *info; /* DC-240, etc */ - int subminor; /* which minor dev #? */ - struct semaphore sem; /* locks this struct */ - - /* this is non-null iff the device is open */ - char *buf; /* buffer for I/O */ - - devfs_handle_t devfs; /* devfs device */ - - /* always valid */ - wait_queue_head_t wait; /* for timed waits */ -}; - -/* Support multiple cameras, possibly of different types. */ -static struct camera_state *minor_data [MAX_CAMERAS]; - -/* make this an rwlock if contention becomes an issue */ -static DECLARE_MUTEX (state_table_mutex); - -static ssize_t camera_read (struct file *file, - char *buf, size_t len, loff_t *ppos) -{ - struct camera_state *camera; - int retries; - int retval = 0; - - if (len > MAX_PACKET_SIZE) - return -EINVAL; - - camera = (struct camera_state *) file->private_data; - down (&camera->sem); - if (!camera->dev) { - up (&camera->sem); - return -ENODEV; - } - - /* Big reads are common, for image downloading. Smaller ones - * are also common (even "directory listing" commands don't - * send very much data). We preserve packet boundaries here, - * they matter in the application protocol. - */ - for (retries = 0; retries < MAX_READ_RETRY; retries++) { - int count; - - if (signal_pending (current)) { - retval = -EINTR; - break; - } - - retval = usb_bulk_msg (camera->dev, - usb_rcvbulkpipe (camera->dev, camera->inEP), - camera->buf, len, &count, HZ*10); - - dbg ("read (%Zd) - 0x%x %d", len, retval, count); - - if (!retval) { - if (copy_to_user (buf, camera->buf, count)) - retval = -EFAULT; - else - retval = count; - break; - } - if (retval != -ETIMEDOUT) - break; - interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT); - - dbg ("read (%Zd) - retry", len); - } - up (&camera->sem); - return retval; -} - -static ssize_t camera_write (struct file *file, - const char *buf, size_t len, loff_t *ppos) -{ - struct camera_state *camera; - ssize_t bytes_written = 0; - - if (len > MAX_PACKET_SIZE) - return -EINVAL; - - camera = (struct camera_state *) file->private_data; - down (&camera->sem); - if (!camera->dev) { - up (&camera->sem); - return -ENODEV; - } - - /* most writes will be small: simple commands, sometimes with - * parameters. putting images (like borders) into the camera - * would be the main use of big writes. - */ - while (len > 0) { - char *obuf = camera->buf; - int maxretry = MAX_WRITE_RETRY; - unsigned long copy_size, thistime; - - /* it's not clear that retrying can do any good ... or that - * fragmenting application packets into N writes is correct. - */ - thistime = copy_size = len; - if (copy_from_user (obuf, buf, copy_size)) { - bytes_written = -EFAULT; - break; - } - while (thistime) { - int result; - int count; - - if (signal_pending (current)) { - if (!bytes_written) - bytes_written = -EINTR; - goto done; - } - - result = usb_bulk_msg (camera->dev, - usb_sndbulkpipe (camera->dev, camera->outEP), - obuf, thistime, &count, HZ*10); - - if (result) - dbg ("write USB err - %d", result); - - if (count) { - obuf += count; - thistime -= count; - maxretry = MAX_WRITE_RETRY; - continue; - } else if (!result) - break; - - if (result == -ETIMEDOUT) { /* NAK - delay a bit */ - if (!maxretry--) { - if (!bytes_written) - bytes_written = -ETIME; - goto done; - } - interruptible_sleep_on_timeout (&camera->wait, - RETRY_TIMEOUT); - continue; - } - if (!bytes_written) - bytes_written = -EIO; - goto done; - } - bytes_written += copy_size; - len -= copy_size; - buf += copy_size; - } -done: - up (&camera->sem); - dbg ("wrote %Zd", bytes_written); - return bytes_written; -} - -static int camera_open (struct inode *inode, struct file *file) -{ - struct camera_state *camera = NULL; - int subminor; - int value = 0; - - down (&state_table_mutex); - subminor = minor (inode->i_rdev) - USB_CAMERA_MINOR_BASE; - if (subminor < 0 || subminor >= MAX_CAMERAS - || !(camera = minor_data [subminor])) { - up (&state_table_mutex); - return -ENODEV; - } - down (&camera->sem); - up (&state_table_mutex); - - if (camera->buf) { - value = -EBUSY; - goto done; - } - - if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) { - value = -ENOMEM; - goto done; - } - - dbg ("open #%d", subminor); - - file->private_data = camera; -done: - up (&camera->sem); - return value; -} - -static int camera_release (struct inode *inode, struct file *file) -{ - struct camera_state *camera; - int subminor; - - camera = (struct camera_state *) file->private_data; - down (&state_table_mutex); - down (&camera->sem); - - if (camera->buf) { - kfree (camera->buf); - camera->buf = 0; - } - subminor = camera->subminor; - - /* If camera was unplugged with open file ... */ - if (!camera->dev) { - minor_data [subminor] = NULL; - kfree (camera); - } else - up (&camera->sem); - - up (&state_table_mutex); - - dbg ("close #%d", subminor); - - return 0; -} - - /* XXX should define some ioctls to expose camera type - * to applications ... what USB exposes should suffice. - * apps should be able to see the camera type. - */ -static /* const */ struct file_operations usb_camera_fops = { - /* Uses GCC initializer extension; simpler to maintain */ - owner: THIS_MODULE, - read: camera_read, - write: camera_write, - open: camera_open, - release: camera_release, -}; - - - -static void * -camera_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info) -{ - int i; - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - int direction, ep; - char name[8]; - struct camera_state *camera = NULL; - - - /* these have one config, one interface */ - if (dev->descriptor.bNumConfigurations != 1 - || dev->config[0].bNumInterfaces != 1) { - dbg ("Bogus camera config info"); - return NULL; - } - - /* models differ in how they report themselves */ - interface = &dev->actconfig->interface[ifnum].altsetting[0]; - if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE - && interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC) - || interface->bInterfaceSubClass != 0 - || interface->bInterfaceProtocol != 0 - || interface->bNumEndpoints != 2 - ) { - dbg ("Bogus camera interface info"); - return NULL; - } - - - /* select "subminor" number (part of a minor number) */ - down (&state_table_mutex); - for (i = 0; i < MAX_CAMERAS; i++) { - if (!minor_data [i]) - break; - } - if (i >= MAX_CAMERAS) { - info ("Ignoring additional USB Camera"); - goto bye; - } - - /* allocate & init camera state */ - camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL); - if (!camera) { - err ("no memory!"); - goto bye; - } - - init_MUTEX (&camera->sem); - camera->info = camera_info; - camera->subminor = i; - camera->buf = NULL; - init_waitqueue_head (&camera->wait); - - - /* get input and output endpoints (either order) */ - endpoint = interface->endpoint; - camera->outEP = camera->inEP = -1; - - ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK; - if (direction == USB_DIR_IN) - camera->inEP = ep; - else - camera->outEP = ep; - - ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK; - if (direction == USB_DIR_IN) - camera->inEP = ep; - else - camera->outEP = ep; - - if (camera->outEP == -1 || camera->inEP == -1 - || endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK - || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK - ) { - dbg ("Bogus endpoints"); - goto error; - } - - info ("USB Camera #%d connected, major/minor %d/%d", camera->subminor, - USB_MAJOR, USB_CAMERA_MINOR_BASE + camera->subminor); - - camera->dev = dev; - usb_inc_dev_use (dev); - - /* If we have devfs, register the device */ - sprintf(name, "dc2xx%d", camera->subminor); - camera->devfs = devfs_register(usb_devfs_handle, name, - DEVFS_FL_DEFAULT, USB_MAJOR, - USB_CAMERA_MINOR_BASE + camera->subminor, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | - S_IWGRP, &usb_camera_fops, NULL); - - goto bye; - -error: - minor_data [camera->subminor] = NULL; - kfree (camera); - camera = NULL; -bye: - up (&state_table_mutex); - return camera; -} - -static void camera_disconnect(struct usb_device *dev, void *ptr) -{ - struct camera_state *camera = (struct camera_state *) ptr; - int subminor = camera->subminor; - - down (&state_table_mutex); - down (&camera->sem); - - devfs_unregister(camera->devfs); - - /* If camera's not opened, we can clean up right away. - * Else apps see a disconnect on next I/O; the release cleans. - */ - if (!camera->buf) { - minor_data [subminor] = NULL; - kfree (camera); - camera = NULL; - } else - camera->dev = NULL; - - info ("USB Camera #%d disconnected", subminor); - usb_dec_dev_use (dev); - - if (camera != NULL) - up (&camera->sem); - up (&state_table_mutex); -} - -static /* const */ struct usb_driver camera_driver = { - name: "dc2xx", - - id_table: camera_table, - probe: camera_probe, - disconnect: camera_disconnect, - - fops: &usb_camera_fops, - minor: USB_CAMERA_MINOR_BASE -}; - - -int __init usb_dc2xx_init(void) -{ - if (usb_register (&camera_driver) < 0) - return -1; - info(DRIVER_VERSION ":" DRIVER_DESC); - return 0; -} - -void __exit usb_dc2xx_cleanup(void) -{ - usb_deregister (&camera_driver); -} - -module_init (usb_dc2xx_init); -module_exit (usb_dc2xx_cleanup); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c index 8e3dcf1c93177944d2d0470f88125f3eb25d9ee1..7da934cc347ca2a4fd3c14a1c6d99b1d0d75b342 100644 --- a/drivers/usb/misc/tiglusb.c +++ b/drivers/usb/misc/tiglusb.c @@ -33,7 +33,7 @@ /* * Version Information */ -#define DRIVER_VERSION "1.02" +#define DRIVER_VERSION "1.03" #define DRIVER_AUTHOR "Romain Lievin <roms@lpg.ticalc.org> & Julien Blache <jb@jblache.org>" #define DRIVER_DESC "TI-GRAPH LINK USB (aka SilverLink) driver" #define DRIVER_LICENSE "GPL" @@ -383,6 +383,7 @@ static void tiglusb_disconnect (struct usb_device *dev, void *drv_context) wake_up (&s->wait); if (s->state == _started) sleep_on (&s->remove_ok); + down (&s->mutex); s->dev = NULL; s->opened = 0; @@ -479,7 +480,7 @@ static void __exit tiglusb_cleanup (void) /* --------------------------------------------------------------------- */ -__setup ("tipar=", tiglusb_setup); +__setup ("tiusb=", tiglusb_setup); module_init (tiglusb_init); module_exit (tiglusb_cleanup); diff --git a/drivers/usb/net/Config.in b/drivers/usb/net/Config.in index eb5d55b4444b8e52e343701ea8f7c9f22fb42c82..695afcfc359931f493e67c81908d4e1a397c4a9c 100644 --- a/drivers/usb/net/Config.in +++ b/drivers/usb/net/Config.in @@ -7,10 +7,10 @@ if [ "$CONFIG_NET" = "n" ]; then else dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB CDC Ethernet support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB Pegasus/Pegasus-II based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB KLSI KL5USB101-based ethernet device support' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET + dep_tristate ' USB Pegasus/Pegasus-II based ethernet device support' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET dep_tristate ' USB RTL8150 based ethernet device support (EXPERIMENTAL)' CONFIG_USB_RTL8150 $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL - dep_tristate ' USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB-to-USB Networking cable device support' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET fi # Turn on CONFIG_USB_NET if any of the drivers are compiled into the kernel to diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index b6c2b2e7e515483f1d4ac2539bffe7eb12ebf0e9..0b2336935381dc1a9500f612d06bb3b1dc9d59d5 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -278,7 +278,7 @@ static void catc_rx_done(struct urb *urb) atomic_dec(&catc->recq_sz); dbg("getting extra packet"); urb->dev = catc->usbdev; - if ((status = usb_submit_urb(urb, GFP_KERNEL)) < 0) { + if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { dbg("submit(rx_urb) status %d", status); } } else { @@ -329,7 +329,7 @@ static void catc_irq_done(struct urb *urb) atomic_inc(&catc->recq_sz); } else { catc->rx_urb->dev = catc->usbdev; - if ((status = usb_submit_urb(catc->rx_urb, GFP_KERNEL)) < 0) { + if ((status = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) { err("submit(rx_urb) status %d", status); } } @@ -351,7 +351,7 @@ static void catc_tx_run(struct catc *catc) catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx]; catc->tx_urb->dev = catc->usbdev; - if ((status = usb_submit_urb(catc->tx_urb, GFP_KERNEL)) < 0) + if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0) err("submit(tx_urb), status %d", status); catc->tx_idx = !catc->tx_idx; @@ -655,7 +655,6 @@ static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) { struct catc *catc = dev->priv; u32 cmd; - char tmp[40]; if (get_user(cmd, (u32 *)useraddr)) return -EFAULT; @@ -666,8 +665,7 @@ static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; strncpy(info.driver, SHORT_DRIVER_DESC, ETHTOOL_BUSINFO_LEN); strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "usb%d:%d", catc->usbdev->bus->busnum, catc->usbdev->devnum); - strncpy(info.bus_info, tmp,ETHTOOL_BUSINFO_LEN); + usb_make_path (catc->usbdev, info.bus_info, sizeof info.bus_info); if (copy_to_user(useraddr, &info, sizeof(info))) return -EFAULT; return 0; @@ -909,9 +907,9 @@ static void *catc_probe(struct usb_device *usbdev, unsigned int ifnum, const str f5u011_rxmode(catc, catc->rxmode); } dbg("Init done."); - printk(KERN_INFO "%s: %s USB Ethernet at usb%d:%d.%d, ", + printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s/%d, ", netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate", - usbdev->bus->busnum, usbdev->devnum, ifnum); + usbdev->bus->bus_name, usbdev->devpath, ifnum); for (i = 0; i < 5; i++) printk("%2.2x:", netdev->dev_addr[i]); printk("%2.2x.\n", netdev->dev_addr[i]); return catc; diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index f9af587bf9945fc7f9eb13e185f581de825b15b8..ade99f9c8af75e8c828b635ce4d0e435ca95f998 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -5,17 +5,18 @@ * (c) 2000 Interlan Communications * (c) 2000 Stephane Alnet * (C) 2001 Brad Hards + * (C) 2002 Oliver Neukum * * Original author: The Zapman <zapman@interlan.net> - * Inspired by, and much credit goes to Michael Rothwell + * Inspired by, and much credit goes to Michael Rothwell * <rothwell@interlan.net> for the test equipment, help, and patience * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. - * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki + * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki * for providing the firmware and driver resources. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2, or + * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -25,7 +26,7 @@ * * 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. + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************/ @@ -36,8 +37,8 @@ * Fix bugs from previous two steps * Snoop other OSs for any tricks we're not doing * SMP locking - * Reduce arbitrary timeouts - * Smart multicast support + * Reduce arbitrary timeouts + * Smart multicast support * Temporary MAC change support * Tunable SOFs parameter - ioctl()? * Ethernet stats collection @@ -99,8 +100,14 @@ #define KAWETH_SOFS_TO_WAIT 0x05 +#define INTBUFFERSIZE 4 -MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr> and Brad Hards <bhards@bigpond.net.au>"); +#define STATE_OFFSET 0 +#define STATE_MASK 0x40 +#define STATE_SHIFT 5 + + +MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr>, Brad Hards <bhards@bigpond.net.au> and Oliver Neukum <oliver@neukum.org>"); MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver"); MODULE_LICENSE("GPL"); @@ -118,21 +125,21 @@ int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, * usb_device_id ****************************************************************/ static struct usb_device_id usb_klsi_table[] = { - { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ + { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ { USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */ - { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ - { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ - { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ + { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ + { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ + { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ { USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */ { USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */ - { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ + { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ { USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */ { USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */ - { USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ - { USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ - { USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ - { USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ - { USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ + { USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ + { USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ + { USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ + { USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ + { USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ { USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */ { USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */ { USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */ @@ -144,9 +151,9 @@ static struct usb_device_id usb_klsi_table[] = { { USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */ { USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */ { USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */ - { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ - { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ - { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ + { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ + { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ + { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ { USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */ {} /* Null terminator */ }; @@ -200,17 +207,23 @@ struct kaweth_device spinlock_t device_lock; __u32 status; + int end; + int removed; + int suspend_lowmem; + int linkstate; struct usb_device *dev; struct net_device *net; - wait_queue_head_t control_wait; + wait_queue_head_t term_wait; struct urb *rx_urb; struct urb *tx_urb; - + struct urb *irq_urb; + __u8 firmware_buf[KAWETH_FIRMWARE_BUF_SIZE]; __u8 tx_buf[KAWETH_BUF_SIZE]; __u8 rx_buf[KAWETH_BUF_SIZE]; + __u8 intbuffer[INTBUFFERSIZE]; __u16 packet_filter_bitmap; struct kaweth_ethernet_configuration configuration; @@ -223,13 +236,13 @@ struct kaweth_device * kaweth_control ****************************************************************/ static int kaweth_control(struct kaweth_device *kaweth, - unsigned int pipe, - __u8 request, - __u8 requesttype, - __u16 value, + unsigned int pipe, + __u8 request, + __u8 requesttype, + __u16 value, __u16 index, - void *data, - __u16 size, + void *data, + __u16 size, int timeout) { struct usb_ctrlrequest *dr; @@ -247,7 +260,7 @@ static int kaweth_control(struct kaweth_device *kaweth, kaweth_dbg("kmalloc() failed"); return -ENOMEM; } - + dr->bRequestType= requesttype; dr->bRequest = request; dr->wValue = cpu_to_le16p(&value); @@ -354,19 +367,19 @@ static int kaweth_set_receive_filter(struct kaweth_device *kaweth, /**************************************************************** * kaweth_download_firmware ****************************************************************/ -static int kaweth_download_firmware(struct kaweth_device *kaweth, - __u8 *data, +static int kaweth_download_firmware(struct kaweth_device *kaweth, + __u8 *data, __u16 data_len, __u8 interrupt, __u8 type) -{ +{ if(data_len > KAWETH_FIRMWARE_BUF_SIZE) { kaweth_err("Firmware too big: %d", data_len); return -ENOSPC; } - + memcpy(kaweth->firmware_buf, data, data_len); - + kaweth->firmware_buf[2] = (data_len & 0xFF) - 7; kaweth->firmware_buf[3] = data_len >> 8; kaweth->firmware_buf[4] = type; @@ -375,8 +388,8 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth, kaweth_dbg("High: %i, Low:%i", kaweth->firmware_buf[3], kaweth->firmware_buf[2]); - kaweth_dbg("Downloading firmware at %p to kaweth device at %p", - data, + kaweth_dbg("Downloading firmware at %p to kaweth device at %p", + data, kaweth); kaweth_dbg("Firmware length: %d", data_len); @@ -405,7 +418,7 @@ static int kaweth_trigger_firmware(struct kaweth_device *kaweth, kaweth->firmware_buf[5] = interrupt; kaweth->firmware_buf[6] = 0x00; kaweth->firmware_buf[7] = 0x00; - + kaweth_dbg("Triggering firmware"); return kaweth_control(kaweth, @@ -429,12 +442,12 @@ static int kaweth_reset(struct kaweth_device *kaweth) kaweth_dbg("kaweth_reset(%p)", kaweth); result = kaweth_control(kaweth, usb_sndctrlpipe(kaweth->dev, 0), - USB_REQ_SET_CONFIGURATION, - 0, + USB_REQ_SET_CONFIGURATION, + 0, kaweth->dev->config[0].bConfigurationValue, - 0, - NULL, - 0, + 0, + NULL, + 0, KAWETH_CONTROL_TIMEOUT); udelay(10000); @@ -445,17 +458,40 @@ static int kaweth_reset(struct kaweth_device *kaweth) } static void kaweth_usb_receive(struct urb *); +static void kaweth_resubmit_rx_urb(struct kaweth_device *, int); + +/**************************************************************** + int_callback +*****************************************************************/ +static void int_callback(struct urb *u) +{ + struct kaweth_device *kaweth = u->context; + int act_state; + + /* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */ + if (kaweth->suspend_lowmem) + kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); + + /* we check the link state to report changes */ + if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) { + if (!act_state) + netif_carrier_on(kaweth->net); + else + netif_carrier_off(kaweth->net); + + kaweth->linkstate = act_state; + } + +} /**************************************************************** * kaweth_resubmit_rx_urb ****************************************************************/ -static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, +static void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, int mem_flags) { int result; - memset(kaweth->rx_urb, 0, sizeof(*kaweth->rx_urb)); - FILL_BULK_URB(kaweth->rx_urb, kaweth->dev, usb_rcvbulkpipe(kaweth->dev, 1), @@ -465,7 +501,11 @@ static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, kaweth); if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) { + if (result == -ENOMEM) + kaweth->suspend_lowmem = 1; kaweth_err("resubmitting rx_urb %d failed", result); + } else { + kaweth->suspend_lowmem = 0; } } @@ -478,23 +518,30 @@ static void kaweth_usb_receive(struct urb *urb) { struct kaweth_device *kaweth = urb->context; struct net_device *net = kaweth->net; - + int count = urb->actual_length; int count2 = urb->transfer_buffer_length; - + __u16 pkt_len = le16_to_cpup((u16 *)kaweth->rx_buf); struct sk_buff *skb; - if(kaweth->status & KAWETH_STATUS_CLOSING) { + if(unlikely(urb->status == -ECONNRESET || urb->status == -ECONNABORTED)) + /* we are killed - set a flag and wake the disconnect handler */ + { + kaweth->end = 1; + wake_up(&kaweth->term_wait); return; } - - if(urb->status && urb->status != -EREMOTEIO && count != 1) { + + if (kaweth->status & KAWETH_STATUS_CLOSING) + return; + + if(urb->status && urb->status != -EREMOTEIO && count != 1) { kaweth_err("%s RX status: %d count: %d packet_len: %d", - net->name, + net->name, urb->status, - count, + count, (int)pkt_len); kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); return; @@ -508,7 +555,7 @@ static void kaweth_usb_receive(struct urb *urb) kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); return; } - + if(!(skb = dev_alloc_skb(pkt_len+2))) { kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); return; @@ -517,13 +564,13 @@ static void kaweth_usb_receive(struct urb *urb) skb->dev = net; eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0); - + skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, net); - + netif_rx(skb); - + kaweth->stats.rx_packets++; kaweth->stats.rx_bytes += pkt_len; } @@ -546,6 +593,18 @@ static int kaweth_open(struct net_device *net) kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); + FILL_INT_URB( + kaweth->irq_urb, + kaweth->dev, + usb_rcvintpipe(kaweth->dev, 3), + kaweth->intbuffer, + INTBUFFERSIZE, + int_callback, + kaweth, + HZ/4); + + usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); + netif_start_queue(net); kaweth_async_set_rx_mode(kaweth); @@ -563,6 +622,7 @@ static int kaweth_close(struct net_device *net) kaweth->status |= KAWETH_STATUS_CLOSING; + usb_unlink_urb(kaweth->irq_urb); usb_unlink_urb(kaweth->rx_urb); kaweth->status &= ~KAWETH_STATUS_CLOSING; @@ -602,11 +662,18 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) { struct kaweth_device *kaweth = net->priv; int count = skb->len; - + int res; spin_lock(&kaweth->device_lock); + if (kaweth->removed) { + /* our device is undergoing disconnection - we bail out */ + spin_unlock(&kaweth->device_lock); + dev_kfree_skb(skb); + return 0; + } + kaweth_async_set_rx_mode(kaweth); netif_stop_queue(net); @@ -614,8 +681,6 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) memcpy(kaweth->tx_buf + 2, skb->data, skb->len); - memset(kaweth->tx_urb, 0, sizeof(*kaweth->tx_urb)); - FILL_BULK_URB(kaweth->tx_urb, kaweth->dev, usb_sndbulkpipe(kaweth->dev, 2), @@ -623,15 +688,17 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) count + 2, kaweth_usb_transmit_complete, kaweth); + kaweth->end = 0; + kaweth->tx_urb->transfer_flags |= USB_ASYNC_UNLINK; if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC))) { kaweth_warn("kaweth failed tx_urb %d", res); kaweth->stats.tx_errors++; - + netif_start_queue(net); - } - else + } + else { kaweth->stats.tx_packets++; kaweth->stats.tx_bytes += skb->len; @@ -651,7 +718,7 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) static void kaweth_set_rx_mode(struct net_device *net) { struct kaweth_device *kaweth = net->priv; - + __u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED | KAWETH_PACKET_FILTER_BROADCAST | KAWETH_PACKET_FILTER_MULTICAST; @@ -662,7 +729,7 @@ static void kaweth_set_rx_mode(struct net_device *net) if (net->flags & IFF_PROMISC) { packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS; - } + } else if ((net->mc_count) || (net->flags & IFF_ALLMULTI)) { packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST; } @@ -677,7 +744,7 @@ static void kaweth_set_rx_mode(struct net_device *net) static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) { __u16 packet_filter_bitmap = kaweth->packet_filter_bitmap; - kaweth->packet_filter_bitmap = 0; + kaweth->packet_filter_bitmap = 0; if(packet_filter_bitmap == 0) return; { @@ -737,14 +804,14 @@ static void *kaweth_probe( int result = 0; kaweth_dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x", - dev->devnum, - (int)dev->descriptor.idVendor, + dev->devnum, + (int)dev->descriptor.idVendor, (int)dev->descriptor.idProduct, (int)dev->descriptor.bcdDevice); kaweth_dbg("Device at %p", dev); - kaweth_dbg("Descriptor length: %x type: %x", + kaweth_dbg("Descriptor length: %x type: %x", (int)dev->descriptor.bLength, (int)dev->descriptor.bDescriptorType); @@ -757,7 +824,8 @@ static void *kaweth_probe( kaweth->dev = dev; spin_lock_init(&kaweth->device_lock); - + init_waitqueue_head(&kaweth->term_wait); + kaweth_dbg("Resetting."); kaweth_reset(kaweth); @@ -772,20 +840,20 @@ static void *kaweth_probe( } else { /* Download the firmware */ kaweth_info("Downloading firmware..."); - if ((result = kaweth_download_firmware(kaweth, - kaweth_new_code, - len_kaweth_new_code, - 100, + if ((result = kaweth_download_firmware(kaweth, + kaweth_new_code, + len_kaweth_new_code, + 100, 2)) < 0) { kaweth_err("Error downloading firmware (%d)", result); kfree(kaweth); return NULL; } - if ((result = kaweth_download_firmware(kaweth, - kaweth_new_code_fix, - len_kaweth_new_code_fix, - 100, + if ((result = kaweth_download_firmware(kaweth, + kaweth_new_code_fix, + len_kaweth_new_code_fix, + 100, 3)) < 0) { kaweth_err("Error downloading firmware fix (%d)", result); kfree(kaweth); @@ -845,7 +913,7 @@ static void *kaweth_probe( (int)kaweth->configuration.hw_addr[5]); if(!memcmp(&kaweth->configuration.hw_addr, - &bcast_addr, + &bcast_addr, sizeof(bcast_addr))) { kaweth_err("Firmware not functioning properly, no net device created"); kfree(kaweth); @@ -856,13 +924,13 @@ static void *kaweth_probe( kaweth_dbg("Error setting URB size"); return kaweth; } - + if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) { kaweth_err("Error setting SOFS wait"); return kaweth; } - result = kaweth_set_receive_filter(kaweth, + result = kaweth_set_receive_filter(kaweth, KAWETH_PACKET_FILTER_DIRECTED | KAWETH_PACKET_FILTER_BROADCAST | KAWETH_PACKET_FILTER_MULTICAST); @@ -871,11 +939,18 @@ static void *kaweth_probe( kaweth_err("Error setting receive filter"); return kaweth; } - + kaweth_dbg("Initializing net device."); kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kaweth->tx_urb) + goto err_no_urb; kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kaweth->rx_urb) + goto err_only_tx; + kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kaweth->irq_urb) + goto err_tx_and_rx; kaweth->net = init_etherdev(0, 0); if (!kaweth->net) { @@ -884,17 +959,17 @@ static void *kaweth_probe( } memcpy(kaweth->net->broadcast, &bcast_addr, sizeof(bcast_addr)); - memcpy(kaweth->net->dev_addr, + memcpy(kaweth->net->dev_addr, &kaweth->configuration.hw_addr, sizeof(kaweth->configuration.hw_addr)); - + kaweth->net->priv = kaweth; kaweth->net->open = kaweth_open; kaweth->net->stop = kaweth_close; kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT; kaweth->net->tx_timeout = kaweth_tx_timeout; - + kaweth->net->do_ioctl = kaweth_ioctl; kaweth->net->hard_start_xmit = kaweth_start_xmit; kaweth->net->set_multicast_list = kaweth_set_rx_mode; @@ -904,10 +979,18 @@ static void *kaweth_probe( memset(&kaweth->stats, 0, sizeof(kaweth->stats)); kaweth_info("kaweth interface created at %s", kaweth->net->name); - + kaweth_dbg("Kaweth probe returning."); return kaweth; + +err_tx_and_rx: + usb_free_urb(kaweth->rx_urb); +err_only_tx: + usb_free_urb(kaweth->tx_urb); +err_no_urb: + kfree(kaweth); + return NULL; } /**************************************************************** @@ -923,9 +1006,20 @@ static void kaweth_disconnect(struct usb_device *dev, void *ptr) kaweth_warn("unregistering non-existant device"); return; } - usb_unlink_urb(kaweth->tx_urb); + + kaweth->removed = 1; + usb_unlink_urb(kaweth->irq_urb); usb_unlink_urb(kaweth->rx_urb); + /* we need to wait for the urb to be cancelled, if it is active */ + spin_lock(&kaweth->device_lock); + if (usb_unlink_urb(kaweth->tx_urb) == -EINPROGRESS) { + spin_unlock(&kaweth->device_lock); + wait_event(kaweth->term_wait, kaweth->end); + } else { + spin_unlock(&kaweth->device_lock); + } + if(kaweth->net) { if(kaweth->net->flags & IFF_UP) { kaweth_dbg("Closing net device"); @@ -944,7 +1038,7 @@ static void kaweth_disconnect(struct usb_device *dev, void *ptr) // FIXME this completion stuff is a modified clone of -// an OLD version of some stuff in usb.c ... +// an OLD version of some stuff in usb.c ... struct usb_api_data { wait_queue_head_t wqh; int done; @@ -974,7 +1068,7 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) init_waitqueue_head(&awd.wqh); awd.done = 0; - + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&awd.wqh, &wait); urb->context = &awd; @@ -1021,7 +1115,7 @@ int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, int retv; int length; - urb = usb_alloc_urb(0, GFP_KERNEL); + urb = usb_alloc_urb(0, GFP_NOIO); if (!urb) return -ENOMEM; @@ -1065,3 +1159,4 @@ module_exit(kaweth_exit); + diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index b88a05e5b19b927f35b6106c13cffda5fdf3b583..67b458eb750ccc4511a5f0f85f60877dc60c469f 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -41,7 +41,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> @@ -59,7 +58,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.5.2 (2002/03/21)" +#define DRIVER_VERSION "v0.5.4 (2002/04/11)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -85,105 +84,103 @@ static struct usb_device_id pegasus_ids[] = { {match_flags: USB_DEVICE_ID_MATCH_DEVICE, idVendor:vid, idProduct:pid}, #include "pegasus.h" #undef PEGASUS_DEV - { } + {} }; - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_PARM(loopback, "i"); MODULE_PARM(mii_mode, "i"); MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); -MODULE_DEVICE_TABLE (usb, pegasus_ids); - +MODULE_DEVICE_TABLE(usb, pegasus_ids); -static int update_eth_regs_async( pegasus_t * ); +static int update_eth_regs_async(pegasus_t *); /* Aargh!!! I _really_ hate such tweaks */ -static void ctrl_callback( struct urb *urb ) +static void ctrl_callback(struct urb *urb) { - pegasus_t *pegasus = urb->context; + pegasus_t *pegasus = urb->context; - if ( !pegasus ) + if (!pegasus) return; - switch ( urb->status ) { - case 0: - if ( pegasus->flags & ETH_REGS_CHANGE ) { - pegasus->flags &= ~ETH_REGS_CHANGE; - pegasus->flags |= ETH_REGS_CHANGED; - update_eth_regs_async( pegasus ); - return; - } - break; - case -EINPROGRESS: + switch (urb->status) { + case 0: + if (pegasus->flags & ETH_REGS_CHANGE) { + pegasus->flags &= ~ETH_REGS_CHANGE; + pegasus->flags |= ETH_REGS_CHANGED; + update_eth_regs_async(pegasus); return; - case -ENOENT: - break; - default: - warn("%s: status %d", __FUNCTION__, urb->status); + } + break; + case -EINPROGRESS: + return; + case -ENOENT: + break; + default: + warn("%s: status %d", __FUNCTION__, urb->status); } pegasus->flags &= ~ETH_REGS_CHANGED; - wake_up(&pegasus->ctrl_wait ); + wake_up(&pegasus->ctrl_wait); } - -static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) +static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, + void *data) { - int ret; + int ret; unsigned char *buffer; DECLARE_WAITQUEUE(wait, current); - buffer = kmalloc(size,GFP_KERNEL); + buffer = kmalloc(size, GFP_KERNEL); if (!buffer) { err("unable to allocate memory for configuration descriptors"); return 0; } - memcpy(buffer,data,size); + memcpy(buffer, data, size); add_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); - while ( pegasus->flags & ETH_REGS_CHANGED ) + while (pegasus->flags & ETH_REGS_CHANGED) schedule(); remove_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_RUNNING); pegasus->dr.bRequestType = PEGASUS_REQT_READ; pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; - pegasus->dr.wValue = cpu_to_le16 (0); + pegasus->dr.wValue = cpu_to_le16(0); pegasus->dr.wIndex = cpu_to_le16p(&indx); pegasus->dr.wLength = cpu_to_le16p(&size); pegasus->ctrl_urb->transfer_buffer_length = size; - FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, - usb_rcvctrlpipe(pegasus->usb,0), - (char *)&pegasus->dr, - buffer, size, ctrl_callback, pegasus ); + FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb, + usb_rcvctrlpipe(pegasus->usb, 0), + (char *) &pegasus->dr, + buffer, size, ctrl_callback, pegasus); - add_wait_queue( &pegasus->ctrl_wait, &wait ); - set_current_state( TASK_UNINTERRUPTIBLE ); + add_wait_queue(&pegasus->ctrl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); /* using ATOMIC, we'd never wake up if we slept */ - if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) { + if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { err("%s: BAD CTRLs %d", __FUNCTION__, ret); goto out; } schedule(); out: - remove_wait_queue( &pegasus->ctrl_wait, &wait ); - memcpy(data,buffer,size); + remove_wait_queue(&pegasus->ctrl_wait, &wait); + memcpy(data, buffer, size); kfree(buffer); return ret; } - -static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) +static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, + void *data) { - int ret; + int ret; unsigned char *buffer; DECLARE_WAITQUEUE(wait, current); @@ -196,47 +193,46 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) add_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); - while ( pegasus->flags & ETH_REGS_CHANGED ) + while (pegasus->flags & ETH_REGS_CHANGED) schedule(); remove_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_RUNNING); pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; - pegasus->dr.wValue = cpu_to_le16 (0); - pegasus->dr.wIndex = cpu_to_le16p( &indx ); - pegasus->dr.wLength = cpu_to_le16p( &size ); + pegasus->dr.wValue = cpu_to_le16(0); + pegasus->dr.wIndex = cpu_to_le16p(&indx); + pegasus->dr.wLength = cpu_to_le16p(&size); pegasus->ctrl_urb->transfer_buffer_length = size; - FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, - usb_sndctrlpipe(pegasus->usb,0), - (char *)&pegasus->dr, - buffer, size, ctrl_callback, pegasus ); - - add_wait_queue( &pegasus->ctrl_wait, &wait ); - set_current_state( TASK_UNINTERRUPTIBLE ); + FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb, 0), + (char *) &pegasus->dr, + buffer, size, ctrl_callback, pegasus); - if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) { + add_wait_queue(&pegasus->ctrl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + + if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { err("%s: BAD CTRL %d", __FUNCTION__, ret); goto out; } - + schedule(); out: - remove_wait_queue( &pegasus->ctrl_wait, &wait ); + remove_wait_queue(&pegasus->ctrl_wait, &wait); kfree(buffer); - + return ret; } - -static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) +static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) { - int ret; + int ret; unsigned char *buffer; __u16 dat = data; DECLARE_WAITQUEUE(wait, current); - + buffer = kmalloc(1, GFP_KERNEL); if (!buffer) { err("unable to allocate memory for configuration descriptors"); @@ -246,129 +242,126 @@ static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) add_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); - while ( pegasus->flags & ETH_REGS_CHANGED ) + while (pegasus->flags & ETH_REGS_CHANGED) schedule(); remove_wait_queue(&pegasus->ctrl_wait, &wait); set_current_state(TASK_RUNNING); pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; - pegasus->dr.wValue = cpu_to_le16p( &dat); - pegasus->dr.wIndex = cpu_to_le16p( &indx ); - pegasus->dr.wLength = cpu_to_le16( 1 ); + pegasus->dr.wValue = cpu_to_le16p(&dat); + pegasus->dr.wIndex = cpu_to_le16p(&indx); + pegasus->dr.wLength = cpu_to_le16(1); pegasus->ctrl_urb->transfer_buffer_length = 1; - FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, - usb_sndctrlpipe(pegasus->usb,0), - (char *)&pegasus->dr, - buffer, 1, ctrl_callback, pegasus ); + FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb, 0), + (char *) &pegasus->dr, + buffer, 1, ctrl_callback, pegasus); - add_wait_queue( &pegasus->ctrl_wait, &wait ); - set_current_state( TASK_UNINTERRUPTIBLE ); + add_wait_queue(&pegasus->ctrl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); - if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) { + if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) { err("%s: BAD CTRL %d", __FUNCTION__, ret); goto out; } schedule(); out: - remove_wait_queue( &pegasus->ctrl_wait, &wait ); + remove_wait_queue(&pegasus->ctrl_wait, &wait); kfree(buffer); return ret; } - -static int update_eth_regs_async( pegasus_t *pegasus ) +static int update_eth_regs_async(pegasus_t * pegasus) { - int ret; + int ret; pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; pegasus->dr.wValue = 0; - pegasus->dr.wIndex = cpu_to_le16(EthCtrl0); + pegasus->dr.wIndex = cpu_to_le16(EthCtrl0); pegasus->dr.wLength = cpu_to_le16(3); pegasus->ctrl_urb->transfer_buffer_length = 3; - FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb, - usb_sndctrlpipe(pegasus->usb,0), - (char *)&pegasus->dr, - pegasus->eth_regs, 3, ctrl_callback, pegasus ); + FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb, 0), + (char *) &pegasus->dr, + pegasus->eth_regs, 3, ctrl_callback, pegasus); - if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) - err("%s: BAD CTRL %d, flgs %x",__FUNCTION__,ret,pegasus->flags); + if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) + err("%s: BAD CTRL %d, flgs %x", __FUNCTION__, ret, + pegasus->flags); - return ret; + return ret; } - -static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) +static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd) { - int i; - __u8 data[4] = { phy, 0, 0, indx }; - __u16 regdi; - - set_register( pegasus, PhyCtrl, 0 ); - set_registers( pegasus, PhyAddr, sizeof(data), data ); - set_register( pegasus, PhyCtrl, (indx | PHY_READ) ); + int i; + __u8 data[4] = { phy, 0, 0, indx }; + __u16 regdi; + + set_register(pegasus, PhyCtrl, 0); + set_registers(pegasus, PhyAddr, sizeof(data), data); + set_register(pegasus, PhyCtrl, (indx | PHY_READ)); for (i = 0; i < REG_TIMEOUT; i++) { get_registers(pegasus, PhyCtrl, 1, data); - if ( data[0] & PHY_DONE ) + if (data[0] & PHY_DONE) break; } - if ( i < REG_TIMEOUT ) { - get_registers( pegasus, PhyData, 2, ®di ); + if (i < REG_TIMEOUT) { + get_registers(pegasus, PhyData, 2, ®di); *regd = le16_to_cpu(regdi); - return 0; + return 0; } warn("%s: failed", __FUNCTION__); - + return 1; } - -static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) +static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd) { - int i; - __u8 data[4] = { phy, 0, 0, indx }; - - *(data + 1) = cpu_to_le16p( ®d ); - set_register( pegasus, PhyCtrl, 0 ); - set_registers( pegasus, PhyAddr, 4, data ); - set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) ); + int i; + __u8 data[4] = { phy, 0, 0, indx }; + + *(data + 1) = cpu_to_le16p(®d); + set_register(pegasus, PhyCtrl, 0); + set_registers(pegasus, PhyAddr, 4, data); + set_register(pegasus, PhyCtrl, (indx | PHY_WRITE)); for (i = 0; i < REG_TIMEOUT; i++) { get_registers(pegasus, PhyCtrl, 1, data); - if ( data[0] & PHY_DONE ) + if (data[0] & PHY_DONE) break; } - if ( i < REG_TIMEOUT ) - return 0; + if (i < REG_TIMEOUT) + return 0; warn("%s: failed", __FUNCTION__); return 1; } - -static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata ) +static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata) { - int i; + int i; __u8 tmp; __u16 retdatai; - - set_register( pegasus, EpromCtrl, 0 ); - set_register( pegasus, EpromOffset, index ); - set_register( pegasus, EpromCtrl, EPROM_READ); - for ( i=0; i < REG_TIMEOUT; i++ ) { - get_registers( pegasus, EpromCtrl, 1, &tmp ); - if ( tmp & EPROM_DONE ) + set_register(pegasus, EpromCtrl, 0); + set_register(pegasus, EpromOffset, index); + set_register(pegasus, EpromCtrl, EPROM_READ); + + for (i = 0; i < REG_TIMEOUT; i++) { + get_registers(pegasus, EpromCtrl, 1, &tmp); + if (tmp & EPROM_DONE) break; } - if ( i < REG_TIMEOUT ) { - get_registers( pegasus, EpromData, 2, &retdatai ); - *retdata = le16_to_cpu (retdatai); - return 0; + if (i < REG_TIMEOUT) { + get_registers(pegasus, EpromData, 2, &retdatai); + *retdata = le16_to_cpu(retdatai); + return 0; } warn("%s: failed", __FUNCTION__); @@ -376,355 +369,372 @@ static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata ) } #ifdef PEGASUS_WRITE_EEPROM -static inline void enable_eprom_write( pegasus_t *pegasus ) +static inline void enable_eprom_write(pegasus_t * pegasus) { - __u8 tmp; + __u8 tmp; - get_registers( pegasus, EthCtrl2, 1, &tmp ); - set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE ); + get_registers(pegasus, EthCtrl2, 1, &tmp); + set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); } - -static inline void disable_eprom_write( pegasus_t *pegasus ) +static inline void disable_eprom_write(pegasus_t * pegasus) { - __u8 tmp; + __u8 tmp; - get_registers( pegasus, EthCtrl2, 1, &tmp ); - set_register( pegasus, EpromCtrl, 0 ); - set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE ); + get_registers(pegasus, EthCtrl2, 1, &tmp); + set_register(pegasus, EpromCtrl, 0); + set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); } - -static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data ) +static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) { - int i, tmp; - __u8 d[4] = {0x3f, 0, 0, EPROM_WRITE}; - - set_registers( pegasus, EpromOffset, 4, d ); - enable_eprom_write( pegasus ); - set_register( pegasus, EpromOffset, index ); - set_registers( pegasus, EpromData, 2, &data ); - set_register( pegasus, EpromCtrl, EPROM_WRITE ); - - for ( i=0; i < REG_TIMEOUT; i++ ) { - get_registers( pegasus, EpromCtrl, 1, &tmp ); - if ( tmp & EPROM_DONE ) + int i, tmp; + __u8 d[4] = { 0x3f, 0, 0, EPROM_WRITE }; + + set_registers(pegasus, EpromOffset, 4, d); + enable_eprom_write(pegasus); + set_register(pegasus, EpromOffset, index); + set_registers(pegasus, EpromData, 2, &data); + set_register(pegasus, EpromCtrl, EPROM_WRITE); + + for (i = 0; i < REG_TIMEOUT; i++) { + get_registers(pegasus, EpromCtrl, 1, &tmp); + if (tmp & EPROM_DONE) break; } - disable_eprom_write( pegasus ); - if ( i < REG_TIMEOUT ) - return 0; + disable_eprom_write(pegasus); + if (i < REG_TIMEOUT) + return 0; warn("%s: failed", __FUNCTION__); - return -1; + return -1; } -#endif /* PEGASUS_WRITE_EEPROM */ +#endif /* PEGASUS_WRITE_EEPROM */ -static inline void get_node_id( pegasus_t *pegasus, __u8 *id ) +static inline void get_node_id(pegasus_t * pegasus, __u8 * id) { - int i; + int i; __u16 w16; - + for (i = 0; i < 3; i++) { - read_eprom_word( pegasus, i, &w16); - ((__u16 *) id)[i] = cpu_to_le16p (&w16); + read_eprom_word(pegasus, i, &w16); + ((__u16 *) id)[i] = cpu_to_le16p(&w16); } } - -static void set_ethernet_addr( pegasus_t *pegasus ) +static void set_ethernet_addr(pegasus_t * pegasus) { - __u8 node_id[6]; + __u8 node_id[6]; get_node_id(pegasus, node_id); - set_registers( pegasus, EthID, sizeof(node_id), node_id ); - memcpy( pegasus->net->dev_addr, node_id, sizeof(node_id) ); + set_registers(pegasus, EthID, sizeof(node_id), node_id); + memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id)); } - -static inline int reset_mac( pegasus_t *pegasus ) +static inline int reset_mac(pegasus_t * pegasus) { - __u8 data = 0x8; - int i; + __u8 data = 0x8; + int i; set_register(pegasus, EthCtrl1, data); for (i = 0; i < REG_TIMEOUT; i++) { get_registers(pegasus, EthCtrl1, 1, &data); if (~data & 0x08) { - if (loopback & 1) + if (loopback & 1) break; - if ( mii_mode && (pegasus->features & HAS_HOME_PNA) ) - set_register( pegasus, Gpio1, 0x34 ); + if (mii_mode && (pegasus->features & HAS_HOME_PNA)) + set_register(pegasus, Gpio1, 0x34); else - set_register( pegasus, Gpio1, 0x26 ); - set_register( pegasus, Gpio0, pegasus->features ); - set_register( pegasus, Gpio0, DEFAULT_GPIO_SET ); + set_register(pegasus, Gpio1, 0x26); + set_register(pegasus, Gpio0, pegasus->features); + set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); break; } } - if ( i == REG_TIMEOUT ) + if (i == REG_TIMEOUT) return 1; if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { - __u16 auxmode; - read_mii_word(pegasus, 1, MII_TPISTATUS, &auxmode); - write_mii_word(pegasus, 1, MII_TPISTATUS, auxmode | 4); + set_register(pegasus, Gpio0, 0x24); + set_register(pegasus, Gpio0, 0x26); } if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { - __u16 auxmode; + __u16 auxmode; read_mii_word(pegasus, 3, 0x1b, &auxmode); write_mii_word(pegasus, 3, 0x1b, auxmode | 4); } - return 0; + return 0; } - -static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) +static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) { - __u16 linkpart, bmsr; - __u8 data[4]; + __u16 linkpart, bmsr; + __u8 data[4]; pegasus_t *pegasus = dev->priv; - read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr); read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr); - if ( !(bmsr & 4) && !loopback ) - warn( "%s: link NOT established (%04x) - check the cable.", - dev->name, bmsr ); - if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) ) + if (!(bmsr & BMSR_LSTATUS) && !loopback) + warn("%s: link NOT established (%04x) - check the cable.", + dev->name, bmsr); + if (read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart)) return 2; - if ( !(linkpart & 1) ) - warn( "link partner stat %x", linkpart ); + if (!(linkpart & 1)) + warn("link partner stat %x", linkpart); data[0] = 0xc9; data[1] = 0; - if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL) ) - data[1] |= 0x20; /* set full duplex */ - if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF) ) - data[1] |= 0x10; /* set 100 Mbps */ - if ( mii_mode ) + if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) + data[1] |= 0x20; /* set full duplex */ + if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF)) + data[1] |= 0x10; /* set 100 Mbps */ + if (mii_mode) data[1] = 0; data[2] = (loopback & 1) ? 0x09 : 0x01; - memcpy( pegasus->eth_regs, data, sizeof(data) ); + memcpy(pegasus->eth_regs, data, sizeof(data)); + set_registers(pegasus, EthCtrl0, 3, data); - set_registers( pegasus, EthCtrl0, 3, data ); + if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || + usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { + u16 auxmode; + read_mii_word(pegasus, 0, 0x1b, &auxmode); + write_mii_word(pegasus, 0, 0x1b, auxmode | 4); + } return 0; } - -static void read_bulk_callback( struct urb *urb ) +static void read_bulk_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; struct net_device *net; - int count = urb->actual_length, res; + int count = urb->actual_length; int rx_status; - struct sk_buff *skb; + struct sk_buff *skb; __u16 pkt_len; - if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) ) + if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) return; net = pegasus->net; - if ( !netif_device_present(net) ) + if (!netif_device_present(net)) return; - if ( pegasus->flags & PEGASUS_RX_BUSY ) { - pegasus->stats.rx_errors++; - dbg("pegasus Rx busy"); - return; - } - pegasus->flags |= PEGASUS_RX_BUSY; - - switch ( urb->status ) { - case 0: - break; - case -ETIMEDOUT: - dbg( "reset MAC" ); - pegasus->flags &= ~PEGASUS_RX_BUSY; - break; - default: - dbg( "%s: RX status %d", net->name, urb->status ); - goto goon; + switch (urb->status) { + case 0: + break; + case -ETIMEDOUT: + dbg("reset MAC"); + pegasus->flags &= ~PEGASUS_RX_BUSY; + break; + default: + dbg("%s: RX status %d", net->name, urb->status); + goto goon; } - if ( !count ) + if (!count) goto goon; - rx_status = le32_to_cpu(*(int *)(pegasus->rx_buff + count - 4)); - if ( rx_status & 0x000e0000 ) { + rx_status = le32_to_cpu(*(int *)(urb->transfer_buffer + count - 4)); + if (rx_status & 0x000e0000) { dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000); pegasus->stats.rx_errors++; - if ( rx_status & 0x060000 ) + if (rx_status & 0x060000) pegasus->stats.rx_length_errors++; - if ( rx_status & 0x080000 ) + if (rx_status & 0x080000) pegasus->stats.rx_crc_errors++; - if ( rx_status & 0x100000 ) + if (rx_status & 0x100000) pegasus->stats.rx_frame_errors++; goto goon; } - pkt_len = (rx_status & 0xfff) - 8; - if ( !(skb = dev_alloc_skb(pkt_len+2)) ) - goto goon; + tasklet_schedule(&pegasus->rx_tl); + + if (!pegasus->rx_skb) + return; + skb_put(pegasus->rx_skb, pkt_len); + pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); + netif_rx(pegasus->rx_skb); + + if (!(skb = dev_alloc_skb(PEGASUS_MTU + 2))) { + pegasus->rx_skb = NULL; + return; + } + skb->dev = net; skb_reserve(skb, 2); - eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0); - skb_put(skb, pkt_len); - - skb->protocol = eth_type_trans(skb, net); - netif_rx(skb); + pegasus->rx_skb = skb; pegasus->stats.rx_packets++; pegasus->stats.rx_bytes += pkt_len; - goon: - FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, - usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_buff, PEGASUS_MAX_MTU, - read_bulk_callback, pegasus ); - if ( (res = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) ) - warn("%s: failed submint rx_urb %d", __FUNCTION__, res); - pegasus->flags &= ~PEGASUS_RX_BUSY; + FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, + usb_rcvbulkpipe(pegasus->usb, 1), + pegasus->rx_skb->data, PEGASUS_MTU + 8, + read_bulk_callback, pegasus); + if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) + pegasus->flags |= PEGASUS_RX_URB_FAIL; + else + pegasus->flags &= ~PEGASUS_RX_URB_FAIL; } +static void rx_fixup(unsigned long data) +{ + pegasus_t *pegasus; -static void write_bulk_callback( struct urb *urb ) + pegasus = (pegasus_t *)data; + + if (pegasus->flags & PEGASUS_RX_URB_FAIL) { + goto try_again; + } + if (pegasus->rx_skb) + return; + if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) { + tasklet_schedule(&pegasus->rx_tl); + return; + } + FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, + usb_rcvbulkpipe(pegasus->usb, 1), + pegasus->rx_skb->data, PEGASUS_MTU + 8, + read_bulk_callback, pegasus); +try_again: + if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) { + pegasus->flags |= PEGASUS_RX_URB_FAIL; + tasklet_schedule(&pegasus->rx_tl); + } else { + pegasus->flags &= ~PEGASUS_RX_URB_FAIL; + } +} + +static void write_bulk_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; - if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) ) + if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) return; - if ( !netif_device_present(pegasus->net) ) + if (!netif_device_present(pegasus->net)) return; - - if ( urb->status ) + + if (urb->status) info("%s: TX status %d", pegasus->net->name, urb->status); pegasus->net->trans_start = jiffies; - netif_wake_queue( pegasus->net ); + netif_wake_queue(pegasus->net); } #ifdef PEGASUS_USE_INTR -static void intr_callback( struct urb *urb ) +static void intr_callback(struct urb *urb) { pegasus_t *pegasus = urb->context; struct net_device *net; - __u8 *d; + __u8 *d; - if ( !pegasus ) + if (!pegasus) return; - - switch ( urb->status ) { - case 0: - break; - case -ENOENT: - return; - default: - info("intr status %d", urb->status); + + switch (urb->status) { + case 0: + break; + case -ENOENT: + return; + default: + info("intr status %d", urb->status); } d = urb->transfer_buffer; net = pegasus->net; - if ( d[0] & 0xfc ) { + if (d[0] & 0xfc) { pegasus->stats.tx_errors++; - if ( d[0] & TX_UNDERRUN ) + if (d[0] & TX_UNDERRUN) pegasus->stats.tx_fifo_errors++; - if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) ) + if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) pegasus->stats.tx_aborted_errors++; - if ( d[0] & LATE_COL ) + if (d[0] & LATE_COL) pegasus->stats.tx_window_errors++; - if ( d[0] & (NO_CARRIER | LOSS_CARRIER) ) + if (d[0] & (NO_CARRIER | LOSS_CARRIER)) pegasus->stats.tx_carrier_errors++; } } #endif -static void pegasus_tx_timeout( struct net_device *net ) +static void pegasus_tx_timeout(struct net_device *net) { pegasus_t *pegasus = net->priv; - if ( !pegasus ) + if (!pegasus) return; - + warn("%s: Tx timed out.", net->name); pegasus->tx_urb->transfer_flags |= USB_ASYNC_UNLINK; - usb_unlink_urb( pegasus->tx_urb ); + usb_unlink_urb(pegasus->tx_urb); pegasus->stats.tx_errors++; } - -static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) +static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) { - pegasus_t *pegasus = net->priv; - int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; - int res; + pegasus_t *pegasus = net->priv; + int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; + int res; __u16 l16 = skb->len; - - netif_stop_queue( net ); - - ((__u16 *)pegasus->tx_buff)[0] = cpu_to_le16( l16 ); - memcpy(pegasus->tx_buff+2, skb->data, skb->len); - FILL_BULK_URB( pegasus->tx_urb, pegasus->usb, - usb_sndbulkpipe(pegasus->usb, 2), - pegasus->tx_buff, PEGASUS_MAX_MTU, - write_bulk_callback, pegasus ); - pegasus->tx_urb->transfer_buffer_length = count; + + netif_stop_queue(net); + + ((__u16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); + memcpy(pegasus->tx_buff + 2, skb->data, skb->len); + FILL_BULK_URB(pegasus->tx_urb, pegasus->usb, + usb_sndbulkpipe(pegasus->usb, 2), + pegasus->tx_buff, count, + write_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { warn("failed tx_urb %d", res); pegasus->stats.tx_errors++; - netif_start_queue( net ); + netif_start_queue(net); } else { pegasus->stats.tx_packets++; pegasus->stats.tx_bytes += skb->len; net->trans_start = jiffies; } - dev_kfree_skb(skb); return 0; } - -static struct net_device_stats *pegasus_netdev_stats( struct net_device *dev ) +static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) { - return &((pegasus_t *)dev->priv)->stats; + return &((pegasus_t *) dev->priv)->stats; } - -static inline void disable_net_traffic( pegasus_t *pegasus ) +static inline void disable_net_traffic(pegasus_t * pegasus) { - int tmp=0; + int tmp = 0; - set_registers( pegasus, EthCtrl0, 2, &tmp ); + set_registers(pegasus, EthCtrl0, 2, &tmp); } - -static inline void get_interrupt_interval( pegasus_t *pegasus ) +static inline void get_interrupt_interval(pegasus_t * pegasus) { - __u8 data[2]; + __u8 data[2]; - read_eprom_word( pegasus, 4, (__u16 *)data ); - if ( data[1] < 0x80 ) { - info( "intr interval will be changed from %ums to %ums", - data[1], 0x80 ); + read_eprom_word(pegasus, 4, (__u16 *) data); + if (data[1] < 0x80) { + info("intr interval will be changed from %ums to %ums", + data[1], 0x80); data[1] = 0x80; #ifdef PEGASUS_WRITE_EEPROM - write_eprom_word( pegasus, 4, *(__u16 *)data ); + write_eprom_word(pegasus, 4, *(__u16 *) data); #endif } pegasus->intr_interval = data[1]; } - static void set_carrier(struct net_device *net) { - pegasus_t *pegasus; - short tmp; + pegasus_t *pegasus; + short tmp; pegasus = net->priv; read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp); @@ -732,33 +742,37 @@ static void set_carrier(struct net_device *net) netif_carrier_on(net); else netif_carrier_off(net); - -} +} static int pegasus_open(struct net_device *net) { - pegasus_t *pegasus = (pegasus_t *)net->priv; - int res; + pegasus_t *pegasus = (pegasus_t *) net->priv; + int res; + + if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) + return -ENOMEM; + pegasus->rx_skb->dev = net; + skb_reserve(pegasus->rx_skb, 2); down(&pegasus->sem); - FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, - usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_buff, PEGASUS_MAX_MTU, - read_bulk_callback, pegasus ); - if ( (res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL)) ) + FILL_BULK_URB(pegasus->rx_urb, pegasus->usb, + usb_rcvbulkpipe(pegasus->usb, 1), + pegasus->rx_skb->data, PEGASUS_MTU + 8, + read_bulk_callback, pegasus); + if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) warn("%s: failed rx_urb %d", __FUNCTION__, res); #ifdef PEGASUS_USE_INTR - FILL_INT_URB( pegasus->intr_urb, pegasus->usb, - usb_rcvintpipe(pegasus->usb, 3), - pegasus->intr_buff, sizeof(pegasus->intr_buff), - intr_callback, pegasus, pegasus->intr_interval ); - if ( (res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL)) ) + FILL_INT_URB(pegasus->intr_urb, pegasus->usb, + usb_rcvintpipe(pegasus->usb, 3), + pegasus->intr_buff, sizeof(pegasus->intr_buff), + intr_callback, pegasus, pegasus->intr_interval); + if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) warn("%s: failed intr_urb %d", __FUNCTION__, res); #endif - netif_start_queue( net ); + netif_start_queue(net); pegasus->flags |= PEGASUS_RUNNING; - if ( (res = enable_net_traffic(net, pegasus->usb)) ) { + if ((res = enable_net_traffic(net, pegasus->usb))) { err("can't enable_net_traffic() - %d", res); res = -EIO; goto exit; @@ -771,125 +785,122 @@ static int pegasus_open(struct net_device *net) return res; } - -static int pegasus_close( struct net_device *net ) +static int pegasus_close(struct net_device *net) { - pegasus_t *pegasus = net->priv; + pegasus_t *pegasus = net->priv; down(&pegasus->sem); pegasus->flags &= ~PEGASUS_RUNNING; - netif_stop_queue( net ); - if ( !(pegasus->flags & PEGASUS_UNPLUG) ) - disable_net_traffic( pegasus ); + netif_stop_queue(net); + if (!(pegasus->flags & PEGASUS_UNPLUG)) + disable_net_traffic(pegasus); - usb_unlink_urb( pegasus->rx_urb ); - usb_unlink_urb( pegasus->tx_urb ); - usb_unlink_urb( pegasus->ctrl_urb ); + usb_unlink_urb(pegasus->rx_urb); + usb_unlink_urb(pegasus->tx_urb); + usb_unlink_urb(pegasus->ctrl_urb); #ifdef PEGASUS_USE_INTR - usb_unlink_urb( pegasus->intr_urb ); + usb_unlink_urb(pegasus->intr_urb); #endif up(&pegasus->sem); return 0; } - static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr) { - pegasus_t *pegasus; - int cmd; - char tmp[128]; + pegasus_t *pegasus; + int cmd; + char tmp[128]; pegasus = net->priv; - if (get_user(cmd, (int *)uaddr)) + if (get_user(cmd, (int *) uaddr)) return -EFAULT; switch (cmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum, - pegasus->usb->devnum); - strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); - if (copy_to_user(uaddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd; - short lpa, bmcr; - - if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) - return -EFAULT; - ecmd.supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | - SUPPORTED_MII); - ecmd.port = PORT_TP; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = pegasus->phy; - read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); - read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); - if (bmcr & BMCR_ANENABLE) { - ecmd.autoneg = AUTONEG_ENABLE; - ecmd.speed = lpa & (LPA_100HALF|LPA_100FULL) ? - SPEED_100 : SPEED_10; - if (ecmd.speed == SPEED_100) - ecmd.duplex = lpa & LPA_100FULL ? - DUPLEX_FULL : DUPLEX_HALF; - else - ecmd.duplex = lpa & LPA_10FULL ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - ecmd.autoneg = AUTONEG_DISABLE; - ecmd.speed = bmcr & BMCR_SPEED100 ? - SPEED_100 : SPEED_10; - ecmd.duplex = bmcr & BMCR_FULLDPLX ? - DUPLEX_FULL : DUPLEX_HALF; + case ETHTOOL_GDRVINFO:{ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRIVER_VERSION, + ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum, + pegasus->usb->devnum); + strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case ETHTOOL_GSET:{ + struct ethtool_cmd ecmd; + short lpa, bmcr; + + if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) + return -EFAULT; + ecmd.supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | SUPPORTED_MII); + ecmd.port = PORT_TP; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = pegasus->phy; + read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); + read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = lpa & (LPA_100HALF | LPA_100FULL) ? + SPEED_100 : SPEED_10; + if (ecmd.speed == SPEED_100) + ecmd.duplex = lpa & LPA_100FULL ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd.duplex = lpa & LPA_10FULL ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = bmcr & BMCR_SPEED100 ? + SPEED_100 : SPEED_10; + ecmd.duplex = bmcr & BMCR_FULLDPLX ? + DUPLEX_FULL : DUPLEX_HALF; + } + if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SSET:{ + return -EOPNOTSUPP; + } + case ETHTOOL_GLINK:{ + struct ethtool_value edata = { ETHTOOL_GLINK }; + edata.data = netif_carrier_ok(net); + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; } - if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - - return 0; - } - case ETHTOOL_SSET: { - return -EOPNOTSUPP; - } - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = netif_carrier_ok(net); - if (copy_to_user(uaddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } default: return -EOPNOTSUPP; } } - -static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) +static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { - __u16 *data = (__u16 *)&rq->ifr_data; - pegasus_t *pegasus = net->priv; - int res; + __u16 *data = (__u16 *) & rq->ifr_data; + pegasus_t *pegasus = net->priv; + int res; down(&pegasus->sem); - switch(cmd) { + switch (cmd) { case SIOCETHTOOL: res = pegasus_ethtool_ioctl(net, rq->ifr_data); break; case SIOCDEVPRIVATE: data[0] = pegasus->phy; - case SIOCDEVPRIVATE+1: - read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); + case SIOCDEVPRIVATE + 1: + read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]); res = 0; break; - case SIOCDEVPRIVATE+2: - if ( !capable(CAP_NET_ADMIN) ) { + case SIOCDEVPRIVATE + 2: + if (!capable(CAP_NET_ADMIN)) { up(&pegasus->sem); return -EPERM; } @@ -903,8 +914,7 @@ static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) return res; } - -static void pegasus_set_multicast( struct net_device *net ) +static void pegasus_set_multicast(struct net_device *net) { pegasus_t *pegasus = net->priv; @@ -914,7 +924,7 @@ static void pegasus_set_multicast( struct net_device *net ) pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; info("%s: Promiscuous mode enabled", net->name); } else if ((net->mc_count > multicast_filter_limit) || - (net->flags & IFF_ALLMULTI)) { + (net->flags & IFF_ALLMULTI)) { pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; info("%s set allmulti", net->name); @@ -924,98 +934,97 @@ static void pegasus_set_multicast( struct net_device *net ) } pegasus->flags |= ETH_REGS_CHANGE; - ctrl_callback( pegasus->ctrl_urb ); + ctrl_callback(pegasus->ctrl_urb); netif_wake_queue(net); } - -static __u8 mii_phy_probe( pegasus_t *pegasus ) +static __u8 mii_phy_probe(pegasus_t * pegasus) { - int i; - __u16 tmp; + int i; + __u16 tmp; - for ( i=0; i < 32; i++ ) { - read_mii_word( pegasus, i, MII_BMSR, &tmp ); - if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 ) + for (i = 0; i < 32; i++) { + read_mii_word(pegasus, i, MII_BMSR, &tmp); + if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0) continue; else - return i; + return i; } - return 0xff; + return 0xff; } - -static inline void setup_pegasus_II( pegasus_t *pegasus ) +static inline void setup_pegasus_II(pegasus_t * pegasus) { - set_register( pegasus, Reg1d, 0 ); - set_register( pegasus, Reg7b, 2 ); - if ( pegasus->features & HAS_HOME_PNA && mii_mode ) - set_register( pegasus, Reg81, 6 ); + set_register(pegasus, Reg1d, 0); + set_register(pegasus, Reg7b, 2); + if (pegasus->features & HAS_HOME_PNA && mii_mode) + set_register(pegasus, Reg81, 6); else - set_register( pegasus, Reg81, 2 ); + set_register(pegasus, Reg81, 2); } - -static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) +static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id) { - struct net_device *net; - pegasus_t *pegasus; - int dev_index = id - pegasus_ids; + struct net_device *net; + pegasus_t *pegasus; + int dev_index = id - pegasus_ids; if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { err("usb_set_configuration() failed"); return NULL; } - if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { + if (!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { err("out of memory allocating device structure"); return NULL; } - usb_inc_dev_use( dev ); + usb_inc_dev_use(dev); memset(pegasus, 0, sizeof(struct pegasus)); pegasus->dev_index = dev_index; - init_waitqueue_head( &pegasus->ctrl_wait ); + init_waitqueue_head(&pegasus->ctrl_wait); pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->ctrl_urb) { - kfree (pegasus); + kfree(pegasus); return NULL; } pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->rx_urb) { - usb_free_urb (pegasus->ctrl_urb); - kfree (pegasus); + usb_free_urb(pegasus->ctrl_urb); + kfree(pegasus); return NULL; } pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->tx_urb) { - usb_free_urb (pegasus->rx_urb); - usb_free_urb (pegasus->ctrl_urb); - kfree (pegasus); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + kfree(pegasus); return NULL; } pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->intr_urb) { - usb_free_urb (pegasus->tx_urb); - usb_free_urb (pegasus->rx_urb); - usb_free_urb (pegasus->ctrl_urb); - kfree (pegasus); + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + kfree(pegasus); return NULL; } - net = init_etherdev( NULL, 0 ); - if ( !net ) { - usb_free_urb (pegasus->tx_urb); - usb_free_urb (pegasus->rx_urb); - usb_free_urb (pegasus->ctrl_urb); - kfree( pegasus ); + net = init_etherdev(NULL, 0); + if (!net) { + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); + kfree(pegasus); return NULL; } init_MUTEX(&pegasus->sem); + tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long)pegasus); + down(&pegasus->sem); pegasus->usb = dev; pegasus->net = net; @@ -1033,32 +1042,32 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum, pegasus->features = usb_dev_id[dev_index].private; #ifdef PEGASUS_USE_INTR - get_interrupt_interval( pegasus ); + get_interrupt_interval(pegasus); #endif - if ( reset_mac(pegasus) ) { + if (reset_mac(pegasus)) { err("can't reset MAC"); - unregister_netdev( pegasus->net ); - usb_free_urb (pegasus->tx_urb); - usb_free_urb (pegasus->rx_urb); - usb_free_urb (pegasus->ctrl_urb); + unregister_netdev(pegasus->net); + usb_free_urb(pegasus->tx_urb); + usb_free_urb(pegasus->rx_urb); + usb_free_urb(pegasus->ctrl_urb); kfree(pegasus->net); kfree(pegasus); pegasus = NULL; goto exit; } - info( "%s: %s", net->name, usb_dev_id[dev_index].name ); + info("%s: %s", net->name, usb_dev_id[dev_index].name); - set_ethernet_addr( pegasus ); + set_ethernet_addr(pegasus); - if ( pegasus->features & PEGASUS_II ) { - info( "setup Pegasus II specific registers" ); - setup_pegasus_II( pegasus ); + if (pegasus->features & PEGASUS_II) { + info("setup Pegasus II specific registers"); + setup_pegasus_II(pegasus); } - - pegasus->phy = mii_phy_probe( pegasus ); - if ( pegasus->phy == 0xff ) { - warn( "can't locate MII phy, using default" ); + + pegasus->phy = mii_phy_probe(pegasus); + if (pegasus->phy == 0xff) { + warn("can't locate MII phy, using default"); pegasus->phy = 1; } exit: @@ -1066,19 +1075,18 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum, return pegasus; } - -static void pegasus_disconnect( struct usb_device *dev, void *ptr ) +static void pegasus_disconnect(struct usb_device *dev, void *ptr) { struct pegasus *pegasus = ptr; - if ( !pegasus ) { + if (!pegasus) { warn("unregistering non-existant device"); return; } pegasus->flags |= PEGASUS_UNPLUG; - unregister_netdev( pegasus->net ); - usb_dec_dev_use( dev ); + unregister_netdev(pegasus->net); + usb_dec_dev_use(dev); usb_unlink_urb(pegasus->intr_urb); usb_unlink_urb(pegasus->tx_urb); usb_unlink_urb(pegasus->rx_urb); @@ -1087,12 +1095,13 @@ static void pegasus_disconnect( struct usb_device *dev, void *ptr ) usb_free_urb(pegasus->tx_urb); usb_free_urb(pegasus->rx_urb); usb_free_urb(pegasus->ctrl_urb); - kfree( pegasus->net ); - kfree( pegasus ); + if (pegasus->rx_skb) + dev_kfree_skb(pegasus->rx_skb); + kfree(pegasus->net); + kfree(pegasus); pegasus = NULL; } - static struct usb_driver pegasus_driver = { name: "pegasus", probe: pegasus_probe, @@ -1103,13 +1112,13 @@ static struct usb_driver pegasus_driver = { int __init pegasus_init(void) { info(DRIVER_VERSION ":" DRIVER_DESC); - return usb_register( &pegasus_driver ); + return usb_register(&pegasus_driver); } void __exit pegasus_exit(void) { - usb_deregister( &pegasus_driver ); + usb_deregister(&pegasus_driver); } -module_init( pegasus_init ); -module_exit( pegasus_exit ); +module_init(pegasus_init); +module_exit(pegasus_exit); diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h index a43894d5fb9465eb01b42be4a811f568c16b88c1..a7f0cb56be54ec6685a7073fdf27b6520dd7c1c5 100644 --- a/drivers/usb/net/pegasus.h +++ b/drivers/usb/net/pegasus.h @@ -22,8 +22,7 @@ #define PEGASUS_II 0x80000000 #define HAS_HOME_PNA 0x40000000 -#define PEGASUS_MTU 1500 -#define PEGASUS_MAX_MTU 1536 +#define PEGASUS_MTU 1536 #define EPROM_WRITE 0x01 #define EPROM_READ 0x02 @@ -45,6 +44,7 @@ #define CTRL_URB_RUNNING 0x00000010 #define CTRL_URB_SLEEP 0x00000020 #define PEGASUS_UNPLUG 0x00000040 +#define PEGASUS_RX_URB_FAIL 0x00000080 #define ETH_REGS_CHANGE 0x40000000 #define ETH_REGS_CHANGED 0x80000000 @@ -98,13 +98,14 @@ typedef struct pegasus { unsigned features; int dev_index; int intr_interval; + struct tasklet_struct rx_tl; struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; + struct sk_buff *rx_skb; struct usb_ctrlrequest dr; wait_queue_head_t ctrl_wait; struct semaphore sem; - unsigned char rx_buff[PEGASUS_MAX_MTU]; - unsigned char tx_buff[PEGASUS_MAX_MTU]; unsigned char intr_buff[8]; + __u8 tx_buff[PEGASUS_MTU]; __u8 eth_regs[4]; __u8 phy; __u8 gpio_res; @@ -236,7 +237,7 @@ PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204, LINKSYS_GPIO_RESET | HAS_HOME_PNA ) PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206, LINKSYS_GPIO_RESET ) -PEGASUS_DEV( "Linksys USB USB10TX", VENDOR_LINKSYS, 0x400b, +PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b, LINKSYS_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c, LINKSYS_GPIO_RESET | PEGASUS_II ) diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 6427c5ceee3194f272ca90e428e6b461818e432b..0472f266480a0d655c7062b5126157b8a51641c6 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -21,14 +21,11 @@ #include <linux/usb.h> #include <asm/uaccess.h> - - /* Version Information */ -#define DRIVER_VERSION "v0.5.0 (2002/03/28)" +#define DRIVER_VERSION "v0.5.4 (2002/04/11)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "rtl8150 based usb-ethernet driver" - #define IRD 0x0120 #define MAR 0x0126 #define CR 0x012e @@ -53,64 +50,65 @@ #define PHY_WRITE 0x20 #define PHY_GO 0x40 +#define MII_TIMEOUT 10 + #define RTL8150_REQT_READ 0xc0 #define RTL8150_REQT_WRITE 0x40 #define RTL8150_REQ_GET_REGS 0x05 #define RTL8150_REQ_SET_REGS 0x05 -#define RTL8150_MTU 1500 -#define RTL8150_MAX_MTU 1536 - +#define RTL8150_MTU 1540 #define RTL8150_TX_TIMEOUT (HZ) +#define RX_SKB_POOL_SIZE 4 /* rtl8150 flags */ -#define RTL8150_FLAG_HWCRC 0 +#define RTL8150_HW_CRC 0 #define RX_REG_SET 1 #define RTL8150_UNPLUG 2 - +#define RX_URB_FAIL 3 /* Define these values to match your device */ #define VENDOR_ID_REALTEK 0x0bda #define PRODUCT_ID_RTL8150 0x8150 /* table of devices that work with this driver */ -static struct usb_device_id rtl8150_table [] = { - { USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150) }, - { } +static struct usb_device_id rtl8150_table[] = { + {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)}, + {} }; -MODULE_DEVICE_TABLE (usb, rtl8150_table); - +MODULE_DEVICE_TABLE(usb, rtl8150_table); struct rtl8150 { - unsigned int flags; - struct usb_device *udev; - struct usb_interface *interface; - struct semaphore sem; - struct net_device_stats stats; - struct net_device *netdev; - struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; - struct usb_ctrlrequest dr; - int intr_interval; - u16 rx_creg; - u8 rx_buff[RTL8150_MAX_MTU]; - u8 tx_buff[RTL8150_MAX_MTU]; - u8 intr_buff[8]; - u8 phy; + unsigned long flags; + struct usb_device *udev; + struct semaphore sem; + struct tasklet_struct tl; + struct net_device_stats stats; + struct net_device *netdev; + struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; + struct sk_buff *tx_skb, *rx_skb; + struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE]; + spinlock_t rx_pool_lock; + struct usb_ctrlrequest dr; + int intr_interval; + u16 rx_creg; + u8 intr_buff[8]; + u8 phy; }; -typedef struct rtl8150 rtl8150_t; - +typedef struct rtl8150 rtl8150_t; /* the global usb devfs handle */ extern devfs_handle_t usb_devfs_handle; unsigned long multicast_filter_limit = 32; - +static void fill_skb_pool(rtl8150_t *); +static void free_skb_pool(rtl8150_t *); +static struct sk_buff *pull_skb(rtl8150_t *); static void rtl8150_disconnect(struct usb_device *dev, void *ptr); -static void * rtl8150_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id); - +static void *rtl8150_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); static struct usb_driver rtl8150_driver = { name: "rtl8150", @@ -119,33 +117,29 @@ static struct usb_driver rtl8150_driver = { id_table: rtl8150_table, }; - - /* ** ** device related part of the code ** */ -static int get_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) { - return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev,0), - RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, - indx, 0, data, size, HZ/2); + return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, + indx, 0, data, size, HZ / 2); } - -static int set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) { - return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev,0), - RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, - indx, 0, data, size, HZ/2); + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, + indx, 0, data, size, HZ / 2); } - static void ctrl_callback(struct urb *urb) { - rtl8150_t *dev; - + rtl8150_t *dev; + switch (urb->status) { case 0: break; @@ -160,23 +154,22 @@ static void ctrl_callback(struct urb *urb) clear_bit(RX_REG_SET, &dev->flags); } - -static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) { - int ret; + int ret; if (test_bit(RX_REG_SET, &dev->flags)) return -EAGAIN; - + dev->dr.bRequestType = RTL8150_REQT_WRITE; dev->dr.bRequest = RTL8150_REQ_SET_REGS; dev->dr.wValue = cpu_to_le16(indx); dev->dr.wIndex = 0; dev->dr.wLength = cpu_to_le16(size); dev->ctrl_urb->transfer_buffer_length = size; - FILL_CONTROL_URB(dev->ctrl_urb, dev->udev, usb_sndctrlpipe(dev->udev,0), - (char*)&dev->dr, &dev->rx_creg, size, - ctrl_callback, dev); + FILL_CONTROL_URB(dev->ctrl_urb, dev->udev, + usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr, + &dev->rx_creg, size, ctrl_callback, dev); if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) err("control request submission failed: %d", ret); else @@ -185,11 +178,10 @@ static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) return ret; } - -static int read_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 *reg) +static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg) { - int i; - u8 data[3], tmp; + int i; + u8 data[3], tmp; data[0] = phy; data[1] = data[2] = 0; @@ -200,9 +192,9 @@ static int read_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 *reg) set_registers(dev, PHYCNT, 1, &tmp); do { get_registers(dev, PHYCNT, 1, data); - } while ((data[0] & PHY_GO) && (i++ < HZ)); + } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); - if (i < HZ) { + if (i < MII_TIMEOUT) { get_registers(dev, PHYDAT, 2, data); *reg = le16_to_cpup(data); return 0; @@ -210,11 +202,10 @@ static int read_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 *reg) return 1; } - -static int write_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 reg) +static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg) { - int i; - u8 data[3], tmp; + int i; + u8 data[3], tmp; data[0] = phy; *(data + 1) = cpu_to_le16p(®); @@ -225,39 +216,36 @@ static int write_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 reg) set_registers(dev, PHYCNT, 1, &tmp); do { get_registers(dev, PHYCNT, 1, data); - } while((data[0] & PHY_GO) && (i++ < HZ)); + } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); - if (i < HZ) + if (i < MII_TIMEOUT) return 0; else return 1; } - -static inline void set_ethernet_addr(rtl8150_t *dev) +static inline void set_ethernet_addr(rtl8150_t * dev) { - u8 node_id[6]; + u8 node_id[6]; get_registers(dev, IRD, sizeof(node_id), node_id); memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id)); } - -static int rtl8150_reset(rtl8150_t *dev) +static int rtl8150_reset(rtl8150_t * dev) { - u8 data=0x10; - int i=HZ; + u8 data = 0x10; + int i = HZ; set_registers(dev, CR, 1, &data); do { get_registers(dev, CR, 1, &data); } while ((data & 0x10) && --i); - - return (i > 0) ? 0 : -1; -} + return (i > 0) ? 1 : 0; +} -static int alloc_all_urbs(rtl8150_t *dev) +static int alloc_all_urbs(rtl8150_t * dev) { dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->rx_urb) @@ -284,8 +272,7 @@ static int alloc_all_urbs(rtl8150_t *dev) return 1; } - -static void free_all_urbs(rtl8150_t *dev) +static void free_all_urbs(rtl8150_t * dev) { usb_free_urb(dev->rx_urb); usb_free_urb(dev->tx_urb); @@ -293,8 +280,7 @@ static void free_all_urbs(rtl8150_t *dev) usb_free_urb(dev->ctrl_urb); } - -static void unlink_all_urbs(rtl8150_t *dev) +static void unlink_all_urbs(rtl8150_t * dev) { usb_unlink_urb(dev->rx_urb); usb_unlink_urb(dev->tx_urb); @@ -302,30 +288,28 @@ static void unlink_all_urbs(rtl8150_t *dev) usb_unlink_urb(dev->ctrl_urb); } - static void read_bulk_callback(struct urb *urb) { - rtl8150_t *dev; - unsigned pkt_len, res; - struct sk_buff *skb; + rtl8150_t *dev; + unsigned pkt_len, res; + struct sk_buff *skb; struct net_device *netdev; - u16 rx_stat; + u16 rx_stat; dev = urb->context; - if (!dev) { - warn("!dev"); + if (!dev) + return; + if (test_bit(RTL8150_UNPLUG, &dev->flags)) return; - } netdev = dev->netdev; - if (!netif_device_present(netdev)) { - warn("netdev is not present"); + if (!netif_device_present(netdev)) return; - } + switch (urb->status) { case 0: break; case -ENOENT: - return; + return; /* urb's in unlink state */ case -ETIMEDOUT: warn("reset needed may be?.."); goto goon; @@ -334,35 +318,76 @@ static void read_bulk_callback(struct urb *urb) goto goon; } + tasklet_schedule(&dev->tl); + + if (!dev->rx_skb) { + /* lost packets++ */ + return; + } + res = urb->actual_length; - rx_stat = le16_to_cpu(*(short*)(dev->rx_buff + res - 4)); + rx_stat = le16_to_cpu(*(short *)(urb->transfer_buffer + res - 4)); pkt_len = res - 4; - if (!(skb = dev_alloc_skb(pkt_len + 2))) - goto goon; - skb->dev = netdev; - skb_reserve(skb, 2); - eth_copy_and_sum(skb, dev->rx_buff, pkt_len, 0); - skb_put(skb, pkt_len); - skb->protocol = eth_type_trans(skb, netdev); - netif_rx(skb); + skb_put(dev->rx_skb, pkt_len); + dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev); + netif_rx(dev->rx_skb); dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; + + skb = pull_skb(dev); + if (!skb) { + dev->rx_skb = NULL; + return; + } + + skb->dev = netdev; + skb_reserve(skb, 2); + dev->rx_skb = skb; goon: - FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1), - dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev); - if ((res=usb_submit_urb(dev->rx_urb, GFP_ATOMIC))) - warn("%s: Rx urb submission failed %d", netdev->name, res); + FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), + dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); + if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) + set_bit(RX_URB_FAIL, &dev->flags); + else + clear_bit(RX_URB_FAIL, &dev->flags); } +static void rx_fixup(unsigned long data) +{ + rtl8150_t *dev; + struct sk_buff *skb; + + dev = (rtl8150_t *)data; + + fill_skb_pool(dev); + if (test_bit(RX_URB_FAIL, &dev->flags)) + goto try_again; + if (dev->rx_skb) + return; + if (!(skb = pull_skb(dev))) { + tasklet_schedule(&dev->tl); + return; + } + dev->rx_skb = skb; + FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), + dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); +try_again: + if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) { + set_bit(RX_URB_FAIL, &dev->flags); + tasklet_schedule(&dev->tl); + } else + clear_bit(RX_URB_FAIL, &dev->flags); +} static void write_bulk_callback(struct urb *urb) { - rtl8150_t *dev; + rtl8150_t *dev; dev = urb->context; if (!dev) return; + dev_kfree_skb_irq(dev->tx_skb); if (!netif_device_present(dev->netdev)) return; if (urb->status) @@ -371,43 +396,94 @@ static void write_bulk_callback(struct urb *urb) netif_wake_queue(dev->netdev); } - void intr_callback(struct urb *urb) { - rtl8150_t *dev; + rtl8150_t *dev; dev = urb->context; if (!dev) return; switch (urb->status) { - case 0: - break; - case -ENOENT: - return; - default: - info("%s: intr status %d", dev->netdev->name, - urb->status); + case 0: + break; + case -ENOENT: + return; + default: + info("%s: intr status %d", dev->netdev->name, urb->status); } } - /* ** ** network related part of the code ** */ +static void fill_skb_pool(rtl8150_t *dev) +{ + struct sk_buff *skb; + int i; + unsigned long flags; + + spin_lock_irqsave(&dev->rx_pool_lock, flags); + for (i = 0; i < RX_SKB_POOL_SIZE; i++) { + if (dev->rx_skb_pool[i]) + continue; + skb = dev_alloc_skb(RTL8150_MTU + 2); + if (!skb) { + spin_unlock_irqrestore(&dev->rx_pool_lock, flags); + return; + } + skb->dev = dev->netdev; + skb_reserve(skb, 2); + dev->rx_skb_pool[i] = skb; + } + spin_unlock_irqrestore(&dev->rx_pool_lock, flags); +} -static int enable_net_traffic(rtl8150_t *dev) +static void free_skb_pool(rtl8150_t *dev) { - u8 cr, tcr, rcr, msr; + int i; - if (rtl8150_reset(dev)) { + spin_lock_irq(&dev->rx_pool_lock); + for (i = 0; i < RX_SKB_POOL_SIZE; i++) + if (dev->rx_skb_pool[i]) + dev_kfree_skb(dev->rx_skb_pool[i]); + spin_unlock_irq(&dev->rx_pool_lock); +} + +static struct sk_buff *pull_skb(rtl8150_t *dev) +{ + struct sk_buff *skb; + int i; + unsigned long flags; + + spin_lock_irqsave(&dev->rx_pool_lock, flags); + for (i = 0; i < RX_SKB_POOL_SIZE; i++) { + if (dev->rx_skb_pool[i]) { + skb = dev->rx_skb_pool[i]; + dev->rx_skb_pool[i] = NULL; + spin_unlock_irqrestore(&dev->rx_pool_lock, flags); + return skb; + } + } + spin_unlock_irqrestore(&dev->rx_pool_lock, flags); + return NULL; +} + +static int enable_net_traffic(rtl8150_t * dev) +{ + u8 cr, tcr, rcr, msr; + + if (!rtl8150_reset(dev)) { warn("%s - device reset failed", __FUNCTION__); } - dev->rx_creg = rcr = 0x9e; /* bit7=1 attach Rx info at the end */ - tcr = 0xd8; /* bit0=1 no CRC at the end of the frame */ + /* RCR bit7=1 attach Rx info at the end; =0 HW CRC (which is broken) */ + dev->rx_creg = rcr = 0x9e; + tcr = 0xd8; cr = 0x0c; + if (!(rcr & 0x80)) + set_bit(RTL8150_HW_CRC, &dev->flags); set_registers(dev, RCR, 1, &rcr); set_registers(dev, TCR, 1, &tcr); set_registers(dev, CR, 1, &cr); @@ -416,26 +492,23 @@ static int enable_net_traffic(rtl8150_t *dev) return 0; } - -static void disable_net_traffic(rtl8150_t *dev) +static void disable_net_traffic(rtl8150_t * dev) { - u8 cr; + u8 cr; get_registers(dev, CR, 1, &cr); cr &= 0xf3; set_registers(dev, CR, 1, &cr); } - static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev) { - return &((rtl8150_t *)dev->priv)->stats; + return &((rtl8150_t *) dev->priv)->stats; } - static void rtl8150_tx_timeout(struct net_device *netdev) { - rtl8150_t *dev; + rtl8150_t *dev; dev = netdev->priv; if (!dev) @@ -446,10 +519,9 @@ static void rtl8150_tx_timeout(struct net_device *netdev) dev->stats.tx_errors++; } - static void rtl8150_set_multicast(struct net_device *netdev) { - rtl8150_t *dev; + rtl8150_t *dev; dev = netdev->priv; netif_stop_queue(netdev); @@ -457,7 +529,7 @@ static void rtl8150_set_multicast(struct net_device *netdev) dev->rx_creg |= 0x0001; info("%s: promiscuous mode", netdev->name); } else if ((netdev->mc_count > multicast_filter_limit) || - (netdev->flags & IFF_ALLMULTI)) { + (netdev->flags & IFF_ALLMULTI)) { dev->rx_creg &= 0xfffe; dev->rx_creg |= 0x0002; info("%s: allmulti set", netdev->name); @@ -469,22 +541,19 @@ static void rtl8150_set_multicast(struct net_device *netdev) netif_wake_queue(netdev); } - static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) { - rtl8150_t *dev; - int count, res; + rtl8150_t *dev; + int count, res; netif_stop_queue(netdev); dev = netdev->priv; count = (skb->len < 60) ? 60 : skb->len; count = (count & 0x3f) ? count : count + 1; - memcpy(dev->tx_buff, skb->data, skb->len); - FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev,2), - dev->tx_buff, RTL8150_MAX_MTU, write_bulk_callback, dev); - dev->tx_urb->transfer_buffer_length = count; - - if ((res = usb_submit_urb(dev->tx_urb, GFP_KERNEL))) { + dev->tx_skb = skb; + FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), + skb->data, count, write_bulk_callback, dev); + if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) { warn("failed tx_urb %d\n", res); dev->stats.tx_errors++; netif_start_queue(netdev); @@ -493,31 +562,33 @@ static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) dev->stats.tx_bytes += skb->len; netdev->trans_start = jiffies; } - dev_kfree_skb(skb); return 0; } - static int rtl8150_open(struct net_device *netdev) { - rtl8150_t *dev; - int res; - + rtl8150_t *dev; + int res; + dev = netdev->priv; if (dev == NULL) { return -ENODEV; } + dev->rx_skb = pull_skb(dev); + if (!dev->rx_skb) + return -ENOMEM; + down(&dev->sem); - FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1), - dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev); - if ((res=usb_submit_urb(dev->rx_urb, GFP_KERNEL))) + FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), + dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); + if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) warn("%s: rx_urb submit failed: %d", __FUNCTION__, res); - FILL_INT_URB(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev,3), - dev->intr_buff, sizeof(dev->intr_buff), intr_callback, - dev, dev->intr_interval); - if ((res=usb_submit_urb(dev->intr_urb, GFP_KERNEL))) + FILL_INT_URB(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3), + dev->intr_buff, sizeof(dev->intr_buff), intr_callback, + dev, dev->intr_interval); + if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) warn("%s: intr_urb submit failed: %d", __FUNCTION__, res); netif_start_queue(netdev); enable_net_traffic(dev); @@ -526,7 +597,6 @@ static int rtl8150_open(struct net_device *netdev) return res; } - static int rtl8150_close(struct net_device *netdev) { rtl8150_t *dev; @@ -537,53 +607,47 @@ static int rtl8150_close(struct net_device *netdev) return -ENODEV; down(&dev->sem); + netif_stop_queue(netdev); if (!test_bit(RTL8150_UNPLUG, &dev->flags)) disable_net_traffic(dev); unlink_all_urbs(dev); - netif_stop_queue(netdev); up(&dev->sem); - return res; } - static int rtl8150_ethtool_ioctl(struct net_device *netdev, void *uaddr) { - rtl8150_t *dev; - int cmd; - char tmp[128]; + rtl8150_t *dev; + int cmd; dev = netdev->priv; - if (get_user(cmd, (int *)uaddr)) + if (get_user(cmd, (int *) uaddr)) return -EFAULT; switch (cmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - + case ETHTOOL_GDRVINFO:{ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - sprintf(tmp, "usb%d:%d", dev->udev->bus->busnum, - dev->udev->devnum); - strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + usb_make_path(dev->udev, info.bus_info, sizeof info.bus_info); if (copy_to_user(uaddr, &info, sizeof(info))) return -EFAULT; return 0; - } - case ETHTOOL_GSET: { + } + case ETHTOOL_GSET:{ struct ethtool_cmd ecmd; - short lpa, bmcr; + short lpa, bmcr; if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) return -EFAULT; ecmd.supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | - SUPPORTED_MII); + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | SUPPORTED_MII); ecmd.port = PORT_TP; ecmd.transceiver = XCVR_INTERNAL; ecmd.phy_address = dev->phy; @@ -595,45 +659,44 @@ static int rtl8150_ethtool_ioctl(struct net_device *netdev, void *uaddr) SPEED_100 : SPEED_10; if (ecmd.speed == SPEED_100) ecmd.duplex = (lpa & LPA_100FULL) ? - DUPLEX_FULL : DUPLEX_HALF; + DUPLEX_FULL : DUPLEX_HALF; else ecmd.duplex = (lpa & LPA_10FULL) ? - DUPLEX_FULL : DUPLEX_HALF; + DUPLEX_FULL : DUPLEX_HALF; } else { ecmd.autoneg = AUTONEG_DISABLE; ecmd.speed = (bmcr & BMCR_SPEED100) ? - SPEED_100 : SPEED_10; + SPEED_100 : SPEED_10; ecmd.duplex = (bmcr & BMCR_FULLDPLX) ? - DUPLEX_FULL : DUPLEX_HALF; + DUPLEX_FULL : DUPLEX_HALF; } if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) return -EFAULT; return 0; - } + } case ETHTOOL_SSET: return -ENOTSUPP; - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; + case ETHTOOL_GLINK:{ + struct ethtool_value edata = { ETHTOOL_GLINK }; edata.data = netif_carrier_ok(netdev); if (copy_to_user(uaddr, &edata, sizeof(edata))) return -EFAULT; return 0; - } + } default: return -EOPNOTSUPP; } } - -static int rtl8150_ioctl (struct net_device *netdev, struct ifreq *rq, int cmd) +static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { rtl8150_t *dev; - u16 *data; - int res; + u16 *data; + int res; dev = netdev->priv; - data = (u16 *)&rq->ifr_data; + data = (u16 *) & rq->ifr_data; res = 0; down(&dev->sem); @@ -643,10 +706,10 @@ static int rtl8150_ioctl (struct net_device *netdev, struct ifreq *rq, int cmd) break; case SIOCDEVPRIVATE: data[0] = dev->phy; - case SIOCDEVPRIVATE+1: + case SIOCDEVPRIVATE + 1: read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]); break; - case SIOCDEVPRIVATE+2: + case SIOCDEVPRIVATE + 2: if (!capable(CAP_NET_ADMIN)) { up(&dev->sem); return -EPERM; @@ -660,27 +723,25 @@ static int rtl8150_ioctl (struct net_device *netdev, struct ifreq *rq, int cmd) return res; } - -static void * rtl8150_probe(struct usb_device *udev, unsigned int ifnum, - const struct usb_device_id *id) +static void *rtl8150_probe(struct usb_device *udev, unsigned int ifnum, + const struct usb_device_id *id) { rtl8150_t *dev; struct net_device *netdev; - udev->config[0].bConfigurationValue = 1; if (usb_set_configuration(udev, udev->config[0].bConfigurationValue)) { err("usb_set_configuration() failed"); return NULL; } if ((udev->descriptor.idVendor != VENDOR_ID_REALTEK) || (udev->descriptor.idProduct != PRODUCT_ID_RTL8150)) { - err("Not the one we are interested about"); + err("Not the one we are interested about"); return NULL; } dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL); if (!dev) { - err ("Out of memory"); - goto exit; + err("Out of memory"); + return NULL; } else memset(dev, 0, sizeof(rtl8150_t)); @@ -688,11 +749,14 @@ static void * rtl8150_probe(struct usb_device *udev, unsigned int ifnum, if (!netdev) { kfree(dev); err("Oh boy, out of memory again?!?"); - dev = NULL; - goto exit; + return NULL; } - + init_MUTEX(&dev->sem); + tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev); + spin_lock_init(&dev->rx_pool_lock); + + down(&dev->sem); dev->udev = udev; dev->netdev = netdev; SET_MODULE_OWNER(netdev); @@ -708,23 +772,29 @@ static void * rtl8150_probe(struct usb_device *udev, unsigned int ifnum, netdev->mtu = RTL8150_MTU; dev->intr_interval = 100; /* 100ms */ - if (rtl8150_reset(dev) || !alloc_all_urbs(dev)) { + if (!alloc_all_urbs(dev)) { + err("out of memory"); + goto err; + } + if (!rtl8150_reset(dev)) { err("couldn't reset the device"); free_all_urbs(dev); - unregister_netdev(dev->netdev); - kfree(netdev); - kfree(dev); - dev = NULL; - goto exit; + goto err; } - + fill_skb_pool(dev); set_ethernet_addr(dev); info("%s: rtl8150 is detected", netdev->name); -exit: + + up(&dev->sem); return dev; +err: + unregister_netdev(dev->netdev); + up(&dev->sem); + kfree(netdev); + kfree(dev); + return NULL; } - static void rtl8150_disconnect(struct usb_device *udev, void *ptr) { rtl8150_t *dev; @@ -734,27 +804,26 @@ static void rtl8150_disconnect(struct usb_device *udev, void *ptr) unregister_netdev(dev->netdev); unlink_all_urbs(dev); free_all_urbs(dev); + free_skb_pool(dev); + if (dev->rx_skb) + dev_kfree_skb(dev->rx_skb); kfree(dev->netdev); kfree(dev); dev->netdev = NULL; dev = NULL; } - - static int __init usb_rtl8150_init(void) { info(DRIVER_DESC " " DRIVER_VERSION); return usb_register(&rtl8150_driver); } - static void __exit usb_rtl8150_exit(void) { usb_deregister(&rtl8150_driver); } - module_init(usb_rtl8150_init); module_exit(usb_rtl8150_exit); diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index c86638c7e08a04f3aa6d950c4caf052964a6ac53..c329ed692ce53294303f99ef9cf61cf04f789654 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -538,25 +538,25 @@ static int genelink_rx_fixup (struct usbnet *dev, struct sk_buff *skb) // allocate the skb for the individual packet gl_skb = alloc_skb (size, GFP_ATOMIC); - if (gl_skb == 0) - return 0; + if (gl_skb) { - // copy the packet data to the new skb - memcpy (gl_skb->data, packet->packet_data, size); + // copy the packet data to the new skb + memcpy (gl_skb->data, packet->packet_data, size); - // set skb data size - gl_skb->len = size; - gl_skb->dev = &dev->net; + // set skb data size + gl_skb->len = size; + gl_skb->dev = &dev->net; - // determine the packet's protocol ID - gl_skb->protocol = eth_type_trans (gl_skb, &dev->net); + // determine the packet's protocol ID + gl_skb->protocol = eth_type_trans (gl_skb, &dev->net); - // update the status - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; + // update the status + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; - // notify os of the received packet - status = netif_rx (gl_skb); + // notify os of the received packet + status = netif_rx (gl_skb); + } // advance to the next packet packet = (struct gl_packet *) @@ -834,10 +834,10 @@ static void nc_dump_registers (struct usbnet *dev) static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl) { #ifdef DEBUG - devdbg (dev, "net1080 %03d/%03d usbctl 0x%x:%s%s%s%s%s;" + devdbg (dev, "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s;" " this%s%s;" " other%s%s; r/o 0x%x", - dev->udev->bus->busnum, dev->udev->devnum, + dev->udev->bus->bus_name, dev->udev->devpath, usbctl, (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", @@ -879,10 +879,10 @@ static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl) static inline void nc_dump_status (struct usbnet *dev, u16 status) { #ifdef DEBUG - devdbg (dev, "net1080 %03d/%03d status 0x%x:" + devdbg (dev, "net1080 %s-%s status 0x%x:" " this (%c) PKT=%d%s%s%s;" " other PKT=%d%s%s%s; unspec 0x%x", - dev->udev->bus->busnum, dev->udev->devnum, + dev->udev->bus->bus_name, dev->udev->devpath, status, // XXX the packet counts don't seem right @@ -917,8 +917,8 @@ static inline void nc_dump_status (struct usbnet *dev, u16 status) static inline void nc_dump_ttl (struct usbnet *dev, u16 ttl) { #ifdef DEBUG - devdbg (dev, "net1080 %03d/%03d ttl 0x%x this = %d, other = %d", - dev->udev->bus->busnum, dev->udev->devnum, + devdbg (dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d", + dev->udev->bus->bus_name, dev->udev->devpath, ttl, TTL_THIS (ttl), @@ -941,7 +941,8 @@ static int net1080_reset (struct usbnet *dev) // nc_dump_registers (dev); if ((retval = nc_register_read (dev, REG_STATUS, vp)) < 0) { - dbg ("can't read dev %d status: %d", dev->udev->devnum, retval); + dbg ("can't read %s-%s status: %d", + dev->udev->bus->bus_name, dev->udev->devpath, retval); goto done; } status = *vp; @@ -1504,10 +1505,9 @@ static int usbnet_open (struct net_device *net) // put into "known safe" state if (info->reset && (retval = info->reset (dev)) < 0) { - devinfo (dev, "open reset fail (%d) usbnet bus%d%s, %s", + devinfo (dev, "open reset fail (%d) usbnet usb-%s-%s, %s", retval, - // FIXME busnum is unstable - dev->udev->bus->busnum, dev->udev->devpath, + dev->udev->bus->bus_name, dev->udev->devpath, info->description); goto done; } @@ -1557,9 +1557,7 @@ static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr) strncpy (info.version, DRIVER_VERSION, sizeof info.version); strncpy (info.fw_version, dev->driver_info->description, sizeof info.fw_version); - snprintf (info.bus_info, sizeof info.bus_info, "USB bus%d%s", - /* FIXME busnums are bogus/unstable IDs */ - dev->udev->bus->busnum, dev->udev->devpath); + usb_make_path (dev->udev, info.bus_info, sizeof info.bus_info); if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; @@ -1847,9 +1845,8 @@ static void usbnet_disconnect (struct usb_device *udev, void *ptr) { struct usbnet *dev = (struct usbnet *) ptr; - devinfo (dev, "unregister usbnet bus%d%s, %s", - // FIXME busnum is unstable - udev->bus->busnum, udev->devpath, + devinfo (dev, "unregister usbnet usb-%s-%s, %s", + udev->bus->bus_name, udev->devpath, dev->driver_info->description); unregister_netdev (&dev->net); @@ -1938,9 +1935,8 @@ usbnet_probe (struct usb_device *udev, unsigned ifnum, net->do_ioctl = usbnet_ioctl; register_netdev (&dev->net); - devinfo (dev, "register usbnet bus%d%s, %s", - // FIXME busnum is unstable - udev->bus->busnum, udev->devpath, + devinfo (dev, "register usbnet usb-%s-%s, %s", + udev->bus->bus_name, udev->devpath, dev->driver_info->description); // ok, it's ready to go. diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 01563bff129e49e6cffa456f8a9df5476da44102..67512a045196556221e300425ec2c6d4ab8480f7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -197,6 +197,7 @@ static struct usb_serial_device_type ftdi_sio_device = { }; static struct usb_serial_device_type ftdi_8U232AM_device = { + owner: THIS_MODULE, name: "FTDI 8U232AM", id_table: id_table_8U232AM, num_interrupt_in: 0, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 8cfc17921efc6214667a07c632903cffefb7f2cd..9307c1d941fe5dd9b658f72210cc4b6d1b1473b8 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -798,6 +798,7 @@ static void keyspan_pda_shutdown (struct usb_serial *serial) #ifdef KEYSPAN static struct usb_serial_device_type keyspan_pda_fake_device = { + owner: THIS_MODULE, name: "Keyspan PDA - (prerenumeration)", id_table: id_table_fake, num_interrupt_in: NUM_DONT_CARE, @@ -810,17 +811,19 @@ static struct usb_serial_device_type keyspan_pda_fake_device = { #ifdef XIRCOM static struct usb_serial_device_type xircom_pgs_fake_device = { - name: "Xircom / Entregra PGS - (prerenumeration)", - id_table: id_table_fake_xircom, - num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, - num_ports: 1, - startup: keyspan_pda_fake_startup, + owner: THIS_MODULE, + name: "Xircom / Entregra PGS - (prerenumeration)", + id_table: id_table_fake_xircom, + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 1, + startup: keyspan_pda_fake_startup, }; #endif static struct usb_serial_device_type keyspan_pda_device = { + owner: THIS_MODULE, name: "Keyspan PDA", id_table: id_table_std, num_interrupt_in: 1, diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 22cd815ff62bd676fae4da2e924efd0b89e32a48..0ffdae91b8bd04e27cf533389a5e97536829b172 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -15,6 +15,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (04/10/2002) gkh + * added serial_read_proc function which creates a + * /proc/tty/driver/usb-serial file. + * * (03/27/2002) gkh * Got USB serial console code working properly and merged into the main * version of the tree. Thanks to Randy Dunlap for the initial version @@ -340,7 +344,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.4" +#define DRIVER_VERSION "v1.5" #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/" #define DRIVER_DESC "USB Serial Driver core" @@ -844,6 +848,48 @@ static void serial_shutdown (struct usb_serial *serial) generic_shutdown(serial); } +static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct usb_serial *serial; + int length = 0; + int i; + off_t begin = 0; + char tmp[40]; + + dbg(__FUNCTION__); + length += sprintf (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION); + for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) { + serial = get_serial_by_minor(i); + if (serial == NULL) + continue; + + length += sprintf (page+length, "%d:", i); + if (serial->type->owner) + length += sprintf (page+length, " module:%s", serial->type->owner->name); + length += sprintf (page+length, " name:\"%s\"", serial->type->name); + length += sprintf (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product); + length += sprintf (page+length, " num_ports:%d", serial->num_ports); + length += sprintf (page+length, " port:%d", i - serial->minor + 1); + + usb_make_path(serial->dev, tmp, sizeof(tmp)); + length += sprintf (page+length, " path:%s", tmp); + + length += sprintf (page+length, "\n"); + if ((length + begin) > (off + count)) + goto done; + if ((length + begin) < off) { + begin += length; + length = 0; + } + } + *eof = 1; +done: + if (off >= (length + begin)) + return 0; + *start = page + (off-begin); + return ((count < begin+length-off) ? count : begin+length-off); +} + /***************************************************************************** * generic devices specific driver functions *****************************************************************************/ @@ -1491,6 +1537,7 @@ static struct tty_driver serial_tty_driver = { unthrottle: serial_unthrottle, break_ctl: serial_break, chars_in_buffer: serial_chars_in_buffer, + read_proc: serial_read_proc, }; diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 18e64e83b1748439135f855f04ca6f91c171b1fd..097df6c8d8a9b9e7f960e376b4c251167fff44bc 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -10,16 +10,14 @@ EXTRA_CFLAGS := -I../../scsi/ obj-$(CONFIG_USB_STORAGE) += usb-storage.o -usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o -usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o -usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o -usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o -usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA) += shuttle_sm.o -usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH) += shuttle_cf.o -usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o -usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o -usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o -usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o +usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o +usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o +usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o +usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o +usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o +usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o +usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o +usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ initializers.o $(usb-storage-obj-y) diff --git a/fs/Config.in b/fs/Config.in index 8a4cf90999bb3ecdb30fdda454bcbbaae6ade42a..bd49b9e40cbf863b854015350a9f9362778ef042 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -124,6 +124,7 @@ if [ "$CONFIG_NET" = "y" ]; then if [ "$CONFIG_NFSD_V3" = "y" -o "$CONFIG_NFS_V3" = "y" ]; then define_bool CONFIG_LOCKD_V4 y fi + define_tristate CONFIG_EXPORTFS $CONFIG_NFSD dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET if [ "$CONFIG_SMB_FS" != "n" ]; then diff --git a/fs/Makefile b/fs/Makefile index 43a3d6ffbacfdad2d0bc473708a6d7bc0593a3ce..f129943df16fe8221caf23e9669481afb698f5aa 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -45,6 +45,7 @@ subdir-$(CONFIG_DEVFS_FS) += devfs subdir-$(CONFIG_HFS_FS) += hfs subdir-$(CONFIG_VXFS_FS) += freevxfs subdir-$(CONFIG_NFS_FS) += nfs +subdir-$(CONFIG_EXPORTFS) += exportfs subdir-$(CONFIG_NFSD) += nfsd subdir-$(CONFIG_LOCKD) += lockd subdir-$(CONFIG_NLS) += nls diff --git a/fs/bfs/bfs_defs.h b/fs/bfs/bfs_defs.h index 4642f67072cb239e5539656c4f5084ce2c18c164..207c95bdb594410147f02667a4b6919b0d9eb526 100644 --- a/fs/bfs/bfs_defs.h +++ b/fs/bfs/bfs_defs.h @@ -1,14 +1,3 @@ -#define su_lasti u.bfs_sb.si_lasti -#define su_blocks u.bfs_sb.si_blocks -#define su_freeb u.bfs_sb.si_freeb -#define su_freei u.bfs_sb.si_freei -#define su_lf_ioff u.bfs_sb.si_lf_ioff -#define su_lf_sblk u.bfs_sb.si_lf_sblk -#define su_lf_eblk u.bfs_sb.si_lf_eblk -#define su_imap u.bfs_sb.si_imap -#define su_sbh u.bfs_sb.si_sbh -#define su_bfs_sb u.bfs_sb.si_bfs_sb - #define printf(format, args...) \ printk(KERN_ERR "BFS-fs: %s(): " format, __FUNCTION__, ## args) diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 02cce3baec24714e9d251461b8733007de02a56d..eed5d411e4d7e7007b66516e32c1795c9e1a89fa 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -78,20 +78,21 @@ static int bfs_create(struct inode * dir, struct dentry * dentry, int mode) int err; struct inode * inode; struct super_block * s = dir->i_sb; + struct bfs_sb_info * info = BFS_SB(s); unsigned long ino; inode = new_inode(s); if (!inode) return -ENOSPC; lock_kernel(); - ino = find_first_zero_bit(s->su_imap, s->su_lasti); - if (ino > s->su_lasti) { + ino = find_first_zero_bit(info->si_imap, info->si_lasti); + if (ino > info->si_lasti) { unlock_kernel(); iput(inode); return -ENOSPC; } - set_bit(ino, s->su_imap); - s->su_freei--; + set_bit(ino, info->si_imap); + info->si_freei--; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; @@ -318,11 +319,9 @@ static struct buffer_head * bfs_find_entry(struct inode * dir, { unsigned long block, offset; struct buffer_head * bh; - struct bfs_sb_info * info; struct bfs_dirent * de; *res_dir = NULL; - info = &dir->i_sb->u.bfs_sb; if (namelen > BFS_NAMELEN) return NULL; bh = NULL; diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 428afd4a11a0ef20b8870839b3a1dfa3759d469e..6413652035c24a63e882c9593a73000dbdc72923 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -60,10 +60,11 @@ static int bfs_get_block(struct inode * inode, sector_t block, long phys; int err; struct super_block *sb = inode->i_sb; + struct bfs_sb_info *info = BFS_SB(sb); struct bfs_inode_info *bi = BFS_I(inode); - struct buffer_head *sbh = sb->su_sbh; + struct buffer_head *sbh = info->si_sbh; - if (block < 0 || block > sb->su_blocks) + if (block < 0 || block > info->si_blocks) return -EIO; phys = bi->i_sblock + block; @@ -89,12 +90,12 @@ static int bfs_get_block(struct inode * inode, sector_t block, /* if the last data block for this file is the last allocated block, we can extend the file trivially, without moving it anywhere */ - if (bi->i_eblock == sb->su_lf_eblk) { + if (bi->i_eblock == info->si_lf_eblk) { dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", create, block, phys); map_bh(bh_result, sb, phys); - sb->su_freeb -= phys - bi->i_eblock; - sb->su_lf_eblk = bi->i_eblock = phys; + info->si_freeb -= phys - bi->i_eblock; + info->si_lf_eblk = bi->i_eblock = phys; mark_inode_dirty(inode); mark_buffer_dirty(sbh); err = 0; @@ -102,7 +103,7 @@ static int bfs_get_block(struct inode * inode, sector_t block, } /* Ok, we have to move this entire file to the next free block */ - phys = sb->su_lf_eblk + 1; + phys = info->si_lf_eblk + 1; if (bi->i_sblock) { /* if data starts on block 0 then there is no data */ err = bfs_move_blocks(inode->i_sb, bi->i_sblock, bi->i_eblock, phys); @@ -116,11 +117,11 @@ static int bfs_get_block(struct inode * inode, sector_t block, dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", create, block, phys); bi->i_sblock = phys; phys += block; - sb->su_lf_eblk = bi->i_eblock = phys; + info->si_lf_eblk = bi->i_eblock = phys; /* this assumes nothing can write the inode back while we are here * and thus update inode->i_blocks! (XXX)*/ - sb->su_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; + info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; mark_inode_dirty(inode); mark_buffer_dirty(sbh); map_bh(bh_result, sb, phys); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 256a3643a7dbae829cdf4adff696d58c4bcebcee..7ad4e6e49e59ff47aee3b3a75ef0241509163ac3 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -39,7 +39,7 @@ static void bfs_read_inode(struct inode * inode) struct buffer_head * bh; int block, off; - if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { + if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) { printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino); make_bad_inode(inode); return; @@ -91,7 +91,7 @@ static void bfs_write_inode(struct inode * inode, int unused) struct buffer_head * bh; int block, off; - if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) { + if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) { printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino); return; } @@ -137,10 +137,11 @@ static void bfs_delete_inode(struct inode * inode) struct buffer_head * bh; int block, off; struct super_block * s = inode->i_sb; + struct bfs_sb_info * info = BFS_SB(s); dprintf("ino=%08lx\n", inode->i_ino); - if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) { + if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > info->si_lasti) { printf("invalid ino=%08lx\n", inode->i_ino); return; } @@ -159,9 +160,9 @@ static void bfs_delete_inode(struct inode * inode) off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK; di = (struct bfs_inode *)bh->b_data + off; if (di->i_ino) { - s->su_freeb += BFS_FILEBLOCKS(di); - s->su_freei++; - clear_bit(di->i_ino, s->su_imap); + info->si_freeb += BFS_FILEBLOCKS(di); + info->si_freei++; + clear_bit(di->i_ino, info->si_imap); dump_imap("delete_inode", s); } di->i_ino = 0; @@ -172,9 +173,9 @@ static void bfs_delete_inode(struct inode * inode) /* if this was the last file, make the previous block "last files last block" even if there is no real file there, saves us 1 gap */ - if (s->su_lf_eblk == BFS_I(inode)->i_eblock) { - s->su_lf_eblk = BFS_I(inode)->i_sblock - 1; - mark_buffer_dirty(s->su_sbh); + if (info->si_lf_eblk == BFS_I(inode)->i_eblock) { + info->si_lf_eblk = BFS_I(inode)->i_sblock - 1; + mark_buffer_dirty(info->si_sbh); } unlock_kernel(); clear_inode(inode); @@ -182,18 +183,22 @@ static void bfs_delete_inode(struct inode * inode) static void bfs_put_super(struct super_block *s) { - brelse(s->su_sbh); - kfree(s->su_imap); + struct bfs_sb_info *info = BFS_SB(s); + brelse(info->si_sbh); + kfree(info->si_imap); + kfree(info); + s->u.generic_sbp = NULL; } static int bfs_statfs(struct super_block *s, struct statfs *buf) { + struct bfs_sb_info *info = BFS_SB(s); buf->f_type = BFS_MAGIC; buf->f_bsize = s->s_blocksize; - buf->f_blocks = s->su_blocks; - buf->f_bfree = buf->f_bavail = s->su_freeb; - buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO; - buf->f_ffree = s->su_freei; + buf->f_blocks = info->si_blocks; + buf->f_bfree = buf->f_bavail = info->si_freeb; + buf->f_files = info->si_lasti + 1 - BFS_ROOT_INO; + buf->f_ffree = info->si_freei; buf->f_fsid.val[0] = kdev_t_to_nr(s->s_dev); buf->f_namelen = BFS_NAMELEN; return 0; @@ -202,7 +207,7 @@ static int bfs_statfs(struct super_block *s, struct statfs *buf) static void bfs_write_super(struct super_block *s) { if (!(s->s_flags & MS_RDONLY)) - mark_buffer_dirty(s->su_sbh); + mark_buffer_dirty(BFS_SB(s)->si_sbh); s->s_dirt = 0; } @@ -267,14 +272,14 @@ void dump_imap(const char *prefix, struct super_block * s) if (!tmpbuf) return; - for (i=s->su_lasti; i>=0; i--) { + for (i=BFS_SB(s)->si_lasti; i>=0; i--) { if (i>PAGE_SIZE-100) break; - if (test_bit(i, s->su_imap)) + if (test_bit(i, BFS_SB(s)->si_imap)) strcat(tmpbuf, "1"); else strcat(tmpbuf, "0"); } - printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf); + printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, BFS_SB(s)->si_lasti, tmpbuf); free_page((unsigned long)tmpbuf); #endif } @@ -285,6 +290,13 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) struct bfs_super_block * bfs_sb; struct inode * inode; int i, imap_len; + struct bfs_sb_info * info; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + s->u.generic_sbp = info; + memset(info, 0, sizeof(*info)); sb_set_blocksize(s, BFS_BSIZE); @@ -302,49 +314,49 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) printf("%s is unclean, continuing\n", s->s_id); s->s_magic = BFS_MAGIC; - s->su_bfs_sb = bfs_sb; - s->su_sbh = bh; - s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) + info->si_bfs_sb = bfs_sb; + info->si_sbh = bh; + info->si_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) + BFS_ROOT_INO - 1; - imap_len = s->su_lasti/8 + 1; - s->su_imap = kmalloc(imap_len, GFP_KERNEL); - if (!s->su_imap) + imap_len = info->si_lasti/8 + 1; + info->si_imap = kmalloc(imap_len, GFP_KERNEL); + if (!info->si_imap) goto out; - memset(s->su_imap, 0, imap_len); + memset(info->si_imap, 0, imap_len); for (i=0; i<BFS_ROOT_INO; i++) - set_bit(i, s->su_imap); + set_bit(i, info->si_imap); s->s_op = &bfs_sops; inode = iget(s, BFS_ROOT_INO); if (!inode) { - kfree(s->su_imap); + kfree(info->si_imap); goto out; } s->s_root = d_alloc_root(inode); if (!s->s_root) { iput(inode); - kfree(s->su_imap); + kfree(info->si_imap); goto out; } - s->su_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */ - s->su_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS; - s->su_freei = 0; - s->su_lf_eblk = 0; - s->su_lf_sblk = 0; - s->su_lf_ioff = 0; - for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) { + info->si_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */ + info->si_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS; + info->si_freei = 0; + info->si_lf_eblk = 0; + info->si_lf_sblk = 0; + info->si_lf_ioff = 0; + for (i=BFS_ROOT_INO; i<=info->si_lasti; i++) { inode = iget(s,i); if (BFS_I(inode)->i_dsk_ino == 0) - s->su_freei++; + info->si_freei++; else { - set_bit(i, s->su_imap); - s->su_freeb -= inode->i_blocks; - if (BFS_I(inode)->i_eblock > s->su_lf_eblk) { - s->su_lf_eblk = BFS_I(inode)->i_eblock; - s->su_lf_sblk = BFS_I(inode)->i_sblock; - s->su_lf_ioff = BFS_INO2OFF(i); + set_bit(i, info->si_imap); + info->si_freeb -= inode->i_blocks; + if (BFS_I(inode)->i_eblock > info->si_lf_eblk) { + info->si_lf_eblk = BFS_I(inode)->i_eblock; + info->si_lf_sblk = BFS_I(inode)->i_sblock; + info->si_lf_ioff = BFS_INO2OFF(i); } } iput(inode); @@ -358,6 +370,8 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) out: brelse(bh); + kfree(info); + s->u.generic_sbp = NULL; return -EINVAL; } diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c5877b4322842fc45aa34ab10ba713c587d17971..49e76ddda8cc4dcfe2f27b7621a3e06bd1b30149 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -38,7 +38,7 @@ enum {Enabled, Magic}; typedef struct { struct list_head list; - int flags; /* type, status, etc. */ + unsigned long flags; /* type, status, etc. */ int offset; /* offset of magic */ int size; /* size of magic/mask */ char *magic; /* magic or filename extension */ diff --git a/fs/dcache.c b/fs/dcache.c index f4b4056b05ff58e79366915ce28a7ec96f060175..9fc04d4f1af0cab7c4fd10e025710f0624b6520b 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -239,12 +239,15 @@ struct dentry * dget_locked(struct dentry *dentry) * return it. Otherwise return NULL. Notice that if inode is a directory * there can be only one alias and it can be unhashed only if it has * no children. + * + * If the inode has a DCACHE_DISCONNECTED alias, then prefer + * any other hashed alias over that one. */ struct dentry * d_find_alias(struct inode *inode) { struct list_head *head, *next, *tmp; - struct dentry *alias; + struct dentry *alias, *discon_alias=NULL; spin_lock(&dcache_lock); head = &inode->i_dentry; @@ -254,13 +257,19 @@ struct dentry * d_find_alias(struct inode *inode) next = tmp->next; alias = list_entry(tmp, struct dentry, d_alias); if (!list_empty(&alias->d_hash)) { - __dget_locked(alias); - spin_unlock(&dcache_lock); - return alias; + if (alias->d_flags & DCACHE_DISCONNECTED) + discon_alias = alias; + else { + __dget_locked(alias); + spin_unlock(&dcache_lock); + return alias; + } } } + if (discon_alias) + __dget_locked(discon_alias); spin_unlock(&dcache_lock); - return NULL; + return discon_alias; } /* @@ -537,6 +546,33 @@ void shrink_dcache_parent(struct dentry * parent) prune_dcache(found); } +/** + * shrink_dcache_anon - further prune the cache + * @head: head of d_hash list of dentries to prune + * + * Prune the dentries that are anonymous + * + */ +void shrink_dcache_anon(struct list_head *head) +{ + struct list_head *lp; + int found; + do { + found = 0; + spin_lock(&dcache_lock); + list_for_each(lp, head) { + struct dentry *this = list_entry(lp, struct dentry, d_hash); + if (!atomic_read(&this->d_count)) { + list_del(&this->d_lru); + list_add_tail(&this->d_lru, &dentry_unused); + found++; + } + } + spin_unlock(&dcache_lock); + prune_dcache(found); + } while(found); +} + /* * This is called from kswapd when we think we need some * more memory, but aren't really sure how much. So we @@ -691,6 +727,112 @@ static inline struct list_head * d_hash(struct dentry * parent, unsigned long ha return dentry_hashtable + (hash & D_HASHMASK); } +/** + * d_alloc_anon - allocate an anonymous dentry + * @inode: inode to allocate the dentry for + * + * This is similar to d_alloc_root. It is used by filesystems when + * creating a dentry for a given inode, often in the process of + * mapping a filehandle to a dentry. The returned dentry may be + * anonymous, or may have a full name (if the inode was already + * in the cache). The file system may need to make further + * efforts to connect this dentry into the dcache properly. + * + * When called on a directory inode, we must ensure that + * the inode only ever has one dentry. If a dentry is + * found, that is returned instead of allocating a new one. + * + * On successful return, the reference to the inode has been transferred + * to the dentry. If %NULL is returned (indicating kmalloc failure), + * the reference on the inode has not been released. + */ + +struct dentry * d_alloc_anon(struct inode *inode) +{ + struct dentry *tmp; + struct dentry *res; + + if ((res = d_find_alias(inode))) { + iput(inode); + return res; + } + + tmp = d_alloc(NULL, &(const struct qstr) {"",0,0}); + tmp->d_parent = tmp; /* make sure dput doesn't croak */ + + spin_lock(&dcache_lock); + if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) { + /* A directory can only have one dentry. + * This (now) has one, so use it. + */ + res = list_entry(inode->i_dentry.next, struct dentry, d_alias); + __dget_locked(res); + } else { + /* attach a disconnected dentry */ + res = tmp; + tmp = NULL; + if (res) { + res->d_sb = inode->i_sb; + res->d_parent = res; + res->d_inode = inode; + res->d_flags |= DCACHE_DISCONNECTED; + list_add(&res->d_alias, &inode->i_dentry); + list_add(&res->d_hash, &inode->i_sb->s_anon); + } + inode = NULL; /* don't drop reference */ + } + spin_unlock(&dcache_lock); + + if (inode) + iput(inode); + if (tmp) + dput(tmp); + return res; +} + + +/** + * d_splice_alias - splice a disconnected dentry into the tree if one exists + * @inode: the inode which may have a disconnected dentry + * @dentry: a negative dentry which we want to point to the inode. + * + * If inode is a directory and has a 'disconnected' dentry (i.e. IS_ROOT and + * DCACHE_DISCONNECTED), then d_move that in place of the given dentry + * and return it, else simply d_add the inode to the dentry and return NULL. + * + * This is (will be) needed in the lookup routine of any filesystem that is exportable + * (via knfsd) so that we can build dcache paths to directories effectively. + * + * If a dentry was found and moved, then it is returned. Otherwise NULL + * is returned. This matches the expected return value of ->lookup. + * + */ +struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) +{ + struct dentry *new = NULL; + + if (inode && S_ISDIR(inode->i_mode)) { + spin_lock(&dcache_lock); + if (!list_empty(&inode->i_dentry)) { + new = list_entry(inode->i_dentry.next, struct dentry, d_alias); + __dget_locked(new); + spin_unlock(&dcache_lock); + d_rehash(dentry); + d_move(new, dentry); + iput(inode); + } else { + /* d_instantiate takes dcache_lock, so we do it by hand */ + list_add(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + spin_unlock(&dcache_lock); + d_rehash(dentry); + } + } else + d_add(dentry, inode); + return new; +} + + /** * d_lookup - search for a dentry * @parent: parent dentry @@ -915,16 +1057,24 @@ void d_move(struct dentry * dentry, struct dentry * target) list_del(&dentry->d_child); list_del(&target->d_child); - /* Switch the parents and the names.. */ + /* Switch the names.. */ switch_names(dentry, target); - write_lock(&dparent_lock); - do_switch(dentry->d_parent, target->d_parent); - write_unlock(&dparent_lock); do_switch(dentry->d_name.len, target->d_name.len); do_switch(dentry->d_name.hash, target->d_name.hash); + /* ... and switch the parents */ + write_lock(&dparent_lock); + if (IS_ROOT(dentry)) { + dentry->d_parent = target->d_parent; + target->d_parent = target; + INIT_LIST_HEAD(&target->d_child); + } else { + do_switch(dentry->d_parent, target->d_parent); + + /* And add them back to the (new) parent lists */ + list_add(&target->d_child, &target->d_parent->d_subdirs); + } + write_unlock(&dparent_lock); - /* And add them back to the (new) parent lists */ - list_add(&target->d_child, &target->d_parent->d_subdirs); list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); spin_unlock(&dcache_lock); } diff --git a/fs/devfs/util.c b/fs/devfs/util.c index 8b265e011e1ca961d8349fc8bddfbdc543e23d9c..f9c955f6b61c3ed5c49388a4f45547da44369ef1 100644 --- a/fs/devfs/util.c +++ b/fs/devfs/util.c @@ -54,6 +54,8 @@ 20020326 Richard Gooch <rgooch@atnf.csiro.au> Fixed bitfield data type for <devfs_*alloc_devnum>. Made major bitfield type and initialiser 64 bit safe. + 20020413 Richard Gooch <rgooch@atnf.csiro.au> + Fixed shift warning on 64 bit machines. */ #include <linux/module.h> #include <linux/init.h> @@ -136,7 +138,7 @@ struct major_list #if BITS_PER_LONG == 32 # define INITIALISER64(low,high) (low), (high) #else -# define INITIALISER64(low,high) ( (high) << 32 | (low) ) +# define INITIALISER64(low,high) ( (unsigned long) (high) << 32 | (low) ) #endif /* Block majors already assigned: diff --git a/fs/exportfs/Makefile b/fs/exportfs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..268f2f73dbd0e42d62d558debed47f644d37d879 --- /dev/null +++ b/fs/exportfs/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the filesystem export support routines. + +O_TARGET := exportfs.o + +export-objs := expfs.o + +obj-y := expfs.o +obj-m := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c new file mode 100644 index 0000000000000000000000000000000000000000..00a75caee89fd71d1203d88cb24fda4552b00c5a --- /dev/null +++ b/fs/exportfs/expfs.c @@ -0,0 +1,516 @@ + +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/smp_lock.h> + +/** + * find_exported_dentry - helper routine to implement export_operations->decode_fh + * @sb: The &super_block identifying the filesystem + * @obj: An opaque identifier of the object to be found - passed to get_inode + * @parent: An optional opqaue identifier of the parent of the object. + * @acceptable: A function used to test possible &dentries to see of they are acceptable + * @context: A parameter to @acceptable so that it knows on what basis to judge. + * + * find_exported_dentry is the central helper routine to enable file systems to provide + * the decode_fh() export_operation. It's main task is to take an &inode, find or create an + * appropriate &dentry structure, and possibly splice this into the dcache in the + * correct place. + * + * The decode_fh() operation provided by the filesystem should call find_exported_dentry() + * with the same parameters that it received except that instead of the file handle fragment, + * pointers to opaque identifiers for the object and optionally its parent are passed. + * The default decode_fh routine passes one pointer to the start of the filehandle fragment, and + * one 8 bytes into the fragment. It is expected that most filesystems will take this + * approach, though the offset to the parent identifier may well be different. + * + * find_exported_dentry() will call get_dentry to get an dentry pointer from the file system. If + * any &dentry in the d_alias list is acceptable, it will be returned. Otherwise + * find_exported_dentry() will attempt to splice a new &dentry into the dcache using get_name() and + * get_parent() to find the appropriate place. + * + */ + +struct export_operations export_op_default; + +#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) + +#define dprintk(x, ...) do{}while(0) + +struct dentry * +find_exported_dentry(struct super_block *sb, void *obj, void *parent, + int (*acceptable)(void *context, struct dentry *de), + void *context) +{ + struct dentry *result = NULL; + struct dentry *target_dir; + int err; + struct export_operations *nops = sb->s_export_op; + struct list_head *le, *head; + struct dentry *toput = NULL; + int noprogress; + + + /* + * Attempt to find the inode. + */ + result = CALL(sb->s_export_op,get_dentry)(sb,obj); + err = -ESTALE; + if (result == NULL) + goto err_out; + if (IS_ERR(result)) { + err = PTR_ERR(result); + goto err_out; + } + if (S_ISDIR(result->d_inode->i_mode) && + (result->d_flags & DCACHE_DISCONNECTED)) { + /* it is an unconnected directory, we must connect it */ + ; + } else { + if (acceptable(context, result)) + return result; + if (S_ISDIR(result->d_inode->i_mode)) { + /* there is no other dentry, so fail */ + goto err_result; + } + /* try any other aliases */ + spin_lock(&dcache_lock); + head = &result->d_inode->i_dentry; + list_for_each(le, head) { + struct dentry *dentry = list_entry(le, struct dentry, d_alias); + dget_locked(dentry); + spin_unlock(&dcache_lock); + if (toput) + dput(toput); + toput = NULL; + if (dentry != result && + acceptable(context, dentry)) { + dput(result); + dentry->d_vfs_flags |= DCACHE_REFERENCED; + return dentry; + } + spin_lock(&dcache_lock); + toput = dentry; + } + spin_unlock(&dcache_lock); + if (toput) + dput(toput); + } + + /* It's a directory, or we are required to confirm the file's + * location in the tree based on the parent information + */ + dprintk("find_exported_dentry: need to look harder for %d/%d\n",kdev_t_to_nr(sb->s_dev),*(int*)obj); + if (S_ISDIR(result->d_inode->i_mode)) + target_dir = dget(result); + else { + if (parent == NULL) + goto err_result; + + target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent); + if (IS_ERR(target_dir)) + err = PTR_ERR(target_dir); + if (target_dir == NULL || IS_ERR(target_dir)) + goto err_result; + } + /* + * Now we need to make sure that target_dir is properly connected. + * It may already be, as the flag isn't always updated when connection + * happens. + * So, we walk up parent links until we find a connected directory, + * or we run out of directories. Then we find the parent, find + * the name of the child in that parent, and do a lookup. + * This should connect the child into the parent + * We then repeat. + */ + + /* it is possible that a confused file system might not let us complete the + * path to the root. For example, if get_parent returns a directory + * in which we cannot find a name for the child. While this implies a very + * sick filesystem we don't want it to cause knfsd to spin. Hence the noprogress + * counter. If we go through the loop 10 times (2 is probably enough) without + * getting anywhere, we just give up + */ + lock_kernel(); + noprogress= 0; + while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) { + struct dentry *pd = target_dir; + read_lock(&dparent_lock); + while (!IS_ROOT(pd) && + (pd->d_parent->d_flags & DCACHE_DISCONNECTED)) + pd = pd->d_parent; + + dget(pd); + read_unlock(&dparent_lock); + + if (!IS_ROOT(pd)) { + /* must have found a connected parent - great */ + pd->d_flags &= ~DCACHE_DISCONNECTED; + noprogress = 0; + } else if (pd == sb->s_root) { + printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n"); + pd->d_flags &= ~DCACHE_DISCONNECTED; + noprogress = 0; + } else { + /* we have hit the top of a disconnected path. Try + * to find parent and connect + * note: racing with some other process renaming a + * directory isn't much of a problem here. If someone + * renames the directory, it will end up properly connected, + * which is what we want + */ + struct dentry *ppd; + struct dentry *npd; + char nbuf[NAME_MAX+1]; + + down(&pd->d_inode->i_sem); + ppd = CALL(nops,get_parent)(pd); + up(&pd->d_inode->i_sem); + + if (IS_ERR(ppd)) { + err = PTR_ERR(ppd); + dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n", + pd->d_inode->i_ino, err); + dput(pd); + break; + } + dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino); + err = CALL(nops,get_name)(ppd, nbuf, pd); + if (err) { + dput(ppd); + if (err == -ENOENT) + /* some race between get_parent and get_name? + * just try again + */ + continue; + dput(pd); + break; + } + dprintk("find_exported_dentry: found name: %s\n", nbuf); + down(&ppd->d_inode->i_sem); + npd = lookup_one_len(nbuf, ppd, strlen(nbuf)); + up(&ppd->d_inode->i_sem); + if (IS_ERR(npd)) { + err = PTR_ERR(npd); + dprintk("find_exported_dentry: lookup failed: %d\n", err); + dput(ppd); + dput(pd); + break; + } + /* we didn't really want npd, we really wanted + * a side-effect of the lookup. + * hopefully, npd == pd, though it isn't really + * a problem if it isn't + */ + if (npd == pd) + noprogress = 0; + else + printk("find_exported_dentry: npd != pd\n"); + dput(npd); + dput(ppd); + if (IS_ROOT(pd)) { + /* something went wrong, we will have to give up */ + dput(pd); + break; + } + } + dput(pd); + } + + if (target_dir->d_flags & DCACHE_DISCONNECTED) { + /* something went wrong - oh-well */ + if (!err) + err = -ESTALE; + unlock_kernel(); + goto err_target; + } + /* if we weren't after a directory, have one more step to go */ + if (result != target_dir) { + struct dentry *nresult; + char nbuf[NAME_MAX+1]; + err = CALL(nops,get_name)(target_dir, nbuf, result); + if (!err) { + down(&target_dir->d_inode->i_sem); + nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); + up(&target_dir->d_inode->i_sem); + if (!IS_ERR(nresult)) { + if (nresult->d_inode) { + dput(result); + result = nresult; + } else + dput(nresult); + } + } + } + dput(target_dir); + unlock_kernel(); + /* now result is properly connected, it is our best bet */ + if (acceptable(context, result)) + return result; + /* one last try of the aliases.. */ + spin_lock(&dcache_lock); + head = &result->d_inode->i_dentry; + list_for_each(le, head) { + struct dentry *dentry = list_entry(le, struct dentry, d_alias); + dget_locked(dentry); + spin_unlock(&dcache_lock); + if (toput) dput(toput); + if (dentry != result && + acceptable(context, dentry)) { + dput(result); + dentry->d_vfs_flags |= DCACHE_REFERENCED; + return dentry; + } + spin_lock(&dcache_lock); + toput = dentry; + } + spin_unlock(&dcache_lock); + if (toput) + dput(toput); + + /* drat - I just cannot find anything acceptable */ + dput(result); + return ERR_PTR(-ESTALE); + + err_target: + dput(target_dir); + err_result: + dput(result); + err_out: + return ERR_PTR(err); +} + + + +static struct dentry *get_parent(struct dentry *child) +{ + /* get_parent cannot be supported generically, the locking + * is too icky. + * instead, we just return EACCES. If server reboots or inodes + * get flushed, you lose + */ + return ERR_PTR(-EACCES); +} + + +struct getdents_callback { + char *name; /* name that was found. It already points to a buffer NAME_MAX+1 is size */ + unsigned long ino; /* the inum we are looking for */ + int found; /* inode matched? */ + int sequence; /* sequence counter */ +}; + +/* + * A rather strange filldir function to capture + * the name matching the specified inode number. + */ +static int filldir_one(void * __buf, const char * name, int len, + loff_t pos, ino_t ino, unsigned int d_type) +{ + struct getdents_callback *buf = __buf; + int result = 0; + + buf->sequence++; + if (buf->ino == ino) { + memcpy(buf->name, name, len); + buf->name[len] = '\0'; + buf->found = 1; + result = -1; + } + return result; +} + +/** + * get_name - default export_operations->get_name function + * @dentry: the directory in which to find a name + * @name: a pointer to a %NAME_MAX+1 char buffer to store the name + * @child: the dentry for the child directory. + * + * calls readdir on the parent until it finds an entry with + * the same inode number as the child, and returns that. + */ +static int get_name(struct dentry *dentry, char *name, + struct dentry *child) +{ + struct inode *dir = dentry->d_inode; + int error; + struct file file; + struct getdents_callback buffer; + + error = -ENOTDIR; + if (!dir || !S_ISDIR(dir->i_mode)) + goto out; + error = -EINVAL; + if (!dir->i_fop) + goto out; + /* + * Open the directory ... + */ + error = init_private_file(&file, dentry, FMODE_READ); + if (error) + goto out; + error = -EINVAL; + if (!file.f_op->readdir) + goto out_close; + + buffer.name = name; + buffer.ino = child->d_inode->i_ino; + buffer.found = 0; + buffer.sequence = 0; + while (1) { + int old_seq = buffer.sequence; + + error = vfs_readdir(&file, filldir_one, &buffer); + + if (error < 0) + break; + + error = 0; + if (buffer.found) + break; + error = -ENOENT; + if (old_seq == buffer.sequence) + break; + } + +out_close: + if (file.f_op->release) + file.f_op->release(dir, &file); +out: + return error; +} + + +static struct dentry *export_iget(struct super_block *sb, unsigned long ino, __u32 generation) +{ + + /* iget isn't really right if the inode is currently unallocated!! + * This should really all be done inside each filesystem + * + * ext2fs' read_inode has been strengthed to return a bad_inode if the inode + * had been deleted. + * + * Currently we don't know the generation for parent directory, so a generation + * of 0 means "accept any" + */ + struct inode *inode; + struct dentry *result; + if (ino == 0) + return ERR_PTR(-ESTALE); + inode = iget(sb, ino); + if (inode == NULL) + return ERR_PTR(-ENOMEM); + if (is_bad_inode(inode) + || (generation && inode->i_generation != generation) + ) { + /* we didn't find the right inode.. */ + dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n", + inode->i_ino, + inode->i_nlink, atomic_read(&inode->i_count), + inode->i_generation, + generation); + + iput(inode); + return ERR_PTR(-ESTALE); + } + /* now to find a dentry. + * If possible, get a well-connected one + */ + result = d_alloc_anon(inode); + if (!result) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + result->d_vfs_flags |= DCACHE_REFERENCED; + return result; +} + + +static struct dentry *get_object(struct super_block *sb, void *vobjp) +{ + __u32 *objp = vobjp; + unsigned long ino = objp[0]; + __u32 generation = objp[1]; + + return export_iget(sb, ino, generation); +} + + +/** + * export_encode_fh - default export_operations->encode_fh function + * dentry: the dentry to encode + * fh: where to store the file handle fragment + * max_len: maximum length to store there + * connectable: whether to store parent infomation + * + * This default encode_fh function assumes that the 32 inode number + * is suitable for locating an inode, and that the generation number + * can be used to check that it is still valid. It places them in the + * filehandle fragment where export_decode_fh expects to find them. + */ +static int export_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, + int connectable) +{ + struct inode * inode = dentry->d_inode; + struct inode *parent = dentry->d_parent->d_inode; + int len = *max_len; + int type = 1; + + if (len < 2 || (connectable && len < 4)) + return 255; + + len = 2; + fh[0] = inode->i_ino; + fh[1] = inode->i_generation; + if (connectable && !S_ISDIR(inode->i_mode)) { + fh[2] = parent->i_ino; + fh[3] = parent->i_generation; + len = 4; + type = 2; + } + *max_len = len; + return type; +} + + +/** + * export_decode_fh - default export_operations->decode_fh function + * sb: The superblock + * fh: pointer to the file handle fragment + * fh_len: length of file handle fragment + * acceptable: function for testing acceptability of dentrys + * context: context for @acceptable + * + * This is the default decode_fh() function. + * a fileid_type of 1 indicates that the filehandlefragment + * just contains an object identifier understood by get_dentry. + * a fileid_type of 2 says that there is also a directory + * identifier 8 bytes in to the filehandlefragement. + */ +static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, + int fileid_type, + int (*acceptable)(void *context, struct dentry *de), + void *context) +{ + __u32 parent[2]; + parent[0] = parent[1] = 0; + if (fh_len < 2 || fileid_type > 2) + return NULL; + if (fileid_type == 2) { + if (fh_len > 2) parent[0] = fh[2]; + if (fh_len > 3) parent[1] = fh[3]; + } + return find_exported_dentry(sb, fh, parent, + acceptable, context); +} + +struct export_operations export_op_default = { + decode_fh: export_decode_fh, + encode_fh: export_encode_fh, + + get_name: get_name, + get_parent: get_parent, + get_dentry: get_object, +}; + +EXPORT_SYMBOL(export_op_default); +EXPORT_SYMBOL(find_exported_dentry); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 53ad39ffebd239989faec860e8096dbe5fea92f4..f7335b8739c640d08fb57919ecaa891e69498a07 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -79,10 +79,37 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry) if (!inode) return ERR_PTR(-EACCES); } + if (inode) + return d_splice_alias(inode, dentry); d_add(dentry, inode); return NULL; } +struct dentry *ext2_get_parent(struct dentry *child) +{ + unsigned long ino; + struct dentry *parent; + struct inode *inode; + struct dentry dotdot; + + dotdot.d_name.name = ".."; + dotdot.d_name.len = 2; + + ino = ext2_inode_by_name(child->d_inode, &dotdot); + if (!ino) + return ERR_PTR(-ENOENT); + inode = iget(child->d_inode->i_sb, ino); + + if (!inode) + return ERR_PTR(-EACCES); + parent = d_alloc_anon(inode); + if (!parent) { + iput(inode); + parent = ERR_PTR(-ENOMEM); + } + return parent; +} + /* * By the time this is called, we already have created * the directory cache entry for the new file, but it diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d4fcb94cac45802a47d849a7d48d0d4ef17c63af..83c73e46c70bce2e95496e6c27d84e0d7145db7b 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -209,6 +209,16 @@ static struct super_operations ext2_sops = { remount_fs: ext2_remount, }; +/* Yes, most of these are left as NULL!! + * A NULL value implies the default, which works with ext2-like file + * systems, but can be improved upon. + * Currently only get_parent is required. + */ +struct dentry *ext2_get_parent(struct dentry *child); +static struct export_operations ext2_export_ops = { + get_parent: ext2_get_parent, +}; + /* * This function has been shamelessly adapted from the msdos fs */ @@ -687,6 +697,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) * set up enough so that it can read an inode */ sb->s_op = &ext2_sops; + sb->s_export_op = &ext2_export_ops; sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) || !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) { diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 57cd3df3c18a6d5669ff681edfe3dc3f0edfdb04..b5e0e4500bb531d15794fabd41f652eca2688aea 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -17,6 +17,7 @@ #include <linux/smp_lock.h> #include <linux/msdos_fs.h> #include <linux/fat_cvf.h> +#include <linux/bitops.h> //#include <asm/uaccess.h> #include <asm/unaligned.h> @@ -430,7 +431,6 @@ struct dentry *fat_fh_to_dentry(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent) { struct inode *inode = NULL; - struct list_head *lp; struct dentry *result; if (fhtype != 3) @@ -480,31 +480,14 @@ struct dentry *fat_fh_to_dentry(struct super_block *sb, __u32 *fh, /* now to find a dentry. * If possible, get a well-connected one - * - * Given the way that we found the inode, it *MUST* be - * well-connected, but it is easiest to just copy the - * code. */ - spin_lock(&dcache_lock); - list_for_each(lp, &inode->i_dentry) { - result = list_entry(lp,struct dentry, d_alias); - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - dget_locked(result); - result->d_vfs_flags |= DCACHE_REFERENCED; - spin_unlock(&dcache_lock); - iput(inode); - return result; - } - } - spin_unlock(&dcache_lock); - result = d_alloc_root(inode); + result = d_alloc_anon(inode); if (result == NULL) { iput(inode); return ERR_PTR(-ENOMEM); } - result->d_flags |= DCACHE_NFSD_DISCONNECTED; + result->d_vfs_flags |= DCACHE_REFERENCED; return result; - } diff --git a/fs/intermezzo/dir.c b/fs/intermezzo/dir.c index 8fbd8f177dc17222e168a03e7a582864454d6145..7617c500c35ff8b02fc044afc80161133eeda700 100644 --- a/fs/intermezzo/dir.c +++ b/fs/intermezzo/dir.c @@ -192,7 +192,7 @@ struct dentry *presto_ilookup(struct inode *dir, struct dentry *dentry, } d_instantiate(dentry, inode); - dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; /* NFS hack */ + dentry->d_flags |= DCACHE_DISCONNECTED; /* NFS hack */ EXIT; return NULL; diff --git a/fs/jffs/Makefile b/fs/jffs/Makefile index 546b9a739a01584fa40a0cbea72e7d0efbdb750e..878887381d8c762f5a2692b740497418f39e0db1 100644 --- a/fs/jffs/Makefile +++ b/fs/jffs/Makefile @@ -3,13 +3,6 @@ # # $Id: Makefile,v 1.11 2001/09/25 20:59:41 dwmw2 Exp $ # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now in the main makefile... - -list-multi := jffs.o jffs-objs := jffs_fm.o intrep.o diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index dba6e88fc67b4807e97da7525b3a3ce7145eabee..9e21031fcbfc293cf3610e8d128c8a1366733b6f 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -515,7 +515,7 @@ static inline void sync_metapage(metapage_t *mp) lock_page(page); /* we're done with this page - no need to check for errors */ - if (page->buffers) { + if (page_has_buffers(page)) { writeout_one_page(page); waitfor_one_page(page); } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 967700a34b439627821d3234c7e3a229033a0857..4ffd5c531027cd026c26e57b9c7960a92b9b62b3 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -233,6 +233,10 @@ static void exp_fsid_hash(struct svc_client *clp, struct svc_export *exp) list_add(&exp->ex_fsid_hash, head); } +extern struct dentry * +find_exported_dentry(struct super_block *sb, void *obj, void *parent, + int (*acceptable)(void *context, struct dentry *de), + void *context); /* * Export a file system. */ @@ -316,12 +320,17 @@ exp_export(struct nfsctl_export *nxp) || (nxp->ex_flags & NFSEXP_FSID)) && (inode->i_sb->s_op->read_inode + || inode->i_sb->s_export_op || inode->i_sb->s_op->fh_to_dentry)) /* Ok, we can export it */; else { dprintk("exp_export: export of invalid fs type.\n"); goto finish; } + if (inode->i_sb->s_export_op && + !inode->i_sb->s_export_op->find_exported_dentry) + inode->i_sb->s_export_op->find_exported_dentry = + find_exported_dentry; if ((parent = exp_child(clp, inode->i_sb, nd.dentry)) != NULL) { dprintk("exp_export: export not valid (Rule 3).\n"); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index be2e78d9f5f9fd3fd5a474df30a6282568b9b544..634a40acc689440a554b86113527d02b051a7761 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -30,6 +30,7 @@ #include <linux/nfsd/cache.h> #include <linux/nfsd/xdr.h> #include <linux/nfsd/syscall.h> +#include <linux/nfsd/interface.h> #include <asm/uaccess.h> @@ -379,6 +380,7 @@ static struct file_system_type nfsd_fs_type = { static int __init init_nfsd(void) { printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); + nfsd_stat_init(); /* Statistics */ nfsd_cache_init(); /* RPC reply cache */ nfsd_export_init(); /* Exports table */ diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 6a3f490edcbef7e02e9b8fca9ac3c86129420109..db637bc733d8ce6e0c49c8332b1de68d444db942 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -6,6 +6,7 @@ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org> * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999 + * ... and again Southern-Winter 2001 to support export_operations */ #include <linux/sched.h> @@ -29,6 +30,11 @@ static int nfsd_nr_verified; static int nfsd_nr_put; +extern struct export_operations export_op_default; + +#define CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun) + + struct nfsd_getdents_callback { char *name; /* name that was found. It already points to a buffer NAME_MAX+1 is size */ @@ -60,15 +66,6 @@ dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name); return result; } -/** - * nfsd_get_name - default nfsd_operations->get_name function - * @dentry: the directory in which to find a name - * @name: a pointer to a %NAME_MAX+1 char buffer to store the name - * @child: the dentry for the child directory. - * - * calls readdir on the parent until it finds an entry with - * the same inode number as the child, and returns that. - */ static int nfsd_get_name(struct dentry *dentry, char *name, struct dentry *child) { @@ -120,9 +117,6 @@ static int nfsd_get_name(struct dentry *dentry, char *name, return error; } -/* this should be provided by each filesystem in an nfsd_operations interface as - * iget isn't really the right interface - */ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation) { @@ -136,7 +130,6 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 * of 0 means "accept any" */ struct inode *inode; - struct list_head *lp; struct dentry *result; if (ino == 0) return ERR_PTR(-ESTALE); @@ -159,24 +152,12 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 /* now to find a dentry. * If possible, get a well-connected one */ - spin_lock(&dcache_lock); - list_for_each(lp, &inode->i_dentry) { - result = list_entry(lp,struct dentry, d_alias); - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - dget_locked(result); - result->d_vfs_flags |= DCACHE_REFERENCED; - spin_unlock(&dcache_lock); - iput(inode); - return result; - } - } - spin_unlock(&dcache_lock); - result = d_alloc_root(inode); - if (result == NULL) { + result = d_alloc_anon(inode); + if (!result) { iput(inode); return ERR_PTR(-ENOMEM); } - result->d_flags |= DCACHE_NFSD_DISCONNECTED; + result->d_vfs_flags |= DCACHE_REFERENCED; return result; } @@ -215,7 +196,7 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) #ifdef NFSD_PARANOIA if (!IS_ROOT(target)) printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name); - if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED)) + if (!(target->d_flags & DCACHE_DISCONNECTED)) printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name); #endif tdentry = d_alloc(parent, name); @@ -223,13 +204,6 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) return -ENOMEM; d_move(target, tdentry); - /* tdentry will have been made a "child" of target (the parent of target) - * make it an IS_ROOT instead - */ - spin_lock(&dcache_lock); - list_del_init(&tdentry->d_child); - tdentry->d_parent = tdentry; - spin_unlock(&dcache_lock); d_rehash(target); dput(tdentry); @@ -237,9 +211,9 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) * the children are connected, but it must be a singluar (non-forking) * branch */ - if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) { + if (!(parent->d_flags & DCACHE_DISCONNECTED)) { while (target) { - target->d_flags &= ~DCACHE_NFSD_DISCONNECTED; + target->d_flags &= ~DCACHE_DISCONNECTED; parent = target; spin_lock(&dcache_lock); if (list_empty(&parent->d_subdirs)) @@ -249,9 +223,12 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) #ifdef NFSD_PARANOIA /* must be only child */ if (target->d_child.next != &parent->d_subdirs - || target->d_child.prev != &parent->d_subdirs) + || target->d_child.prev != &parent->d_subdirs) { printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n", parent->d_name.name, target->d_name.name); + spin_unlock(&dcache_lock); + return 0; + } #endif } spin_unlock(&dcache_lock); @@ -261,7 +238,6 @@ int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name) } /* this routine finds the dentry of the parent of a given directory - * it should be in the filesystem accessed by nfsd_operations * it assumes lookup("..") works. */ struct dentry *nfsd_findparent(struct dentry *child) @@ -305,7 +281,7 @@ struct dentry *nfsd_findparent(struct dentry *child) pdentry = d_alloc_root(tdentry->d_inode); if (pdentry) { igrab(tdentry->d_inode); - pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED; + pdentry->d_flags |= DCACHE_DISCONNECTED; } } if (pdentry == NULL) @@ -412,14 +388,14 @@ find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int ne down(&sb->s_nfsd_free_path_sem); result = nfsd_get_dentry(sb, datap, len, fhtype, 0); if (IS_ERR(result) - || !(result->d_flags & DCACHE_NFSD_DISCONNECTED) + || !(result->d_flags & DCACHE_DISCONNECTED) || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) { up(&sb->s_nfsd_free_path_sem); err = PTR_ERR(result); if (IS_ERR(result)) goto err_out; - if ((result->d_flags & DCACHE_NFSD_DISCONNECTED)) + if ((result->d_flags & DCACHE_DISCONNECTED)) nfsdstats.fh_anon++; return result; } @@ -457,7 +433,7 @@ find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int ne dentry = dget(result); } - while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) { + while(dentry->d_flags & DCACHE_DISCONNECTED) { /* LOOP INVARIANT */ /* haven't found a place in the tree yet, but we do have a free path * from dentry down to result, and dentry is a directory. @@ -526,6 +502,46 @@ find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int ne return ERR_PTR(err); } +/* + * our acceptability function. + * if NOSUBTREECHECK, accept anything + * if not, require that we can walk up to exp->ex_dentry + * doing some checks on the 'x' bits + */ +int nfsd_acceptable(void *expv, struct dentry *dentry) +{ + struct svc_export *exp = expv; + int rv; + struct dentry *tdentry; + + if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) + return 1; + + dget(dentry); + read_lock(&dparent_lock); + for (tdentry = dentry; + tdentry != exp->ex_dentry && ! IS_ROOT(tdentry); + (dget(tdentry->d_parent), + dput(tdentry), + tdentry = tdentry->d_parent) + ) { + /* make sure parents give x permission to user */ + int err; + read_unlock(&dparent_lock); + err = permission(tdentry->d_parent->d_inode, S_IXOTH); + read_lock(&dparent_lock); + if (err < 0) + break; + } + read_unlock(&dparent_lock); + if (tdentry != exp->ex_dentry) + dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name); + rv = (tdentry == exp->ex_dentry); + dput(tdentry); + return rv; +} + + /* * Perform sanity checks on the dentry in a client's file handle. * @@ -553,6 +569,8 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) kdev_t xdev = NODEV; ino_t xino = 0; __u32 *datap=NULL; + __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ + int fileid_type; int data_left = fh->fh_size/4; int nfsdev; int fsid = 0; @@ -560,8 +578,8 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) error = nfserr_stale; if (rqstp->rq_vers == 3) error = nfserr_badhandle; + if (fh->fh_version == 1) { - datap = fh->fh_auth; if (--data_left<0) goto out; switch (fh->fh_auth_type) { @@ -602,7 +620,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (!exp) { /* export entry revoked */ - nfsdstats.fh_stale++; goto out; } @@ -626,27 +643,35 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (rqstp->rq_vers == 3) error = nfserr_badhandle; - if (fh->fh_version == 1) { - /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup, - * then should use that */ - switch (fh->fh_fileid_type) { - case 0: - dentry = dget(exp->ex_dentry); - break; - default: - dentry = find_fh_dentry(exp->ex_dentry->d_sb, - datap, data_left, fh->fh_fileid_type, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); - } - } else { - __u32 tfh[3]; + if (fh->fh_version != 1) { tfh[0] = fh->ofh_ino; tfh[1] = fh->ofh_generation; tfh[2] = fh->ofh_dirino; - dentry = find_fh_dentry(exp->ex_dentry->d_sb, - tfh, 3, fh->ofh_dirino?2:1, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); + datap = tfh; + data_left = 3; + if (fh->ofh_dirino == 0) + fileid_type = 1; + else + fileid_type = 2; + } else + fileid_type = fh->fh_fileid_type; + + if (fileid_type == 0) + dentry = dget(exp->ex_dentry); + else { + struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; + if (nop) + dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, + datap, data_left, + fileid_type, + nfsd_acceptable, exp); + else + dentry = find_fh_dentry(exp->ex_dentry->d_sb, + datap, data_left, fileid_type, + !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); } + if (dentry == NULL) + goto out; if (IS_ERR(dentry)) { if (PTR_ERR(dentry) != -EINVAL) error = nfserrno(PTR_ERR(dentry)); @@ -654,7 +679,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) } #ifdef NFSD_PARANOIA if (S_ISDIR(dentry->d_inode->i_mode) && - (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) { + (dentry->d_flags & DCACHE_DISCONNECTED)) { printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); } @@ -681,7 +706,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) * write call). */ - /* When is type ever negative? */ + /* Type can be negative when creating hardlinks - not to a dir */ if (type > 0 && (inode->i_mode & S_IFMT) != type) { error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; goto out; @@ -693,10 +718,14 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) /* * Security: Check that the export is valid for dentry <gam3@acm.org> + * This is only needed with subtree_check, and if export_operations is + * not being used - export_operations does the check via the "acceptable" + * callback */ error = 0; - if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) { + if (exp->ex_mnt->mnt_sb->s_export_op == NULL && + !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) { if (exp->ex_dentry != dentry) { struct dentry *tdentry = dentry; @@ -718,13 +747,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) && !(tdentry->d_inode->i_mode & S_IXOTH) ) { error = nfserr_stale; - nfsdstats.fh_stale++; dprintk("fh_verify: no root_squashed access.\n"); } } while ((tdentry != tdentry->d_parent)); if (exp->ex_dentry != tdentry) { error = nfserr_stale; - nfsdstats.fh_stale++; printk("nfsd Security: %s/%s bad export.\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -746,9 +773,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) } #endif out: + if (error == nfserr_stale) + nfsdstats.fh_stale++; return error; } + /* * Compose a file handle for an NFS reply. * @@ -759,6 +789,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) inline int _fh_update(struct dentry *dentry, struct svc_export *exp, __u32 *datap, int *maxsize) { + struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; struct super_block *sb = dentry->d_sb; if (dentry == exp->ex_dentry) { @@ -766,6 +797,10 @@ inline int _fh_update(struct dentry *dentry, struct svc_export *exp, return 0; } + if (nop) + return CALL(nop,encode_fh)(dentry, datap, maxsize, + !(exp->ex_flags&NFSEXP_NOSUBTREECHECK)); + if (sb->s_op->dentry_to_fh) { int need_parent = !S_ISDIR(dentry->d_inode->i_mode) && !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); @@ -870,11 +905,11 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st _fh_update(dentry, exp, datap, &size); fhp->fh_handle.fh_size += size*4; } + if (fhp->fh_handle.fh_fileid_type == 255) + return nfserr_opnotsupp; } nfsd_nr_verified++; - if (fhp->fh_handle.fh_fileid_type == 255) - return nfserr_opnotsupp; return 0; } @@ -906,6 +941,8 @@ fh_update(struct svc_fh *fhp) fhp->fh_handle.fh_fileid_type = _fh_update(dentry, fhp->fh_export, datap, &size); fhp->fh_handle.fh_size += size*4; + if (fhp->fh_handle.fh_fileid_type == 255) + return nfserr_opnotsupp; } out: return 0; @@ -938,3 +975,4 @@ fh_put(struct svc_fh *fhp) } return; } + diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index f23b55650a8cff42aa4fed9f954e2729ce98f1ac..2ab40353bfeec16b7719c66302eb3234d0ae620c 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls.c + * linux/fs/nls_base.c * * Native language support--charsets and unicode translations. * By Gordon Chaffee 1996, 1997 @@ -93,7 +93,7 @@ utf8_mbstowcs(wchar_t *pwcs, const __u8 *s, int n) ip++; n--; } else { - op += size; + op++; ip += size; n -= size; } diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index c2aaee758d4b2634d6c98e182c0efe8725304380..485ec26ffb6b915358b6ae834fa9f2173e0aaa78 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1249,7 +1249,6 @@ struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data, int len, int fhtype, int parent) { struct cpu_key key ; struct inode *inode = NULL ; - struct list_head *lp; struct dentry *result; /* fhtype happens to reflect the number of u32s encoded. @@ -1304,24 +1303,12 @@ struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data, /* now to find a dentry. * If possible, get a well-connected one */ - spin_lock(&dcache_lock); - list_for_each(lp, &inode->i_dentry) { - result = list_entry(lp,struct dentry, d_alias); - if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { - dget_locked(result); - result->d_vfs_flags |= DCACHE_REFERENCED; - spin_unlock(&dcache_lock); - iput(inode); - return result; - } - } - spin_unlock(&dcache_lock); - result = d_alloc_root(inode); + result = d_alloc_anon(inode); if (result == NULL) { iput(inode); return ERR_PTR(-ENOMEM); } - result->d_flags |= DCACHE_NFSD_DISCONNECTED; + result->d_vfs_flags |= DCACHE_REFERENCED; return result; } diff --git a/fs/super.c b/fs/super.c index 672a7418030720cf9481ef366faba459a465d5aa..8f84b182f19980782ba466da11d2d1ca242dd13a 100644 --- a/fs/super.c +++ b/fs/super.c @@ -51,6 +51,7 @@ static struct super_block *alloc_super(void) INIT_LIST_HEAD(&s->s_locked_inodes); INIT_LIST_HEAD(&s->s_files); INIT_LIST_HEAD(&s->s_instances); + INIT_LIST_HEAD(&s->s_anon); init_rwsem(&s->s_umount); sema_init(&s->s_lock, 1); down_write(&s->s_umount); @@ -192,6 +193,7 @@ void generic_shutdown_super(struct super_block *sb) if (root) { sb->s_root = NULL; shrink_dcache_parent(root); + shrink_dcache_anon(&sb->s_anon); dput(root); fsync_super(sb); lock_super(sb); diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h index 00e98dd57955a2ed5220981b3e3d052106a6da49..dd98eb28d55acfd4b524c4c12db6a717f86ea5bb 100644 --- a/include/asm-i386/bitops.h +++ b/include/asm-i386/bitops.h @@ -34,7 +34,7 @@ * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -static __inline__ void set_bit(int nr, volatile void * addr) +static __inline__ void set_bit(int nr, volatile unsigned long * addr) { __asm__ __volatile__( LOCK_PREFIX "btsl %1,%0" @@ -51,7 +51,7 @@ static __inline__ void set_bit(int nr, volatile void * addr) * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -static __inline__ void __set_bit(int nr, volatile void * addr) +static __inline__ void __set_bit(int nr, volatile unsigned long * addr) { __asm__( "btsl %1,%0" @@ -69,7 +69,7 @@ static __inline__ void __set_bit(int nr, volatile void * addr) * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() * in order to ensure changes are visible on other processors. */ -static __inline__ void clear_bit(int nr, volatile void * addr) +static __inline__ void clear_bit(int nr, volatile unsigned long * addr) { __asm__ __volatile__( LOCK_PREFIX "btrl %1,%0" @@ -77,7 +77,7 @@ static __inline__ void clear_bit(int nr, volatile void * addr) :"Ir" (nr)); } -static __inline__ void __clear_bit(int nr, volatile void * addr) +static __inline__ void __clear_bit(int nr, volatile unsigned long * addr) { __asm__ __volatile__( "btrl %1,%0" @@ -96,7 +96,7 @@ static __inline__ void __clear_bit(int nr, volatile void * addr) * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -static __inline__ void __change_bit(int nr, volatile void * addr) +static __inline__ void __change_bit(int nr, volatile unsigned long * addr) { __asm__ __volatile__( "btcl %1,%0" @@ -113,7 +113,7 @@ static __inline__ void __change_bit(int nr, volatile void * addr) * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -static __inline__ void change_bit(int nr, volatile void * addr) +static __inline__ void change_bit(int nr, volatile unsigned long * addr) { __asm__ __volatile__( LOCK_PREFIX "btcl %1,%0" @@ -129,7 +129,7 @@ static __inline__ void change_bit(int nr, volatile void * addr) * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -static __inline__ int test_and_set_bit(int nr, volatile void * addr) +static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr) { int oldbit; @@ -149,7 +149,7 @@ static __inline__ int test_and_set_bit(int nr, volatile void * addr) * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -static __inline__ int __test_and_set_bit(int nr, volatile void * addr) +static __inline__ int __test_and_set_bit(int nr, volatile unsigned long * addr) { int oldbit; @@ -168,7 +168,7 @@ static __inline__ int __test_and_set_bit(int nr, volatile void * addr) * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -static __inline__ int test_and_clear_bit(int nr, volatile void * addr) +static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr) { int oldbit; @@ -188,7 +188,7 @@ static __inline__ int test_and_clear_bit(int nr, volatile void * addr) * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr) { int oldbit; @@ -200,7 +200,7 @@ static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) } /* WARNING: non atomic and it can be reordered! */ -static __inline__ int __test_and_change_bit(int nr, volatile void * addr) +static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr) { int oldbit; @@ -219,7 +219,7 @@ static __inline__ int __test_and_change_bit(int nr, volatile void * addr) * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -static __inline__ int test_and_change_bit(int nr, volatile void * addr) +static __inline__ int test_and_change_bit(int nr, volatile unsigned long* addr) { int oldbit; @@ -239,12 +239,12 @@ static __inline__ int test_and_change_bit(int nr, volatile void * addr) static int test_bit(int nr, const volatile void * addr); #endif -static __inline__ int constant_test_bit(int nr, const volatile void * addr) +static __inline__ int constant_test_bit(int nr, const volatile unsigned long * addr) { return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; } -static __inline__ int variable_test_bit(int nr, volatile void * addr) +static __inline__ int variable_test_bit(int nr, volatile unsigned long * addr) { int oldbit; @@ -268,7 +268,7 @@ static __inline__ int variable_test_bit(int nr, volatile void * addr) * Returns the bit-number of the first zero bit, not the number of the byte * containing a bit. */ -static __inline__ int find_first_zero_bit(void * addr, unsigned size) +static __inline__ int find_first_zero_bit(unsigned long * addr, unsigned size) { int d0, d1, d2; int res; @@ -300,7 +300,7 @@ static __inline__ int find_first_zero_bit(void * addr, unsigned size) * Returns the bit-number of the first set bit, not the number of the byte * containing a bit. */ -static __inline__ int find_first_bit(void * addr, unsigned size) +static __inline__ int find_first_bit(unsigned long * addr, unsigned size) { int d0, d1; int res; @@ -326,7 +326,7 @@ static __inline__ int find_first_bit(void * addr, unsigned size) * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -static __inline__ int find_next_zero_bit (void * addr, int size, int offset) +static __inline__ int find_next_zero_bit(unsigned long * addr, int size, int offset) { unsigned long * p = ((unsigned long *) addr) + (offset >> 5); int set = 0, bit = offset & 31, res; @@ -359,9 +359,9 @@ static __inline__ int find_next_zero_bit (void * addr, int size, int offset) * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -static __inline__ int find_next_bit(void * addr, int size, int offset) +static __inline__ int find_next_bit(unsigned long *addr, int size, int offset) { - unsigned long * p = ((unsigned long *) addr) + (offset >> 5); + unsigned long * p = addr + (offset >> 5); int set = 0, bit = offset & 31, res; if (bit) { @@ -382,7 +382,7 @@ static __inline__ int find_next_bit(void * addr, int size, int offset) /* * No set bit yet, search remaining full words for a bit */ - res = find_first_bit (p, size - 32 * (p - (unsigned long *) addr)); + res = find_first_bit (p, size - 32 * (p - addr)); return (offset + set + res); } diff --git a/include/asm-ia64/acpi-ext.h b/include/asm-ia64/acpi-ext.h deleted file mode 100644 index a5e1a2c5fd413604a433dfc5f3c876011084e8bc..0000000000000000000000000000000000000000 --- a/include/asm-ia64/acpi-ext.h +++ /dev/null @@ -1,323 +0,0 @@ -#ifndef _ASM_IA64_ACPI_EXT_H -#define _ASM_IA64_ACPI_EXT_H - -/* - * Advanced Configuration and Power Infterface - * Based on 'ACPI Specification 1.0b' Febryary 2, 1999 - * and 'IA-64 Extensions to the ACPI Specification' Rev 0.6 - * - * Copyright (C) 1999 VA Linux Systems - * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> - * Copyright (C) 2000 Intel Corp. - * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> - * ACPI 2.0 specification - */ - -#include <linux/config.h> -#include <linux/types.h> -#include <linux/mm.h> - -#pragma pack(1) -#define ACPI_RSDP_SIG "RSD PTR " /* Trailing space required */ -#define ACPI_RSDP_SIG_LEN 8 -typedef struct { - char signature[8]; - u8 checksum; - char oem_id[6]; - u8 revision; - u32 rsdt; - u32 length; - struct acpi_xsdt *xsdt; - u8 ext_checksum; - u8 reserved[3]; -} acpi20_rsdp_t; - -typedef struct { - char signature[4]; - u32 length; - u8 revision; - u8 checksum; - char oem_id[6]; - char oem_table_id[8]; - u32 oem_revision; - u32 creator_id; - u32 creator_revision; -} acpi_desc_table_hdr_t; - -#define ACPI_RSDT_SIG "RSDT" -#define ACPI_RSDT_SIG_LEN 4 -typedef struct { - acpi_desc_table_hdr_t header; - u8 reserved[4]; - u32 entry_ptrs[1]; /* Not really . . . */ -} acpi20_rsdt_t; - -#define ACPI_XSDT_SIG "XSDT" -#define ACPI_XSDT_SIG_LEN 4 -typedef struct acpi_xsdt { - acpi_desc_table_hdr_t header; - unsigned long entry_ptrs[1]; /* Not really . . . */ -} acpi_xsdt_t; - -/* Common structures for ACPI 2.0 and 0.71 */ - -typedef struct acpi_entry_iosapic { - u8 type; - u8 length; - u8 id; - u8 reserved; - u32 irq_base; /* start of IRQ's this IOSAPIC is responsible for. */ - unsigned long address; /* Address of this IOSAPIC */ -} acpi_entry_iosapic_t; - -/* Local SAPIC flags */ -#define LSAPIC_ENABLED (1<<0) -#define LSAPIC_PERFORMANCE_RESTRICTED (1<<1) -#define LSAPIC_PRESENT (1<<2) - -/* Defines legacy IRQ->pin mapping */ -typedef struct { - u8 type; - u8 length; - u8 bus; /* Constant 0 == ISA */ - u8 isa_irq; /* ISA IRQ # */ - u32 pin; /* called vector in spec; really IOSAPIC pin number */ - u16 flags; /* Edge/Level trigger & High/Low active */ -} acpi_entry_int_override_t; - -#define INT_OVERRIDE_ACTIVE_LOW 0x03 -#define INT_OVERRIDE_LEVEL_TRIGGER 0x0d - -/* IA64 ext 0.71 */ - -typedef struct { - char signature[8]; - u8 checksum; - char oem_id[6]; - char reserved; /* Must be 0 */ - struct acpi_rsdt *rsdt; -} acpi_rsdp_t; - -typedef struct acpi_rsdt { - acpi_desc_table_hdr_t header; - u8 reserved[4]; - unsigned long entry_ptrs[1]; /* Not really . . . */ -} acpi_rsdt_t; - -#define ACPI_SAPIC_SIG "SPIC" -#define ACPI_SAPIC_SIG_LEN 4 -typedef struct { - acpi_desc_table_hdr_t header; - u8 reserved[4]; - unsigned long interrupt_block; -} acpi_sapic_t; - -/* SAPIC structure types */ -#define ACPI_ENTRY_LOCAL_SAPIC 0 -#define ACPI_ENTRY_IO_SAPIC 1 -#define ACPI_ENTRY_INT_SRC_OVERRIDE 2 -#define ACPI_ENTRY_PLATFORM_INT_SOURCE 3 /* Unimplemented */ - -typedef struct acpi_entry_lsapic { - u8 type; - u8 length; - u16 acpi_processor_id; - u16 flags; - u8 id; - u8 eid; -} acpi_entry_lsapic_t; - -typedef struct { - u8 type; - u8 length; - u16 flags; - u8 int_type; - u8 id; - u8 eid; - u8 iosapic_vector; - u8 reserved[4]; - u32 global_vector; -} acpi_entry_platform_src_t; - -/* ACPI 2.0 with 1.3 errata specific structures */ - -#define ACPI_MADT_SIG "APIC" -#define ACPI_MADT_SIG_LEN 4 -typedef struct { - acpi_desc_table_hdr_t header; - u32 lapic_address; - u32 flags; -} acpi_madt_t; - -/* acpi 2.0 MADT flags */ -#define MADT_PCAT_COMPAT (1<<0) - -/* acpi 2.0 MADT structure types */ -#define ACPI20_ENTRY_LOCAL_APIC 0 -#define ACPI20_ENTRY_IO_APIC 1 -#define ACPI20_ENTRY_INT_SRC_OVERRIDE 2 -#define ACPI20_ENTRY_NMI_SOURCE 3 -#define ACPI20_ENTRY_LOCAL_APIC_NMI 4 -#define ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE 5 -#define ACPI20_ENTRY_IO_SAPIC 6 -#define ACPI20_ENTRY_LOCAL_SAPIC 7 -#define ACPI20_ENTRY_PLATFORM_INT_SOURCE 8 - -typedef struct acpi20_entry_lsapic { - u8 type; - u8 length; - u8 acpi_processor_id; - u8 id; - u8 eid; - u8 reserved[3]; - u32 flags; -} acpi20_entry_lsapic_t; - -typedef struct acpi20_entry_lapic_addr_override { - u8 type; - u8 length; - u8 reserved[2]; - unsigned long lapic_address; -} acpi20_entry_lapic_addr_override_t; - -typedef struct { - u8 type; - u8 length; - u16 flags; - u8 int_type; - u8 id; - u8 eid; - u8 iosapic_vector; - u32 global_vector; -} acpi20_entry_platform_src_t; - -/* constants for interrupt routing API for device drivers */ -#define ACPI20_ENTRY_PIS_PMI 1 -#define ACPI20_ENTRY_PIS_INIT 2 -#define ACPI20_ENTRY_PIS_CPEI 3 -#define ACPI_MAX_PLATFORM_IRQS 4 - -#define ACPI_SPCRT_SIG "SPCR" -#define ACPI_SPCRT_SIG_LEN 4 - -#define ACPI_DBGPT_SIG "DBGP" -#define ACPI_DBGPT_SIG_LEN 4 - -extern int acpi20_parse(acpi20_rsdp_t *); -extern int acpi20_early_parse(acpi20_rsdp_t *); -extern int acpi_parse(acpi_rsdp_t *); -extern const char *acpi_get_sysname (void); -extern int acpi_request_vector(u32 int_type); -extern void (*acpi_idle) (void); /* power-management idle function, if any */ -#ifdef CONFIG_NUMA -extern cnodeid_t paddr_to_nid(unsigned long paddr); -#endif - -/* - * ACPI 2.0 SRAT Table - * http://www.microsoft.com/HWDEV/design/SRAT.htm - */ - -typedef struct acpi_srat { - acpi_desc_table_hdr_t header; - u32 table_revision; - u64 reserved; -} acpi_srat_t; - -typedef struct srat_cpu_affinity { - u8 type; - u8 length; - u8 proximity_domain; - u8 apic_id; - u32 flags; - u8 local_sapic_eid; - u8 reserved[7]; -} srat_cpu_affinity_t; - -typedef struct srat_memory_affinity { - u8 type; - u8 length; - u8 proximity_domain; - u8 reserved[5]; - u32 base_addr_lo; - u32 base_addr_hi; - u32 length_lo; - u32 length_hi; - u32 memory_type; - u32 flags; - u64 reserved2; -} srat_memory_affinity_t; - -/* ACPI 2.0 SRAT structure */ -#define ACPI_SRAT_SIG "SRAT" -#define ACPI_SRAT_SIG_LEN 4 -#define ACPI_SRAT_REVISION 1 - -#define SRAT_CPU_STRUCTURE 0 -#define SRAT_MEMORY_STRUCTURE 1 - -/* Only 1 flag for cpu affinity structure! */ -#define SRAT_CPU_FLAGS_ENABLED 0x00000001 - -#define SRAT_MEMORY_FLAGS_ENABLED 0x00000001 -#define SRAT_MEMORY_FLAGS_HOTREMOVABLE 0x00000002 - -/* ACPI 2.0 address range types */ -#define ACPI_ADDRESS_RANGE_MEMORY 1 -#define ACPI_ADDRESS_RANGE_RESERVED 2 -#define ACPI_ADDRESS_RANGE_ACPI 3 -#define ACPI_ADDRESS_RANGE_NVS 4 - -#define NODE_ARRAY_INDEX(x) ((x) / 8) /* 8 bits/char */ -#define NODE_ARRAY_OFFSET(x) ((x) % 8) /* 8 bits/char */ -#define MAX_PXM_DOMAINS (256) - -#ifdef CONFIG_DISCONTIGMEM -/* - * List of node memory chunks. Filled when parsing SRAT table to - * obtain information about memory nodes. -*/ - -struct node_memory_chunk_s { - unsigned long start_paddr; - unsigned long size; - int pxm; // proximity domain of node - int nid; // which cnode contains this chunk? - int bank; // which mem bank on this node -}; - -extern struct node_memory_chunk_s node_memory_chunk[PLAT_MAXCLUMPS]; // temporary? - -struct node_cpuid_s { - u16 phys_id; /* id << 8 | eid */ - int pxm; // proximity domain of cpu - int nid; -}; -extern struct node_cpuid_s node_cpuid[NR_CPUS]; - -extern int pxm_to_nid_map[MAX_PXM_DOMAINS]; /* _PXM to logical node ID map */ -extern int nid_to_pxm_map[PLAT_MAX_COMPACT_NODES]; /* logical node ID to _PXM map */ -extern int numnodes; /* total number of nodes in system */ -extern int num_memory_chunks; /* total number of memory chunks */ - -/* - * ACPI 2.0 SLIT Table - * http://devresource.hp.com/devresource/Docs/TechPapers/IA64/slit.pdf - */ - -typedef struct acpi_slit { - acpi_desc_table_hdr_t header; - u64 localities; - u8 entries[1]; /* dummy, real size = locality^2 */ -} acpi_slit_t; - -extern u8 acpi20_slit[PLAT_MAX_COMPACT_NODES * PLAT_MAX_COMPACT_NODES]; - -#define ACPI_SLIT_SIG "SLIT" -#define ACPI_SLIT_SIG_LEN 4 -#define ACPI_SLIT_REVISION 1 -#define ACPI_SLIT_LOCAL 10 -#endif /* CONFIG_DISCONTIGMEM */ - -#pragma pack() -#endif /* _ASM_IA64_ACPI_EXT_H */ diff --git a/include/asm-ia64/acpi.h b/include/asm-ia64/acpi.h new file mode 100644 index 0000000000000000000000000000000000000000..d23755a689fee8844f86e416ceb357c28aa1bf80 --- /dev/null +++ b/include/asm-ia64/acpi.h @@ -0,0 +1,49 @@ +/* + * asm-ia64/acpi.h + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> + * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com> + * Copyright (C) 2001,2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.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. + * + * 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef _ASM_ACPI_H +#define _ASM_ACPI_H + +#ifdef __KERNEL__ + +#define __acpi_map_table(phys_addr, size) __va(phys_addr) + +int acpi_boot_init (char *cdline); +int acpi_find_rsdp (unsigned long *phys_addr); +int acpi_request_vector (u32 int_type); +int acpi_get_prt (struct pci_vector_struct **vectors, int *count); +int acpi_get_interrupt_model(int *type); + +#ifdef CONFIG_DISCONTIGMEM +#define NODE_ARRAY_INDEX(x) ((x) / 8) /* 8 bits/char */ +#define NODE_ARRAY_OFFSET(x) ((x) % 8) /* 8 bits/char */ +#define MAX_PXM_DOMAINS (256) +#endif /* CONFIG_DISCONTIGMEM */ + +#endif /*__KERNEL__*/ + +#endif /*_ASM_ACPI_H*/ diff --git a/include/asm-ia64/acpikcfg.h b/include/asm-ia64/acpikcfg.h deleted file mode 100644 index d79cbbe4db0a51fec8dd1ac20ae77307e47b095d..0000000000000000000000000000000000000000 --- a/include/asm-ia64/acpikcfg.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _ASM_IA64_ACPIKCFG_H -#define _ASM_IA64_ACPIKCFG_H - -/* - * acpikcfg.h - ACPI based Kernel Configuration Manager External Interfaces - * - * Copyright (C) 2000 Intel Corp. - * Copyright (C) 2000 J.I. Lee <jung-ik.lee@intel.com> - */ - - -u32 __init acpi_cf_init (void * rsdp); -u32 __init acpi_cf_terminate (void ); - -u32 __init -acpi_cf_get_pci_vectors ( - struct pci_vector_struct **vectors, - int *num_pci_vectors - ); - - -#ifdef CONFIG_ACPI_KERNEL_CONFIG_DEBUG -void __init -acpi_cf_print_pci_vectors ( - struct pci_vector_struct *vectors, - int num_pci_vectors - ); -#endif - -#endif /* _ASM_IA64_ACPIKCFG_H */ diff --git a/include/asm-ia64/cacheflush.h b/include/asm-ia64/cacheflush.h new file mode 100644 index 0000000000000000000000000000000000000000..025398be93bc28edbadfce45a38aad1e018b3887 --- /dev/null +++ b/include/asm-ia64/cacheflush.h @@ -0,0 +1,37 @@ +#ifndef _ASM_IA64_CACHEFLUSH_H +#define _ASM_IA64_CACHEFLUSH_H + +/* + * Copyright (C) 2002 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#include <asm/bitops.h> +#include <asm/page.h> + +/* + * Cache flushing routines. This is the kind of stuff that can be very expensive, so try + * to avoid them whenever possible. + */ + +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) +#define flush_icache_page(vma,page) do { } while (0) + +#define flush_dcache_page(page) \ +do { \ + clear_bit(PG_arch_1, &page->flags); \ +} while (0) + +extern void flush_icache_range (unsigned long start, unsigned long end); + +#define flush_icache_user_range(vma, page, user_addr, len) \ +do { \ + unsigned long _addr = (unsigned long) page_address(page) + ((user_addr) & ~PAGE_MASK); \ + flush_icache_range(_addr, _addr + (len)); \ +} while (0) + +#endif /* _ASM_IA64_CACHEFLUSH_H */ diff --git a/include/asm-ia64/efi.h b/include/asm-ia64/efi.h index 189f7bcfd0350ec30268b3da8c834c8c1dc22427..b679cec86170c7f7fc2e2e12a43f3190684bad30 100644 --- a/include/asm-ia64/efi.h +++ b/include/asm-ia64/efi.h @@ -87,6 +87,8 @@ typedef struct { #define EFI_MEMORY_RUNTIME 0x8000000000000000 /* range requires runtime mapping */ #define EFI_MEMORY_DESCRIPTOR_VERSION 1 +#define EFI_PAGE_SHIFT 12 + typedef struct { u32 type; u32 pad; @@ -257,6 +259,7 @@ extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); extern void efi_gettimeofday (struct timeval *tv); extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ extern u64 efi_get_iobase (void); +extern u32 efi_mem_type (u64 phys_addr); /* * Variable Attributes diff --git a/include/asm-ia64/hw_irq.h b/include/asm-ia64/hw_irq.h index 75c82bc8abf56a8f4a59a191ef9643216be7e20b..823d3e8ce20f49d683b7d8305798afd75fb7c13d 100644 --- a/include/asm-ia64/hw_irq.h +++ b/include/asm-ia64/hw_irq.h @@ -88,6 +88,7 @@ hw_resend_irq (struct hw_interrupt_type *h, unsigned int vector) extern struct irq_desc _irq_desc[NR_IRQS]; +#ifndef CONFIG_IA64_GENERIC static inline struct irq_desc * __ia64_irq_desc (unsigned int irq) { @@ -105,6 +106,7 @@ __ia64_local_vector_to_irq (ia64_vector vec) { return (unsigned int) vec; } +#endif /* * Next follows the irq descriptor interface. On IA-64, each CPU supports 256 interrupt diff --git a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h index 697c2c51bc7f124a822f6429f2a36b414c5e3a12..336132c199f827435bbd385cd067b9b87b7e52a8 100644 --- a/include/asm-ia64/ide.h +++ b/include/asm-ia64/ide.h @@ -31,14 +31,14 @@ static __inline__ int ide_default_irq (ide_ioreg_t base) { switch (base) { - case 0x1f0: return isa_irq_to_vector(14); - case 0x170: return isa_irq_to_vector(15); - case 0x1e8: return isa_irq_to_vector(11); - case 0x168: return isa_irq_to_vector(10); - case 0x1e0: return isa_irq_to_vector(8); - case 0x160: return isa_irq_to_vector(12); - default: - return 0; + case 0x1f0: return isa_irq_to_vector(14); + case 0x170: return isa_irq_to_vector(15); + case 0x1e8: return isa_irq_to_vector(11); + case 0x168: return isa_irq_to_vector(10); + case 0x1e0: return isa_irq_to_vector(8); + case 0x160: return isa_irq_to_vector(12); + default: + return 0; } } @@ -46,14 +46,14 @@ static __inline__ ide_ioreg_t ide_default_io_base (int index) { switch (index) { - case 0: return 0x1f0; - case 1: return 0x170; - case 2: return 0x1e8; - case 3: return 0x168; - case 4: return 0x1e0; - case 5: return 0x160; - default: - return 0; + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + case 4: return 0x1e0; + case 5: return 0x160; + default: + return 0; } } diff --git a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h index 26f3ba521bcdbc57bc3cee30f7a1f8d70bf604f1..11d7f1a718f1b8e3a5d5e21fe12f9d9aba1f54a8 100644 --- a/include/asm-ia64/machvec.h +++ b/include/asm-ia64/machvec.h @@ -68,6 +68,8 @@ extern void machvec_noop (void); # include <asm/machvec_hpsim.h> # elif defined (CONFIG_IA64_DIG) # include <asm/machvec_dig.h> +# elif defined (CONFIG_IA64_HP_ZX1) +# include <asm/machvec_hpzx1.h> # elif defined (CONFIG_IA64_SGI_SN1) # include <asm/machvec_sn1.h> # elif defined (CONFIG_IA64_SGI_SN2) @@ -123,6 +125,7 @@ struct ia64_machine_vector { ia64_mv_cmci_handler_t *cmci_handler; ia64_mv_log_print_t *log_print; ia64_mv_send_ipi_t *send_ipi; + ia64_mv_global_tlb_purge_t *global_tlb_purge; ia64_mv_pci_dma_init *dma_init; ia64_mv_pci_alloc_consistent *alloc_consistent; ia64_mv_pci_free_consistent *free_consistent; @@ -149,6 +152,7 @@ struct ia64_machine_vector { { \ #name, \ platform_setup, \ + platform_cpu_init, \ platform_irq_init, \ platform_pci_fixup, \ platform_map_nr, \ diff --git a/include/asm-ia64/machvec_hpzx1.h b/include/asm-ia64/machvec_hpzx1.h new file mode 100644 index 0000000000000000000000000000000000000000..a5edbaf5698a28525cc3cd9acbe8199e93e8716f --- /dev/null +++ b/include/asm-ia64/machvec_hpzx1.h @@ -0,0 +1,74 @@ +#ifndef _ASM_IA64_MACHVEC_HPZX1_h +#define _ASM_IA64_MACHVEC_HPZX1_h + +extern ia64_mv_setup_t dig_setup; +extern ia64_mv_pci_fixup_t hpzx1_pci_fixup; +extern ia64_mv_map_nr_t map_nr_dense; +extern ia64_mv_pci_alloc_consistent sba_alloc_consistent; +extern ia64_mv_pci_free_consistent sba_free_consistent; +extern ia64_mv_pci_map_single sba_map_single; +extern ia64_mv_pci_unmap_single sba_unmap_single; +extern ia64_mv_pci_map_sg sba_map_sg; +extern ia64_mv_pci_unmap_sg sba_unmap_sg; +extern ia64_mv_pci_dma_address sba_dma_address; + +/* + * This stuff has dual use! + * + * For a generic kernel, the macros are used to initialize the + * platform's machvec structure. When compiling a non-generic kernel, + * the macros are used directly. + */ +#define platform_name "hpzx1" +#define platform_setup dig_setup +#define platform_pci_fixup hpzx1_pci_fixup +#define platform_map_nr map_nr_dense +#define platform_pci_dma_init ((ia64_mv_pci_dma_init *) machvec_noop) +#define platform_pci_alloc_consistent sba_alloc_consistent +#define platform_pci_free_consistent sba_free_consistent +#define platform_pci_map_single sba_map_single +#define platform_pci_unmap_single sba_unmap_single +#define platform_pci_map_sg sba_map_sg +#define platform_pci_unmap_sg sba_unmap_sg +#define platform_pci_dma_sync_single ((ia64_mv_pci_dma_sync_single *) machvec_noop) +#define platform_pci_dma_sync_sg ((ia64_mv_pci_dma_sync_sg *) machvec_noop) +#define platform_pci_dma_address sba_dma_address + +#endif /* _ASM_IA64_MACHVEC_HPZX1_h */ +#ifndef _ASM_IA64_MACHVEC_HPZX1_h +#define _ASM_IA64_MACHVEC_HPZX1_h + +extern ia64_mv_setup_t dig_setup; +extern ia64_mv_pci_fixup_t hpzx1_pci_fixup; +extern ia64_mv_map_nr_t map_nr_dense; +extern ia64_mv_pci_alloc_consistent sba_alloc_consistent; +extern ia64_mv_pci_free_consistent sba_free_consistent; +extern ia64_mv_pci_map_single sba_map_single; +extern ia64_mv_pci_unmap_single sba_unmap_single; +extern ia64_mv_pci_map_sg sba_map_sg; +extern ia64_mv_pci_unmap_sg sba_unmap_sg; +extern ia64_mv_pci_dma_address sba_dma_address; + +/* + * This stuff has dual use! + * + * For a generic kernel, the macros are used to initialize the + * platform's machvec structure. When compiling a non-generic kernel, + * the macros are used directly. + */ +#define platform_name "hpzx1" +#define platform_setup dig_setup +#define platform_pci_fixup hpzx1_pci_fixup +#define platform_map_nr map_nr_dense +#define platform_pci_dma_init ((ia64_mv_pci_dma_init *) machvec_noop) +#define platform_pci_alloc_consistent sba_alloc_consistent +#define platform_pci_free_consistent sba_free_consistent +#define platform_pci_map_single sba_map_single +#define platform_pci_unmap_single sba_unmap_single +#define platform_pci_map_sg sba_map_sg +#define platform_pci_unmap_sg sba_unmap_sg +#define platform_pci_dma_sync_single ((ia64_mv_pci_dma_sync_single *) machvec_noop) +#define platform_pci_dma_sync_sg ((ia64_mv_pci_dma_sync_sg *) machvec_noop) +#define platform_pci_dma_address sba_dma_address + +#endif /* _ASM_IA64_MACHVEC_HPZX1_h */ diff --git a/include/asm-ia64/machvec_init.h b/include/asm-ia64/machvec_init.h index 8256910cc579037edfda38ae2870110584384843..71912c19c2d4b8866df3ca3038359fdfd6354375 100644 --- a/include/asm-ia64/machvec_init.h +++ b/include/asm-ia64/machvec_init.h @@ -5,6 +5,11 @@ #include <asm/machvec.h> extern ia64_mv_send_ipi_t ia64_send_ipi; +extern ia64_mv_global_tlb_purge_t ia64_global_tlb_purge; +extern ia64_mv_irq_desc __ia64_irq_desc; +extern ia64_mv_irq_to_vector __ia64_irq_to_vector; +extern ia64_mv_local_vector_to_irq __ia64_local_vector_to_irq; + extern ia64_mv_inb_t __ia64_inb; extern ia64_mv_inw_t __ia64_inw; extern ia64_mv_inl_t __ia64_inl; diff --git a/include/asm-ia64/module.h b/include/asm-ia64/module.h index 931469ebe4a857ba8501d856238e2b65afd8a89d..0c64400aceb7ba6795aec24264d67e9f1616fa45 100644 --- a/include/asm-ia64/module.h +++ b/include/asm-ia64/module.h @@ -51,6 +51,9 @@ ia64_module_init (struct module *mod) return 0; archdata = (struct archdata *)(mod->archdata_start); + if (archdata->unw_start == 0) + return 0; + /* * Make sure the unwind pointers are sane. */ diff --git a/include/asm-ia64/offsets.h b/include/asm-ia64/offsets.h index 835d2a5b70f4e95e9212eb4e86766e2099f324fe..eaa66e4ef3d441041aa2f54c2c25deeb4def3209 100644 --- a/include/asm-ia64/offsets.h +++ b/include/asm-ia64/offsets.h @@ -6,8 +6,8 @@ * This file was generated by arch/ia64/tools/print_offsets.awk. * */ -#define IA64_TASK_SIZE 3936 /* 0xf60 */ -#define IA64_THREAD_INFO_SIZE 24 /* 0x18 */ +#define IA64_TASK_SIZE 3952 /* 0xf70 */ +#define IA64_THREAD_INFO_SIZE 32 /* 0x20 */ #define IA64_PT_REGS_SIZE 400 /* 0x190 */ #define IA64_SWITCH_STACK_SIZE 560 /* 0x230 */ #define IA64_SIGINFO_SIZE 128 /* 0x80 */ @@ -15,7 +15,7 @@ #define SIGFRAME_SIZE 2816 /* 0xb00 */ #define UNW_FRAME_INFO_SIZE 448 /* 0x1c0 */ -#define IA64_TASK_THREAD_KSP_OFFSET 1480 /* 0x5c8 */ +#define IA64_TASK_THREAD_KSP_OFFSET 1496 /* 0x5d8 */ #define IA64_PT_REGS_CR_IPSR_OFFSET 0 /* 0x0 */ #define IA64_PT_REGS_CR_IIP_OFFSET 8 /* 0x8 */ #define IA64_PT_REGS_CR_IFS_OFFSET 16 /* 0x10 */ diff --git a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h index a637cff4ae2d844d3f922ebd0a139b49467176a2..9fe86f1beca6414a0e6742bd209705a6ca8e7951 100644 --- a/include/asm-ia64/pci.h +++ b/include/asm-ia64/pci.h @@ -20,6 +20,11 @@ #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 +void pcibios_config_init(void); +struct pci_bus * pcibios_scan_root(int seg, int bus); +extern int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value); +extern int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value); + struct pci_dev; /* diff --git a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h new file mode 100644 index 0000000000000000000000000000000000000000..7f2871a8cc61908b4046c2e4c0b18e4b9c4d7597 --- /dev/null +++ b/include/asm-ia64/percpu.h @@ -0,0 +1,24 @@ +#ifndef _ASM_IA64_PERCPU_H +#define _ASM_IA64_PERCPU_H + +/* + * Copyright (C) 2002 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#ifdef __ASSEMBLY__ + +#define THIS_CPU(var) (var) /* use this to mark accesses to per-CPU variables... */ + +#else /* !__ASSEMBLY__ */ + +#include <linux/threads.h> + +extern unsigned long __per_cpu_offset[NR_CPUS]; + +#define per_cpu(var, cpu) (*(__typeof__(&(var))) ((void *) &(var) + __per_cpu_offset[cpu])) +#define this_cpu(var) (var) + +#endif /* !__ASSEMBLY__ */ + +#endif /* _ASM_IA64_PERCPU_H */ diff --git a/include/asm-ia64/pgalloc.h b/include/asm-ia64/pgalloc.h index 4e9ebe83bbc24b8f3cd32fdee8b035d111464bcf..9d0740f2e4558398b65c3a50d29fc8375e8fb920 100644 --- a/include/asm-ia64/pgalloc.h +++ b/include/asm-ia64/pgalloc.h @@ -150,7 +150,7 @@ pte_free_kernel (pte_t *pte) free_page((unsigned long) pte); } -extern int do_check_pgt_cache (int, int); +extern void check_pgt_cache (void); /* * IA-64 doesn't have any external MMU info: the page tables contain all the necessary @@ -177,69 +177,4 @@ update_mmu_cache (struct vm_area_struct *vma, unsigned long vaddr, pte_t pte) set_bit(PG_arch_1, &page->flags); /* mark page as clean */ } -/* - * Now for some TLB flushing routines. This is the kind of stuff that - * can be very expensive, so try to avoid them whenever possible. - */ - -/* - * Flush everything (kernel mapping may also have changed due to - * vmalloc/vfree). - */ -extern void __flush_tlb_all (void); - -#ifdef CONFIG_SMP - extern void smp_flush_tlb_all (void); -# define flush_tlb_all() smp_flush_tlb_all() -#else -# define flush_tlb_all() __flush_tlb_all() -#endif - -/* - * Flush a specified user mapping - */ -static inline void -flush_tlb_mm (struct mm_struct *mm) -{ - if (mm) { - mm->context = 0; - if (mm == current->active_mm) { - /* This is called, e.g., as a result of exec(). */ - get_new_mmu_context(mm); - reload_context(mm); - } - } -} - -extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end); - -/* - * Page-granular tlb flush. - */ -static inline void -flush_tlb_page (struct vm_area_struct *vma, unsigned long addr) -{ -#ifdef CONFIG_SMP - flush_tlb_range(vma, (addr & PAGE_MASK), (addr & PAGE_MASK) + PAGE_SIZE); -#else - if (vma->vm_mm == current->active_mm) - asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(PAGE_SHIFT << 2) : "memory"); -#endif -} - -/* - * Flush the TLB entries mapping the virtually mapped linear page - * table corresponding to address range [START-END). - */ -static inline void -flush_tlb_pgtables (struct mm_struct *mm, unsigned long start, unsigned long end) -{ - struct vm_area_struct vma; - - if (rgn_index(start) != rgn_index(end)) - printk("flush_tlb_pgtables: can't flush across regions!!\n"); - vma.vm_mm = mm; - flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end)); -} - #endif /* _ASM_IA64_PGALLOC_H */ diff --git a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h index e086475d50ecedfdd182aff7bcfbadb73f3d5106..f4f92435627e37ccad348fcf4c89d1c47a707e97 100644 --- a/include/asm-ia64/pgtable.h +++ b/include/asm-ia64/pgtable.h @@ -121,6 +121,7 @@ # ifndef __ASSEMBLY__ #include <asm/bitops.h> +#include <asm/cacheflush.h> #include <asm/mmu_context.h> #include <asm/processor.h> @@ -290,30 +291,6 @@ ia64_phys_addr_valid (unsigned long addr) # define pgprot_writecombine(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WC) #endif -/* - * Return the region index for virtual address ADDRESS. - */ -static inline unsigned long -rgn_index (unsigned long address) -{ - ia64_va a; - - a.l = address; - return a.f.reg; -} - -/* - * Return the region offset for virtual address ADDRESS. - */ -static inline unsigned long -rgn_offset (unsigned long address) -{ - ia64_va a; - - a.l = address; - return a.f.off; -} - static inline unsigned long pgd_index (unsigned long address) { @@ -440,33 +417,6 @@ extern void paging_init (void); #define io_remap_page_range remap_page_range /* XXX is this right? */ - -/* - * Now for some cache flushing routines. This is the kind of stuff that can be very - * expensive, so try to avoid them whenever possible. - */ - -/* Caches aren't brain-dead on the IA-64. */ -#define flush_cache_all() do { } while (0) -#define flush_cache_mm(mm) do { } while (0) -#define flush_cache_range(vma, start, end) do { } while (0) -#define flush_cache_page(vma, vmaddr) do { } while (0) -#define flush_page_to_ram(page) do { } while (0) -#define flush_icache_page(vma,page) do { } while (0) - -#define flush_dcache_page(page) \ -do { \ - clear_bit(PG_arch_1, &page->flags); \ -} while (0) - -extern void flush_icache_range (unsigned long start, unsigned long end); - -#define flush_icache_user_range(vma, page, user_addr, len) \ -do { \ - unsigned long _addr = page_address(page) + ((user_addr) & ~PAGE_MASK); \ - flush_icache_range(_addr, _addr + (len)); \ -} while (0) - /* * ZERO_PAGE is a global shared page that is always zero: used * for zero-mapped memory areas etc.. diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 8713f2544632539cfc00378767da15bd6298d3cb..8abb0d56e0dabda4e7775c3e36333f70196de33e 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -15,7 +15,7 @@ #include <linux/config.h> -#include <linux/compiler.h> +#include <linux/percpu.h> #include <asm/ptrace.h> #include <asm/kregs.h> @@ -186,10 +186,6 @@ */ #define IA64_USEC_PER_CYC_SHIFT 41 -#define __HAVE_ARCH_PER_CPU - -#define THIS_CPU(var) (var) - #ifndef __ASSEMBLY__ #include <linux/threads.h> @@ -202,11 +198,6 @@ #include <asm/unwind.h> #include <asm/atomic.h> -extern unsigned long __per_cpu_offset[NR_CPUS]; - -#define per_cpu(var, cpu) (*(__typeof__(&(var))) ((void *) &(var) + __per_cpu_offset[cpu])) -#define this_cpu(var) (var) - /* like above but expressed as bitfields for more efficient access: */ struct ia64_psr { __u64 reserved0 : 1; diff --git a/include/asm-ia64/string.h b/include/asm-ia64/string.h index 5c89e5c5584d31865d04f5ac581838e961c58644..1a44134cd96d9e6289115c069d890bc7cea3966a 100644 --- a/include/asm-ia64/string.h +++ b/include/asm-ia64/string.h @@ -5,8 +5,8 @@ * Here is where we want to put optimized versions of the string * routines. * - * Copyright (C) 1998-2000 Hewlett-Packard Co - * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1998-2000, 2002 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> */ #include <linux/config.h> /* remove this once we remove the A-step workaround... */ @@ -17,7 +17,21 @@ #define __HAVE_ARCH_BCOPY 1 /* see arch/ia64/lib/memcpy.S */ extern __kernel_size_t strlen (const char *); -extern void *memset (void *, int, __kernel_size_t); extern void *memcpy (void *, const void *, __kernel_size_t); +extern void *__memset_generic (void *, int, __kernel_size_t); +extern void __bzero (void *, __kernel_size_t); + +#define memset(s, c, count) \ +({ \ + void *_s = (s); \ + int _c = (c); \ + __kernel_size_t _count = (count); \ + \ + if (__builtin_constant_p(_c) && _c == 0) \ + __bzero(_s, _count); \ + else \ + __memset_generic(_s, _c, _count); \ +}) + #endif /* _ASM_IA64_STRING_H */ diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h index 8ec8655902791326b90c4832d1a9be038d765d1a..df0f2ff62ab54ef5266dd3f633999b0ade518d2e 100644 --- a/include/asm-ia64/system.h +++ b/include/asm-ia64/system.h @@ -18,14 +18,6 @@ #define KERNEL_START (PAGE_OFFSET + 68*1024*1024) -/* - * The following #defines must match with vmlinux.lds.S: - */ -#define IVT_ADDR (KERNEL_START) -#define IVT_END_ADDR (KERNEL_START + 0x8000) -#define ZERO_PAGE_ADDR PAGE_ALIGN(IVT_END_ADDR) -#define SWAPPER_PGD_ADDR (ZERO_PAGE_ADDR + 1*PAGE_SIZE) - #define GATE_ADDR (0xa000000000000000 + PAGE_SIZE) #define PERCPU_ADDR (0xa000000000000000 + 2*PAGE_SIZE) diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h index 6c6b9e143f204092e0a6eb87047fb1ab090dffb3..70962f85c2732e8dce64bb9f9663fcf12ef95d1c 100644 --- a/include/asm-ia64/thread_info.h +++ b/include/asm-ia64/thread_info.h @@ -12,7 +12,10 @@ #define TI_EXEC_DOMAIN 0x00 #define TI_FLAGS 0x08 #define TI_CPU 0x0c -#define TI_ADDR_LIMI 0x10 +#define TI_ADDR_LIMIT 0x10 +#define TI_PRE_COUNT 0x18 + +#define PREEMPT_ACTIVE 0x4000000 #ifndef __ASSEMBLY__ @@ -26,6 +29,7 @@ struct thread_info { __u32 flags; /* thread_info flags (see TIF_*) */ __u32 cpu; /* current CPU */ mm_segment_t addr_limit; /* user-level address space limit */ + __s32 preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ }; #define INIT_THREAD_SIZE /* tell sched.h not to declare the thread_union */ @@ -37,6 +41,7 @@ struct thread_info { flags: 0, \ cpu: 0, \ addr_limit: KERNEL_DS, \ + preempt_count: 0, \ } /* how to get the thread information struct from C */ diff --git a/include/asm-ia64/tlbflush.h b/include/asm-ia64/tlbflush.h new file mode 100644 index 0000000000000000000000000000000000000000..4e2404ef4e36adfec617c3a8ab24e55839d8b28e --- /dev/null +++ b/include/asm-ia64/tlbflush.h @@ -0,0 +1,83 @@ +#ifndef _ASM_IA64_TLBFLUSH_H +#define _ASM_IA64_TLBFLUSH_H + +/* + * Copyright (C) 2002 Hewlett-Packard Co + * David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#include <linux/config.h> + +#include <linux/mm.h> + +#include <asm/mmu_context.h> +#include <asm/page.h> + +/* + * Now for some TLB flushing routines. This is the kind of stuff that + * can be very expensive, so try to avoid them whenever possible. + */ + +/* + * Flush everything (kernel mapping may also have changed due to + * vmalloc/vfree). + */ +extern void __flush_tlb_all (void); + +#ifdef CONFIG_SMP + extern void smp_flush_tlb_all (void); +# define flush_tlb_all() smp_flush_tlb_all() +#else +# define flush_tlb_all() __flush_tlb_all() +#endif + +/* + * Flush a specified user mapping + */ +static inline void +flush_tlb_mm (struct mm_struct *mm) +{ + if (mm) { + mm->context = 0; + if (mm == current->active_mm) { + /* This is called, e.g., as a result of exec(). */ + get_new_mmu_context(mm); + reload_context(mm); + } + } +} + +extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end); + +/* + * Page-granular tlb flush. + */ +static inline void +flush_tlb_page (struct vm_area_struct *vma, unsigned long addr) +{ +#ifdef CONFIG_SMP + flush_tlb_range(vma, (addr & PAGE_MASK), (addr & PAGE_MASK) + PAGE_SIZE); +#else + if (vma->vm_mm == current->active_mm) + asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(PAGE_SHIFT << 2) : "memory"); +#endif +} + +/* + * Flush the TLB entries mapping the virtually mapped linear page + * table corresponding to address range [START-END). + */ +static inline void +flush_tlb_pgtables (struct mm_struct *mm, unsigned long start, unsigned long end) +{ + struct vm_area_struct vma; + + if (REGION_NUMBER(start) != REGION_NUMBER(end)) + printk("flush_tlb_pgtables: can't flush across regions!!\n"); + vma.vm_mm = mm; + flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end)); +} + +#define flush_tlb_kernel_range(start, end) flush_tlb_all() /* XXX fix me */ + +#endif /* _ASM_IA64_TLBFLUSH_H */ diff --git a/include/asm-ia64/uaccess.h b/include/asm-ia64/uaccess.h index 965aecc95e66d09a53efb2393186fa41f8547b16..afe47af459bc45ef9e12cafc220afaf21e05c39d 100644 --- a/include/asm-ia64/uaccess.h +++ b/include/asm-ia64/uaccess.h @@ -56,8 +56,9 @@ * address TASK_SIZE is never valid. We also need to make sure that the address doesn't * point inside the virtually mapped linear page table. */ -#define __access_ok(addr,size,segment) (((unsigned long) (addr)) <= (segment).seg \ - && ((segment).seg == KERNEL_DS.seg || rgn_offset((unsigned long) (addr)) < RGN_MAP_LIMIT)) +#define __access_ok(addr,size,segment) (((unsigned long) (addr)) <= (segment).seg \ + && ((segment).seg == KERNEL_DS.seg \ + || REGION_OFFSET((unsigned long) (addr)) < RGN_MAP_LIMIT)) #define access_ok(type,addr,size) __access_ok((addr),(size),get_fs()) static inline int diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 261387c4998787fe91232610bb27f12a1d9f24c3..48b175af1b08ab9241afcf3f2c66152cbc948c64 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -219,6 +219,9 @@ #define __NR_lremovexattr 1227 #define __NR_fremovexattr 1228 #define __NR_tkill 1229 +#define __NR_futex 1230 +#define __NR_sched_setaffinity 1231 +#define __NR_sched_getaffinity 1232 #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) diff --git a/include/asm-ppc/bitops.h b/include/asm-ppc/bitops.h index ea313f2c8e6d1374c7651f9619f138500b5bd467..8eb167bf8e752c37bc74a7726be5383ab4752ee3 100644 --- a/include/asm-ppc/bitops.h +++ b/include/asm-ppc/bitops.h @@ -30,7 +30,7 @@ * These used to be if'd out here because using : "cc" as a constraint * resulted in errors from egcs. Things appear to be OK with gcc-2.95. */ -static __inline__ void set_bit(int nr, volatile void * addr) +static __inline__ void set_bit(int nr, volatile unsigned long * addr) { unsigned long old; unsigned long mask = 1 << (nr & 0x1f); @@ -50,7 +50,7 @@ static __inline__ void set_bit(int nr, volatile void * addr) /* * non-atomic version */ -static __inline__ void __set_bit(int nr, volatile void *addr) +static __inline__ void __set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -64,7 +64,7 @@ static __inline__ void __set_bit(int nr, volatile void *addr) #define smp_mb__before_clear_bit() smp_mb() #define smp_mb__after_clear_bit() smp_mb() -static __inline__ void clear_bit(int nr, volatile void *addr) +static __inline__ void clear_bit(int nr, volatile unsigned long *addr) { unsigned long old; unsigned long mask = 1 << (nr & 0x1f); @@ -84,7 +84,7 @@ static __inline__ void clear_bit(int nr, volatile void *addr) /* * non-atomic version */ -static __inline__ void __clear_bit(int nr, volatile void *addr) +static __inline__ void __clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -92,7 +92,7 @@ static __inline__ void __clear_bit(int nr, volatile void *addr) *p &= ~mask; } -static __inline__ void change_bit(int nr, volatile void *addr) +static __inline__ void change_bit(int nr, volatile unsigned long *addr) { unsigned long old; unsigned long mask = 1 << (nr & 0x1f); @@ -112,7 +112,7 @@ static __inline__ void change_bit(int nr, volatile void *addr) /* * non-atomic version */ -static __inline__ void __change_bit(int nr, volatile void *addr) +static __inline__ void __change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -123,7 +123,7 @@ static __inline__ void __change_bit(int nr, volatile void *addr) /* * test_and_*_bit do imply a memory barrier (?) */ -static __inline__ int test_and_set_bit(int nr, volatile void *addr) +static __inline__ int test_and_set_bit(int nr, volatile unsigned long *addr) { unsigned int old, t; unsigned int mask = 1 << (nr & 0x1f); @@ -146,7 +146,7 @@ static __inline__ int test_and_set_bit(int nr, volatile void *addr) /* * non-atomic version */ -static __inline__ int __test_and_set_bit(int nr, volatile void *addr) +static __inline__ int __test_and_set_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -156,7 +156,7 @@ static __inline__ int __test_and_set_bit(int nr, volatile void *addr) return (old & mask) != 0; } -static __inline__ int test_and_clear_bit(int nr, volatile void *addr) +static __inline__ int test_and_clear_bit(int nr, volatile unsigned long *addr) { unsigned int old, t; unsigned int mask = 1 << (nr & 0x1f); @@ -179,7 +179,7 @@ static __inline__ int test_and_clear_bit(int nr, volatile void *addr) /* * non-atomic version */ -static __inline__ int __test_and_clear_bit(int nr, volatile void *addr) +static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -189,7 +189,7 @@ static __inline__ int __test_and_clear_bit(int nr, volatile void *addr) return (old & mask) != 0; } -static __inline__ int test_and_change_bit(int nr, volatile void *addr) +static __inline__ int test_and_change_bit(int nr, volatile unsigned long *addr) { unsigned int old, t; unsigned int mask = 1 << (nr & 0x1f); @@ -212,7 +212,7 @@ static __inline__ int test_and_change_bit(int nr, volatile void *addr) /* * non-atomic version */ -static __inline__ int __test_and_change_bit(int nr, volatile void *addr) +static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr) { unsigned long mask = 1 << (nr & 0x1f); unsigned long *p = ((unsigned long *)addr) + (nr >> 5); @@ -222,7 +222,7 @@ static __inline__ int __test_and_change_bit(int nr, volatile void *addr) return (old & mask) != 0; } -static __inline__ int test_bit(int nr, __const__ volatile void *addr) +static __inline__ int test_bit(int nr, __const__ volatile unsigned long *addr) { __const__ unsigned int *p = (__const__ unsigned int *) addr; @@ -230,7 +230,7 @@ static __inline__ int test_bit(int nr, __const__ volatile void *addr) } /* Return the bit position of the most significant 1 bit in a word */ -static __inline__ int __ilog2(unsigned int x) +static __inline__ int __ilog2(unsigned long x) { int lz; @@ -238,7 +238,7 @@ static __inline__ int __ilog2(unsigned int x) return 31 - lz; } -static __inline__ int ffz(unsigned int x) +static __inline__ int ffz(unsigned long x) { if ((x = ~x) == 0) return 32; @@ -296,7 +296,7 @@ static inline int sched_find_first_bit(unsigned long *b) * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -static __inline__ unsigned long find_next_bit(void *addr, +static __inline__ unsigned long find_next_bit(unsigned long *addr, unsigned long size, unsigned long offset) { unsigned int *p = ((unsigned int *) addr) + (offset >> 5); @@ -353,7 +353,7 @@ static __inline__ unsigned long find_next_bit(void *addr, #define find_first_zero_bit(addr, size) \ find_next_zero_bit((addr), (size), 0) -static __inline__ unsigned long find_next_zero_bit(void * addr, +static __inline__ unsigned long find_next_zero_bit(unsigned long * addr, unsigned long size, unsigned long offset) { unsigned int * p = ((unsigned int *) addr) + (offset >> 5); diff --git a/include/linux/acpi_serial.h b/include/linux/acpi_serial.h index c48e64ae12a9bbeb04d619589b7850fff5691532..07fb16f5a2b3ce5250bf4163c9ddc00ada51b5c0 100644 --- a/include/linux/acpi_serial.h +++ b/include/linux/acpi_serial.h @@ -11,6 +11,8 @@ extern void setup_serial_acpi(void *); +#define ACPI_SIG_LEN 4 + /* ACPI table signatures */ #define ACPI_SPCRT_SIGNATURE "SPCR" #define ACPI_DBGPT_SIGNATURE "DBGP" diff --git a/include/linux/bfs_fs.h b/include/linux/bfs_fs.h index 1360909fc571ed60001e748ee8e51a46af3a30b1..0c6349eec459b759326e9e8f9d9e2d353c85f569 100644 --- a/include/linux/bfs_fs.h +++ b/include/linux/bfs_fs.h @@ -7,6 +7,7 @@ #define _LINUX_BFS_FS_H #include <linux/bfs_fs_i.h> +#include <linux/bfs_fs_sb.h> #define BFS_BSIZE_BITS 9 #define BFS_BSIZE (1<<BFS_BSIZE_BITS) @@ -89,6 +90,11 @@ extern struct address_space_operations bfs_aops; extern struct inode_operations bfs_dir_inops; extern struct file_operations bfs_dir_operations; +static inline struct bfs_sb_info *BFS_SB(struct super_block *sb) +{ + return sb->u.generic_sbp; +} + static inline struct bfs_inode_info *BFS_I(struct inode *inode) { return list_entry(inode, struct bfs_inode_info, vfs_inode); diff --git a/include/linux/capability.h b/include/linux/capability.h index 1cccb9db2334b44bc7c90ed9f8e0f103e22139fc..39c4d290c83620c855437f052d74e572956aef35 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -242,6 +242,7 @@ typedef __u32 kernel_cap_t; /* Allow use of FIFO and round-robin (realtime) scheduling on own processes and setting the scheduling algorithm used by another process. */ +/* Allow setting cpu affinity on other processes */ #define CAP_SYS_NICE 23 diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 21da5723105049b3f1c5e4e8d1d55acc89dfe298..6cf86c3e301c134528674dacc6a4d73f626550a6 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -117,12 +117,18 @@ d_iput: no no yes * renamed" and has to be * deleted on the last dput() */ -#define DCACHE_NFSD_DISCONNECTED 0x0004 /* This dentry is not currently connected to the - * dcache tree. Its parent will either be itself, - * or will have this flag as well. - * If this dentry points to a directory, then - * s_nfsd_free_path semaphore will be down - */ +#define DCACHE_DISCONNECTED 0x0004 + /* This dentry is possibly not currently connected to the dcache tree, + * in which case its parent will either be itself, or will have this + * flag as well. nfsd will not use a dentry with this bit set, but will + * first endeavour to clear the bit either by discovering that it is + * connected, or by performing lookup operations. Any filesystem which + * supports nfsd_operations MUST have a lookup function which, if it finds + * a directory inode with a DCACHE_DISCONNECTED dentry, will d_move + * that dentry into place and return that dentry rather than the passed one, + * typically using d_splice_alias. + */ + #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ extern spinlock_t dcache_lock; @@ -166,8 +172,11 @@ extern void d_delete(struct dentry *); /* allocate/de-allocate */ extern struct dentry * d_alloc(struct dentry *, const struct qstr *); +extern struct dentry * d_alloc_anon(struct inode *); +extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); +extern void shrink_dcache_anon(struct list_head *); extern int d_invalidate(struct dentry *); #define shrink_dcache() prune_dcache(0) diff --git a/include/linux/fb.h b/include/linux/fb.h index 2fffbbfae7df6aea658c2f468343d2a34bf1afe7..8a424179cd2fee73ba41be9f71666730ae3b3c39 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -253,19 +253,52 @@ struct fb_vblank { __u32 reserved[4]; /* reserved for future compatibility */ }; +/* Internal HW accel */ +#define ROP_COPY 0 +#define ROP_XOR 1 + +struct fb_copyarea { + __u32 sx; /* screen-relative */ + __u32 sy; + __u32 width; + __u32 height; + __u32 dx; + __u32 dy; +}; + +struct fb_fillrect { + __u32 dx; /* screen-relative */ + __u32 dy; + __u32 width; + __u32 height; + __u32 color; + __u32 rop; +}; + +struct fb_image { + __u32 width; /* Size of image */ + __u32 height; + __u16 dx; /* Where to place image */ + __u16 dy; + __u32 fg_color; /* Only used when a mono bitmap */ + __u32 bg_color; + __u8 depth; /* Dpeth of the image */ + char *data; /* Pointer to image data */ +}; + #ifdef __KERNEL__ #if 1 /* to go away in 2.5.0 */ extern int GET_FB_IDX(kdev_t rdev); #else -#define GET_FB_IDX(node) (MINOR(node)) +#define GET_FB_IDX(node) (minor(node)) #endif #include <linux/fs.h> +#include <linux/poll.h> #include <linux/init.h> #include <linux/devfs_fs_kernel.h> - struct fb_info; struct fb_info_gen; struct vm_area_struct; @@ -295,9 +328,25 @@ struct fb_ops { /* set colormap */ int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); - /* pan display (optional) */ - int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, - struct fb_info *info); + /* checks var and creates a par based on it */ + int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); + /* set the video mode according to par */ + int (*fb_set_par)(struct fb_info *info); + /* set color register */ + int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info); + /* blank display */ + int (*fb_blank)(int blank, struct fb_info *info); + /* pan display */ + int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, struct fb_info *info); + /* draws a rectangle */ + void (*fb_fillrect)(struct fb_info *info, struct fb_fillrect *rect); + /* Copy data from area to another */ + void (*fb_copyarea)(struct fb_info *info, struct fb_copyarea *region); + /* Draws a image to the display */ + void (*fb_imageblit)(struct fb_info *info, struct fb_image *image); + /* perform polling on fb device */ + int (*fb_poll)(struct fb_info *info, poll_table *wait); /* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int con, struct fb_info *info); @@ -321,6 +370,7 @@ struct fb_info { char *screen_base; /* Virtual address */ struct display *disp; /* initial display variable */ struct vc_data *display_fg; /* Console visible on this display */ + int currcon; /* Current VC. */ char fontname[40]; /* default font name */ devfs_handle_t devfs_handle; /* Devfs handle for new name */ devfs_handle_t devfs_lhandle; /* Devfs handle for compat. symlink */ @@ -389,16 +439,29 @@ struct fb_info_gen { extern int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); +extern int gen_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); extern int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info); +extern int gen_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); extern int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info); +extern int gen_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); extern int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); +extern int gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); extern int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); +extern int gen_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); extern int fbgen_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info); +extern void cfb_fillrect(struct fb_info *info, struct fb_fillrect *rect); +extern void cfb_copyarea(struct fb_info *info, struct fb_copyarea *region); +extern void cfb_imageblit(struct fb_info *info, struct fb_image *image); /* * Helper functions @@ -409,9 +472,12 @@ extern int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive, extern void fbgen_set_disp(int con, struct fb_info_gen *info); extern void fbgen_install_cmap(int con, struct fb_info_gen *info); extern int fbgen_update_var(int con, struct fb_info *info); +extern int gen_update_var(int con, struct fb_info *info); extern int fbgen_switch(int con, struct fb_info *info); extern void fbgen_blank(int blank, struct fb_info *info); +extern int gen_switch(int con, struct fb_info *info); +extern void gen_set_disp(int con, struct fb_info *info); /* drivers/video/fbmem.c */ extern int register_framebuffer(struct fb_info *fb_info); diff --git a/include/linux/fs.h b/include/linux/fs.h index ce6bd67e4cc9a2ca11bb3a75c086822ea3fe65e9..72a40b3bbda4c3c10654a2475f99020103b254d4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -22,9 +22,9 @@ #include <linux/stddef.h> #include <linux/string.h> #include <linux/radix-tree.h> +#include <linux/bitops.h> #include <asm/atomic.h> -#include <asm/bitops.h> struct poll_table_struct; @@ -684,7 +684,6 @@ struct quota_mount_options #include <linux/ufs_fs_sb.h> #include <linux/romfs_fs_sb.h> #include <linux/adfs_fs_sb.h> -#include <linux/bfs_fs_sb.h> extern struct list_head super_blocks; extern spinlock_t sb_lock; @@ -702,6 +701,7 @@ struct super_block { struct file_system_type *s_type; struct super_operations *s_op; struct dquot_operations *dq_op; + struct export_operations *s_export_op; unsigned long s_flags; unsigned long s_magic; struct dentry *s_root; @@ -712,6 +712,7 @@ struct super_block { struct list_head s_dirty; /* dirty inodes */ struct list_head s_locked_inodes;/* inodes being synced */ + struct list_head s_anon; /* anonymous dentries for (nfs) exporting */ struct list_head s_files; struct block_device *s_bdev; @@ -728,7 +729,6 @@ struct super_block { struct ufs_sb_info ufs_sb; struct romfs_sb_info romfs_sb; struct adfs_sb_info adfs_sb; - struct bfs_sb_info bfs_sb; void *generic_sbp; } u; /* @@ -881,7 +881,7 @@ struct super_operations { * and must return a dentry for the referenced object or, if "parent" is * set, a dentry for the parent of the object. * If a dentry cannot be found, a "root" dentry should be created and - * flaged as DCACHE_NFSD_DISCONNECTED. nfsd_iget is an example implementation. + * flaged as DCACHE_DISCONNECTED. nfsd_iget is an example implementation. * * dentry_to_fh is given a dentry and must generate the filesys specific * part of the file handle. Available length is passed in *lenp and used @@ -937,6 +937,110 @@ struct dquot_operations { int (*transfer) (struct inode *, struct iattr *); }; + +/** + * &export_operations - for nfsd to communicate with file systems + * decode_fh: decode a file handle fragment and return a &struct dentry + * encode_fh: encode a file handle fragment from a dentry + * get_name: find the name for a given inode in a given directory + * get_parent: find the parent of a given directory + * get_dentry: find a dentry for the inode given a file handle sub-fragment + * + * Description: + * The export_operations structure provides a means for nfsd to communicate + * with a particular exported file system - particularly enabling nfsd and + * the filesystem to co-operate when dealing with file handles. + * + * export_operations contains two basic operation for dealing with file handles, + * decode_fh() and encode_fh(), and allows for some other operations to be defined + * which standard helper routines use to get specific information from the + * filesystem. + * + * nfsd encodes information use to determine which filesystem a filehandle + * applies to in the initial part of the file handle. The remainder, termed a + * file handle fragment, is controlled completely by the filesystem. + * The standard helper routines assume that this fragment will contain one or two + * sub-fragments, one which identifies the file, and one which may be used to + * identify the (a) directory containing the file. + * + * In some situations, nfsd needs to get a dentry which is connected into a + * specific part of the file tree. To allow for this, it passes the function + * acceptable() together with a @context which can be used to see if the dentry + * is acceptable. As there can be multiple dentrys for a given file, the filesystem + * should check each one for acceptability before looking for the next. As soon + * as an acceptable one is found, it should be returned. + * + * decode_fh: + * @decode_fh is given a &struct super_block (@sb), a file handle fragment (@fh, @fh_len) + * and an acceptability testing function (@acceptable, @context). It should return + * a &struct dentry which refers to the same file that the file handle fragment refers + * to, and which passes the acceptability test. If it cannot, it should return + * a %NULL pointer if the file was found but no acceptable &dentries were available, or + * a %ERR_PTR error code indicating why it couldn't be found (e.g. %ENOENT or %ENOMEM). + * + * encode_fh: + * @encode_fh should store in the file handle fragment @fh (using at most @max_len bytes) + * information that can be used by @decode_fh to recover the file refered to by the + * &struct dentry @de. If the @connectable flag is set, the encode_fh() should store + * sufficient information so that a good attempt can be made to find not only + * the file but also it's place in the filesystem. This typically means storing + * a reference to de->d_parent in the filehandle fragment. + * encode_fh() should return the number of bytes stored or a negative error code + * such as %-ENOSPC + * + * get_name: + * @get_name should find a name for the given @child in the given @parent directory. + * The name should be stored in the @name (with the understanding that it is already + * pointing to a a %NAME_MAX+1 sized buffer. get_name() should return %0 on success, + * a negative error code or error. + * @get_name will be called without @parent->i_sem held. + * + * get_parent: + * @get_parent should find the parent directory for the given @child which is also + * a directory. In the event that it cannot be found, or storage space cannot be + * allocated, a %ERR_PTR should be returned. + * + * get_dentry: + * Given a &super_block (@sb) and a pointer to a file-system specific inode identifier, + * possibly an inode number, (@inump) get_dentry() should find the identified inode and + * return a dentry for that inode. + * Any suitable dentry can be returned including, if necessary, a new dentry created + * with d_alloc_root. The caller can then find any other extant dentrys by following the + * d_alias links. If a new dentry was created using d_alloc_root, DCACHE_NFSD_DISCONNECTED + * should be set, and the dentry should be d_rehash()ed. + * + * If the inode cannot be found, either a %NULL pointer or an %ERR_PTR code can be returned. + * The @inump will be whatever was passed to nfsd_find_fh_dentry() in either the + * @obj or @parent parameters. + * + * Locking rules: + * get_parent is called with child->d_inode->i_sem down + * get_name is not (which is possibly inconsistent) + */ + +struct export_operations { + struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, + int (*acceptable)(void *context, struct dentry *de), + void *context); + int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len, + int connectable); + + /* the following are only called from the filesystem itself */ + int (*get_name)(struct dentry *parent, char *name, + struct dentry *child); + struct dentry * (*get_parent)(struct dentry *child); + struct dentry * (*get_dentry)(struct super_block *sb, void *inump); + + /* This is set by the exporting module to a standard helper */ + struct dentry * (*find_exported_dentry)( + struct super_block *sb, void *obj, void *parent, + int (*acceptable)(void *context, struct dentry *de), + void *context); + + +}; + + struct file_system_type { const char *name; int fs_flags; diff --git a/include/linux/ide.h b/include/linux/ide.h index 92ee18d5b00ce711459ee3308ce787ac6cb363f9..6fea8e5b510c005cdad45428b7f0a6f0cba48dc9 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -14,8 +14,8 @@ #include <linux/device.h> #include <linux/devfs_fs_kernel.h> #include <linux/interrupt.h> +#include <linux/bitops.h> #include <asm/hdreg.h> -#include <asm/bitops.h> /* * This is the multiple IDE interface driver, as evolved from hd.c. @@ -313,9 +313,11 @@ typedef struct ide_tag_info_s { #define IDE_CUR_AR(drive) (HWGROUP((drive))->rq->special) struct ide_settings_s; - -typedef struct ide_drive_s { - struct ata_channel *channel; /* parent pointer to the channel we are attached to */ +/* structure describing an ATA/ATAPI device */ +typedef +struct ata_device { + struct ata_channel * channel; + char name[6]; /* device name */ unsigned int usage; /* current "open()" count for drive */ char type; /* distingiush different devices: disk, cdrom, tape, floppy, ... */ @@ -324,11 +326,11 @@ typedef struct ide_drive_s { * could move this to the channel and many sync problems would * magically just go away. */ - request_queue_t queue; /* per device request queue */ + request_queue_t queue; /* per device request queue */ - struct list_head free_req; /* free ata requests */ + struct list_head free_req; /* free ata requests */ - struct ide_drive_s *next; /* circular list of hwgroup drives */ + struct ata_device *next; /* circular list of hwgroup drives */ /* Those are directly injected jiffie values. They should go away and * we should use generic timers instead!!! @@ -346,8 +348,6 @@ typedef struct ide_drive_s { byte retry_pio; /* retrying dma capable host in pio */ byte state; /* retry state */ byte unmask; /* flag: okay to unmask other irqs */ - byte slow; /* flag: slow data port */ - byte bswap; /* flag: byte swap data */ byte dsc_overlap; /* flag: DSC overlap */ unsigned waiting_for_dma: 1; /* dma currently in progress */ @@ -359,7 +359,6 @@ typedef struct ide_drive_s { unsigned removable : 1; /* 1 if need to do check_media_change */ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */ unsigned no_unmask : 1; /* disallow setting unmask bit */ - unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ unsigned nobios : 1; /* flag: do not probe bios for drive */ unsigned revalidate : 1; /* request revalidation */ unsigned atapi_overlap : 1; /* flag: ATAPI overlap (not supported) */ @@ -376,7 +375,6 @@ typedef struct ide_drive_s { byte mult_count; /* current multiple sector setting */ byte mult_req; /* requested multiple sector setting */ byte tune_req; /* requested drive tuning setting */ - byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ byte bad_wstat; /* used for ignoring WRERR_STAT */ byte nowerr; /* used for ignoring WRERR_STAT */ byte sect0; /* offset of first sector for DM6:DDO */ @@ -390,12 +388,18 @@ typedef struct ide_drive_s { unsigned long long capacity48; /* total number of sectors */ unsigned int drive_data; /* for use by tuneproc/selectproc as needed */ + /* FIXME: Those are properties of a channel and not a drive! Move them + * later there. + */ + byte slow; /* flag: slow data port */ + unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ + byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ + wait_queue_head_t wqueue; /* used to wait for drive in open() */ struct hd_driveid *id; /* drive model identification info */ struct hd_struct *part; /* drive partition table */ - char name[6]; /* drive name, such as "hda" */ struct ata_operations *driver; void *driver_data; /* extra driver data */ @@ -447,47 +451,6 @@ typedef enum { ide_dma_read, ide_dma_write, ide_dma_begin, typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *); -/* - * An ide_ideproc_t() performs CPU-polled transfers to/from a drive. - * Arguments are: the drive, the buffer pointer, and the length (in bytes or - * words depending on if it's an IDE or ATAPI call). - * - * If it is not defined for a controller, standard-code is used from ide.c. - * - * Controllers which are not memory-mapped in the standard way need to - * override that mechanism using this function to work. - * - */ -typedef enum { ideproc_ide_input_data, ideproc_ide_output_data, - ideproc_atapi_input_bytes, ideproc_atapi_output_bytes -} ide_ide_action_t; - -typedef void (ide_ideproc_t)(ide_ide_action_t, ide_drive_t *, void *, unsigned int); - -/* - * An ide_tuneproc_t() is used to set the speed of an IDE interface - * to a particular PIO mode. The "byte" parameter is used - * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255 - * indicates that the interface driver should "auto-tune" the PIO mode - * according to the drive capabilities in drive->id; - * - * Not all interface types support tuning, and not all of those - * support all possible PIO settings. They may silently ignore - * or round values as they see fit. - */ -typedef void (ide_tuneproc_t) (ide_drive_t *, byte); -typedef int (ide_speedproc_t) (ide_drive_t *, byte); - -/* - * This is used to provide support for strange interfaces - */ -typedef void (ide_selectproc_t) (ide_drive_t *); -typedef void (ide_resetproc_t) (ide_drive_t *); -typedef int (ide_quirkproc_t) (ide_drive_t *); -typedef void (ide_intrproc_t) (ide_drive_t *); -typedef void (ide_maskproc_t) (ide_drive_t *, int); -typedef void (ide_rw_proc_t) (ide_drive_t *, ide_dma_action_t); - enum { ATA_PRIMARY = 0, ATA_SECONDARY = 1 @@ -507,15 +470,40 @@ struct ata_channel { #endif ide_drive_t drives[MAX_DRIVES]; /* drive info */ struct gendisk *gd; /* gendisk structure */ - ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */ - ide_speedproc_t *speedproc; /* routine to retune DMA modes for drives */ - ide_selectproc_t *selectproc; /* tweaks hardware to select drive */ - ide_resetproc_t *resetproc; /* routine to reset controller after a disk reset */ - ide_intrproc_t *intrproc; /* special interrupt handling for shared pci interrupts */ - ide_maskproc_t *maskproc; /* special host masking for drive selection */ - ide_quirkproc_t *quirkproc; /* check host's drive quirk list */ - ide_rw_proc_t *rwproc; /* adjust timing based upon rq->cmd direction */ - ide_ideproc_t *ideproc; /* CPU-polled transfer routine */ + + /* + * Routines to tune PIO and DMA mode for drives. + * + * A value of 255 indicates that the function should choose the optimal + * mode itself. + */ + void (*tuneproc) (ide_drive_t *, byte pio); + int (*speedproc) (ide_drive_t *, byte pio); + + /* tweaks hardware to select drive */ + void (*selectproc) (ide_drive_t *); + + /* routine to reset controller after a disk reset */ + void (*resetproc) (ide_drive_t *); + + /* special interrupt handling for shared pci interrupts */ + void (*intrproc) (ide_drive_t *); + + /* special host masking for drive selection */ + void (*maskproc) (ide_drive_t *, int); + + /* adjust timing based upon rq->cmd direction */ + void (*rwproc) (ide_drive_t *, ide_dma_action_t); + + /* check host's drive quirk list */ + int (*quirkproc) (ide_drive_t *); + + /* CPU-polled transfer routines */ + void (*ata_read)(ide_drive_t *, void *, unsigned int); + void (*ata_write)(ide_drive_t *, void *, unsigned int); + void (*atapi_read)(ide_drive_t *, void *, unsigned int); + void (*atapi_write)(ide_drive_t *, void *, unsigned int); + ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */ unsigned long dma_base; /* base addr for dma ports */ unsigned dma_extra; /* extra addr for dma ports */ @@ -829,7 +817,7 @@ struct ata_taskfile { */ struct ata_request { struct request *ar_rq; /* real request */ - struct ide_drive_s *ar_drive; /* associated drive */ + struct ata_device *ar_drive; /* associated drive */ unsigned long ar_flags; /* ATA_AR_* flags */ int ar_tag; /* tag number, if any */ struct list_head ar_queue; /* pending list */ @@ -848,12 +836,11 @@ struct ata_request { #define AR_TASK_CMD(ar) ((ar)->ar_task.taskfile.command) -void ata_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount); -void ata_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount); -void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount); -void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount); -void taskfile_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount); -void taskfile_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount); +extern void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount); +extern void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount); + +extern void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount); +extern void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount); extern ide_startstop_t ata_taskfile(ide_drive_t *drive, struct ata_taskfile *args, struct request *rq); @@ -988,6 +975,7 @@ extern void revalidate_drives(void); #define ATA_AR_QUEUED 1 #define ATA_AR_SETUP 2 #define ATA_AR_RETURN 4 +#define ATA_AR_STATIC 8 /* * if turn-around time is longer than this, halve queue depth @@ -1028,7 +1016,8 @@ static inline struct ata_request *ata_ar_get(ide_drive_t *drive) static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar) { - list_add(&ar->ar_queue, &drive->free_req); + if (!(ar->ar_flags & ATA_AR_STATIC)) + list_add(&ar->ar_queue, &drive->free_req); if (ar->ar_flags & ATA_AR_QUEUED) { /* clear the tag */ diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index f4b23c425760fa15410598aa8e51eaa173289db4..67bce357a7a107e0b4407693255a26ec4f9f6563 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -7,8 +7,8 @@ #include <linux/kernel.h> #include <linux/smp.h> #include <linux/cache.h> +#include <linux/bitops.h> -#include <asm/bitops.h> #include <asm/atomic.h> #include <asm/system.h> #include <asm/ptrace.h> diff --git a/include/linux/nls.h b/include/linux/nls.h index f47902bcfdb01d4a7f307c96cccbbdafee9c2aac..2ab6466f80435eb35fbb93f2472ed3b5b4180b90 100644 --- a/include/linux/nls.h +++ b/include/linux/nls.h @@ -18,7 +18,7 @@ struct nls_table { }; /* this value hold the maximum octet of charset */ -#define NLS_MAX_CHARSET_SIZE 3 +#define NLS_MAX_CHARSET_SIZE 6 /* for UTF-8 */ /* nls.c */ extern int register_nls(struct nls_table *); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1872896adcafd855bad2fbee062159ce9d7629db..a8e6b78f3c6c9e0d7288223c1f77a69ca73fc5c9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -510,6 +510,9 @@ #define PCI_DEVICE_ID_HP_DIVA1 0x1049 #define PCI_DEVICE_ID_HP_DIVA2 0x104A #define PCI_DEVICE_ID_HP_SP2_0 0x104B +#define PCI_DEVICE_ID_HP_ZX1_SBA 0x1229 +#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a +#define PCI_DEVICE_ID_HP_ZX1_LBA 0x122e #define PCI_VENDOR_ID_PCTECH 0x1042 #define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 diff --git a/include/linux/prctl.h b/include/linux/prctl.h index c523963c02190a0878e281ca4777460ec74c57cd..bbe6c00bfbd38747f1dea6e301e6866c0903d3e4 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -20,4 +20,10 @@ #define PR_GET_KEEPCAPS 7 #define PR_SET_KEEPCAPS 8 +/* Get/set floating-point emulation control bits (if meaningful) */ +#define PR_GET_FPEMU 9 +#define PR_SET_FPEMU 10 +# define PR_FPEMU_NOPRINT 1 /* silently emulate fp operations accesses */ +# define PR_FPEMU_SIGFPE 2 /* don't emulate fp operations, send SIGFPE instead */ + #endif /* _LINUX_PRCTL_H */ diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index 07e24c4dc71eff6016aadcf6a5db7f213ca664f1..9ec6c82cd7a60ed0863136095bffd820e361a9f5 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -23,7 +23,7 @@ #include <linux/major.h> #include <linux/ioctl.h> #include <linux/types.h> -#include <asm/bitops.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/hdreg.h> #include <linux/proc_fs.h> diff --git a/include/linux/signal.h b/include/linux/signal.h index c4e6eb3bd2ca90ad1f85c66e277e573fde2c9f58..c728eae4f9a5ddd15f255af64be33fad07e009eb 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -24,9 +24,9 @@ struct sigpending { */ #ifndef __HAVE_ARCH_SIG_BITOPS -#include <asm/bitops.h> +#include <linux/bitops.h> -/* We don't use <asm/bitops.h> for these because there is no need to +/* We don't use <linux/bitops.h> for these because there is no need to be atomic. */ static inline void sigaddset(sigset_t *set, int _sig) { diff --git a/include/linux/smp.h b/include/linux/smp.h index 8fa9b49f0961195663ae49f3a92af02d44bfe001..85c6273694c9b3bdd4c71344d7cdacac21aec349 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/compiler.h> +#include <linux/threads.h> #include <asm/smp.h> /* diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index 926186379545a8402429b07d8fcde9972e4da605..9beb0f922efc96e724398ed952cdbd0373c46bb4 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -7,8 +7,8 @@ #ifndef _LINUX_THREAD_INFO_H #define _LINUX_THREAD_INFO_H +#include <linux/bitops.h> #include <asm/thread_info.h> -#include <asm/bitops.h> #ifdef __KERNEL__ diff --git a/include/linux/tqueue.h b/include/linux/tqueue.h index 4a730f0ad90c36dc4e0a2a48b89a78f29ca6fdf1..5c518e91a8be62f0d568bd275105358b13032c3e 100644 --- a/include/linux/tqueue.h +++ b/include/linux/tqueue.h @@ -15,7 +15,7 @@ #include <linux/spinlock.h> #include <linux/list.h> -#include <asm/bitops.h> +#include <linux/bitops.h> #include <asm/system.h> /* diff --git a/include/linux/usb.h b/include/linux/usb.h index 9849c917c9ace70ea5bf848564aee8cd00ca0535..f06fafb62ee1133d89840d5e38ac4ce2fb277a59 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -890,7 +890,6 @@ extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); -extern int usb_make_path(struct usb_device *dev, char *buf, size_t size); /* * timeouts, in seconds, used for sending/receiving control messages @@ -917,6 +916,7 @@ struct usb_operations; */ struct usb_bus { int busnum; /* Bus number (in order of reg) */ + char *bus_name; /* stable id (PCI slot_name etc) */ #ifdef DEVNUM_ROUND_ROBIN int devnum_next; /* Next open device number in round-robin allocation */ @@ -1087,6 +1087,37 @@ const struct usb_device_id *usb_match_id(struct usb_device *dev, struct usb_interface *interface, const struct usb_device_id *id); +/** + * usb_make_path - returns stable device path in the usb tree + * @dev: the device whose path is being constructed + * @buf: where to put the string + * @size: how big is "buf"? + * + * Returns length of the string (> 0) or negative if size was too small. + * + * This identifier is intended to be "stable", reflecting physical paths in + * hardware such as physical bus addresses for host controllers or ports on + * USB hubs. That makes it stay the same until systems are physically + * reconfigured, by re-cabling a tree of USB devices or by moving USB host + * controllers. Adding and removing devices, including virtual root hubs + * in host controller driver modules, does not change these path identifers; + * neither does rebooting or re-enumerating. These are more useful identifiers + * than changeable ("unstable") ones like bus numbers or device addresses. + * + * With a partial exception for devices connected to USB 2.0 root hubs, these + * identifiers are also predictable: so long as the device tree isn't changed, + * plugging any USB device into a given hub port always gives it the same path. + * Because of the use of "companion" controllers, devices connected to ports on + * USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are + * high speed, and a different one if they are full or low speed. + */ +static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size) +{ + int actual; + actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath); + return (actual >= size) ? -1 : actual; +} + /* -------------------------------------------------------------------------- */ /* diff --git a/include/sound/asound.h b/include/sound/asound.h index 3c7df6d5964916b549d436449af934fa6de46092..8ea3e38a656b6119dda37ef96d5aaa80d89374c2 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -30,6 +30,7 @@ #ifdef __KERNEL__ #include <linux/types.h> +#include <linux/time.h> #include <asm/byteorder.h> #if __LITTLE_ENDIAN == 1234 diff --git a/include/sound/core.h b/include/sound/core.h index 94590bff56fbcff0b1fc0ef7d3259a9055793dd8..4374ac83c750966660b612e8054d5410e1cd5e3a 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -252,6 +252,7 @@ void *snd_malloc_isa_pages_fallback(unsigned long size, dma_addr_t *dma_addr, un #define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) #endif #ifdef CONFIG_PCI +struct pci_dev; void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr); void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size); void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 3140dc6e2e2c57f16c0d88823375500911e517e6..f2c10a8db17095c641e3be2654a3860faa7ba6a7 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -162,6 +162,8 @@ EXPORT_SYMBOL(d_invalidate); /* May be it will be better in dcache.h? */ EXPORT_SYMBOL(d_move); EXPORT_SYMBOL(d_instantiate); EXPORT_SYMBOL(d_alloc); +EXPORT_SYMBOL(d_alloc_anon); +EXPORT_SYMBOL(d_splice_alias); EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(__d_path); EXPORT_SYMBOL(mark_buffer_dirty); @@ -240,6 +242,7 @@ EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(prune_dcache); EXPORT_SYMBOL(shrink_dcache_sb); EXPORT_SYMBOL(shrink_dcache_parent); +EXPORT_SYMBOL(shrink_dcache_anon); EXPORT_SYMBOL(find_inode_number); EXPORT_SYMBOL(is_subdir); EXPORT_SYMBOL(get_unused_fd); diff --git a/kernel/printk.c b/kernel/printk.c index 7d8e4a167222585155670c3e55a9c29032429ab7..6226e86fca326410bfc7ee2713f773f37e58857c 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -39,6 +39,10 @@ #define LOG_BUF_MASK (LOG_BUF_LEN-1) +#ifndef arch_consoles_callable +#define arch_consoles_callable() (1) +#endif + /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */ @@ -439,6 +443,14 @@ asmlinkage int printk(const char *fmt, ...) log_level_unknown = 1; } + if (!arch_consoles_callable()) { + /* + * On some architectures, the consoles are not usable + * on secondary CPUs early in the boot process. + */ + spin_unlock_irqrestore(&logbuf_lock, flags); + goto out; + } if (!down_trylock(&console_sem)) { /* * We own the drivers. We can drop the spinlock and let @@ -455,6 +467,7 @@ asmlinkage int printk(const char *fmt, ...) */ spin_unlock_irqrestore(&logbuf_lock, flags); } +out: return printed_len; } EXPORT_SYMBOL(printk); diff --git a/kernel/sched.c b/kernel/sched.c index bf8c9a91e0080db0fbccd5e413c83d42d73504ca..c9bde194aa0833952d9655929f75aed68d4c1b5d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1237,11 +1237,11 @@ asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param) /** * sys_sched_setaffinity - set the cpu affinity of a process * @pid: pid of the process - * @len: length of new_mask - * @new_mask: user-space pointer to the new cpu mask + * @len: length of the bitmask pointed to by user_mask_ptr + * @user_mask_ptr: user-space pointer to the new cpu mask */ asmlinkage int sys_sched_setaffinity(pid_t pid, unsigned int len, - unsigned long *new_mask_ptr) + unsigned long *user_mask_ptr) { unsigned long new_mask; task_t *p; @@ -1250,7 +1250,7 @@ asmlinkage int sys_sched_setaffinity(pid_t pid, unsigned int len, if (len < sizeof(new_mask)) return -EINVAL; - if (copy_from_user(&new_mask, new_mask_ptr, sizeof(new_mask))) + if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) return -EFAULT; new_mask &= cpu_online_map; @@ -1289,8 +1289,8 @@ asmlinkage int sys_sched_setaffinity(pid_t pid, unsigned int len, /** * sys_sched_getaffinity - get the cpu affinity of a process * @pid: pid of the process - * @len: length of the new mask - * @user_mask_ptr: userspace pointer to the mask + * @len: length of the bitmask pointed to by user_mask_ptr + * @user_mask_ptr: user-space pointer to hold the current cpu mask */ asmlinkage int sys_sched_getaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr) @@ -1649,6 +1649,7 @@ void set_cpus_allowed(task_t *p, unsigned long new_mask) if (!new_mask) BUG(); + preempt_disable(); rq = task_rq_lock(p, &flags); p->cpus_allowed = new_mask; /* @@ -1657,7 +1658,7 @@ void set_cpus_allowed(task_t *p, unsigned long new_mask) */ if (new_mask & (1UL << p->thread_info->cpu)) { task_rq_unlock(rq, &flags); - return; + goto out; } init_MUTEX_LOCKED(&req.sem); @@ -1667,6 +1668,8 @@ void set_cpus_allowed(task_t *p, unsigned long new_mask) wake_up_process(rq->migration_thread); down(&req.sem); +out: + preempt_enable(); } static volatile unsigned long migration_mask; diff --git a/kernel/sys.c b/kernel/sys.c index 69d29b4ad2738f1f92d0d5933ed6a17fcf7bc6da..9ff907a46c55e7d7815556ee4ddbba39b0e08a71 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -21,6 +21,19 @@ #include <asm/io.h> #include <asm/unistd.h> +#ifndef SET_UNALIGN_CTL +# define SET_UNALIGN_CTL(a,b) (-EINVAL) +#endif +#ifndef GET_UNALIGN_CTL +# define GET_UNALIGN_CTL(a,b) (-EINVAL) +#endif +#ifndef SET_FPEMU_CTL +# define SET_FPEMU_CTL(a,b) (-EINVAL) +#endif +#ifndef GET_FPEMU_CTL +# define GET_FPEMU_CTL(a,b) (-EINVAL) +#endif + /* * this is where the system-wide overflow UID and GID are defined, for * architectures that now have 32-bit UID/GID but didn't in the past @@ -1240,20 +1253,18 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, } current->mm->dumpable = arg2; break; - case PR_SET_UNALIGN: -#ifdef SET_UNALIGN_CTL + + case PR_SET_UNALIGN: error = SET_UNALIGN_CTL(current, arg2); -#else - error = -EINVAL; -#endif break; - - case PR_GET_UNALIGN: -#ifdef GET_UNALIGN_CTL + case PR_GET_UNALIGN: error = GET_UNALIGN_CTL(current, arg2); -#else - error = -EINVAL; -#endif + break; + case PR_SET_FPEMU: + error = SET_FPEMU_CTL(current, arg2); + break; + case PR_GET_FPEMU: + error = GET_FPEMU_CTL(current, arg2); break; case PR_GET_KEEPCAPS: diff --git a/lib/radix-tree.c b/lib/radix-tree.c index aa33c677df7e86d79a55cb29e461623c3b6773a9..626c777107245c84f5e945571cfa47b4f4d637f4 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -49,11 +49,27 @@ struct radix_tree_path { static kmem_cache_t *radix_tree_node_cachep; static mempool_t *radix_tree_node_pool; -#define radix_tree_node_alloc(root) \ - mempool_alloc(radix_tree_node_pool, (root)->gfp_mask) -#define radix_tree_node_free(node) \ - mempool_free((node), radix_tree_node_pool); +/* + * mempool scribbles on the first eight bytes of the managed + * memory. Here we implement a temp workaround for that. + */ +#include <linux/list.h> +static inline struct radix_tree_node * +radix_tree_node_alloc(struct radix_tree_root *root) +{ + struct radix_tree_node *ret; + ret = mempool_alloc(radix_tree_node_pool, root->gfp_mask); + if (ret) + memset(ret, 0, sizeof(struct list_head)); + return ret; +} + +static inline void +radix_tree_node_free(struct radix_tree_node *node) +{ + mempool_free(node, radix_tree_node_pool); +} /* * Return the maximum key which can be store into a diff --git a/mm/swapfile.c b/mm/swapfile.c index 586f48b8e6f88a346b0e8a9450d0444be3ea3c01..f93135a3f2d2704580f954171cf59df6b6efb090 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1095,7 +1095,8 @@ void si_swapinfo(struct sysinfo *val) swap_list_lock(); for (i = 0; i < nr_swapfiles; i++) { unsigned int j; - if (!(swap_info[i].flags & SWP_USED)) + if (!(swap_info[i].flags & SWP_USED) || + (swap_info[i].flags & SWP_WRITEOK)) continue; for (j = 0; j < swap_info[i].max; ++j) { switch (swap_info[i].swap_map[j]) { diff --git a/mm/vmscan.c b/mm/vmscan.c index 0d730d16afb3f18d3840cd6afd03db68551c59f4..dc7263432fedb67f17bb21b02ff56de2cc8937ae 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -34,6 +34,29 @@ */ #define DEF_PRIORITY (6) +/* + * On the swap_out path, the radix-tree node allocations are performing + * GFP_ATOMIC allocations under PF_MEMALLOC. They can completely + * exhaust the page allocator. This is bad; some pages should be left + * available for the I/O system to start sending the swapcache contents + * to disk. + * + * So PF_MEMALLOC is dropped here. This causes the slab allocations to fail + * earlier, so radix-tree nodes will then be allocated from the mempool + * reserves. + */ +static inline int +swap_out_add_to_swap_cache(struct page *page, swp_entry_t entry) +{ + int flags = current->flags; + int ret; + + current->flags &= ~PF_MEMALLOC; + ret = add_to_swap_cache(page, entry); + current->flags = flags; + return ret; +} + /* * The swap-out function returns 1 if it successfully * scanned all the pages it was asked to (`count'). @@ -139,7 +162,7 @@ static inline int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* * (adding to the page cache will clear the dirty * and uptodate bits, so we need to do it again) */ - switch (add_to_swap_cache(page, entry)) { + switch (swap_out_add_to_swap_cache(page, entry)) { case 0: /* Success */ SetPageUptodate(page); set_page_dirty(page); diff --git a/net/atm/Makefile b/net/atm/Makefile index bf0a67b9ae37bd1d0e014d735694a39b085f9d47..3c2b16ab1f7342787d1e62204f0fde8b2b663805 100644 --- a/net/atm/Makefile +++ b/net/atm/Makefile @@ -1,17 +1,11 @@ # # Makefile for the ATM Protocol Families. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := atm.o export-objs := common.o atm_misc.o raw.o resources.o ipcommon.o proc.o -list-multi := mpoa.o mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o obj-$(CONFIG_ATM) := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o @@ -36,6 +30,3 @@ obj-$(CONFIG_ATM_MPOA) += mpoa.o obj-$(CONFIG_PPPOATM) += pppoatm.o include $(TOPDIR)/Rules.make - -mpoa.o: $(mpoa-objs) - $(LD) -r -o mpoa.o $(mpoa-objs) diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 013523ab4beeb6353a5e991e81f3439a2ebf2b34..536f9d4fba0b31d65cfc9af98e5726dc85feb313 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -1,9 +1,9 @@ # # Makefile for the Bluetooth subsystem # + O_TARGET := bluetooth.o -list-multi := hci.o l2cap.o export-objs := syms.o hci-objs := af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o l2cap-objs := l2cap_core.o l2cap_proc.o @@ -12,9 +12,3 @@ obj-$(CONFIG_BLUEZ) += hci.o obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o include $(TOPDIR)/Rules.make - -hci.o: $(hci-objs) - $(LD) -r -o $@ $(hci-objs) - -l2cap.o: $(l2cap-objs) - $(LD) -r -o $@ $(l2cap-objs) diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 90c33ec64130379659d034069ba936f2f1c20a11..5c72051c5e6e85076f048d0bb7722c4ea6ee6b22 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -1,19 +1,11 @@ # # Makefile for the netfilter modules on top of IPv4. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := netfilter.o export-objs = ip_conntrack_standalone.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o -# Multipart objects. -list-multi := ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o - # objects for the conntrack and NAT core (used by standalone and backw. compat) ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o ip_nf_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o @@ -95,15 +87,3 @@ obj-$(CONFIG_IP_NF_COMPAT_IPFWADM) += ipfwadm.o obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o include $(TOPDIR)/Rules.make - -ip_conntrack.o: $(ip_conntrack-objs) - $(LD) -r -o $@ $(ip_conntrack-objs) - -iptable_nat.o: $(iptable_nat-objs) - $(LD) -r -o $@ $(iptable_nat-objs) - -ipfwadm.o: $(ipfwadm-objs) - $(LD) -r -o $@ $(ipfwadm-objs) - -ipchains.o: $(ipchains-objs) - $(LD) -r -o $@ $(ipchains-objs) diff --git a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile index 12c52cf24ca88a5f3d30ba582d53ae761810206c..fe375e622bc4dd9b8ae8c7cb370730492f334890 100644 --- a/net/irda/ircomm/Makefile +++ b/net/irda/ircomm/Makefile @@ -1,15 +1,9 @@ # # Makefile for the Linux IrDA IrCOMM protocol layer. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := ircomm_and_tty.o -list-multi := ircomm.o ircomm-tty.o ircomm-objs := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o ircomm-tty-objs := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o @@ -17,9 +11,3 @@ obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o include $(TOPDIR)/Rules.make -ircomm.o: $(ircomm-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(ircomm-objs) - -ircomm-tty.o: $(ircomm-tty-objs) - $(LD) $(LD_RFLAG) -r -o $@ $(ircomm-tty-objs) - diff --git a/sound/core/device.c b/sound/core/device.c index 39ee1570a14cbdcfe9f88d592b74ef7bcbf7de3c..031277e3db11c14f04c934aa977083d326c28eb7 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <sound/core.h> +#include <linux/errno.h> int snd_device_new(snd_card_t *card, snd_device_type_t type, void *device_data, snd_device_ops_t *ops) diff --git a/sound/core/memory.c b/sound/core/memory.c index 25664f8bf664af2d1e706d1109acf8bcf334a311..6cc7093972e81cc49c44556b6efdb1cfa4925ff1 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/pci.h> #include <sound/core.h> #include <sound/info.h> diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index fe259ff683606e951cceffc7ab72ce35931dd45f..80c925cf92e64b0bc80bb6b9a25e74f071193c41 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -20,6 +20,7 @@ #include <sound/driver.h> #include <linux/init.h> +#include <linux/slab.h> #include <sound/core.h> #include "seq_clientmgr.h" #include <sound/initval.h> diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index d451effca18f7b4ad31d0e3324d0c3e9ffa0bd2c..85f915ee50bb5e0e0d1ec1e49e80432f055cb1b5 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -29,6 +29,8 @@ Possible options for midisynth module: #include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/errno.h> +#include <asm/semaphore.h> #include <sound/core.h> #include <sound/rawmidi.h> #include <sound/seq_kernel.h> diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index 7ebf0514b16b9c064d000027e45ed55a4d53916f..ccdb510492567c4ac0e4d89ffed424fa6fd52874 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -21,6 +21,7 @@ #include <sound/driver.h> #include <linux/slab.h> +#include <linux/errno.h> #include <sound/core.h> #include <sound/seq_kernel.h> #include <sound/seq_midi_event.h> diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 7cd9beb919097f8bd6a00736ad4bd67f08edf48d..a3c7bb213553c72bfc5ae0e02e6e8b5c853e8e50 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -544,10 +544,10 @@ int snd_seq_queue_use(int queueid, int client, int use) return -EINVAL; down(&queue->timer_mutex); if (use) { - if (!test_and_set_bit(client, &queue->clients_bitmap)) + if (!test_and_set_bit(client, queue->clients_bitmap)) queue->clients++; } else { - if (test_and_clear_bit(client, &queue->clients_bitmap)) + if (test_and_clear_bit(client, queue->clients_bitmap)) queue->clients--; } if (queue->clients) { @@ -575,7 +575,7 @@ int snd_seq_queue_is_used(int queueid, int client) q = queueptr(queueid); if (q == NULL) return -EINVAL; /* invalid queue */ - result = test_bit(client, &q->clients_bitmap) ? 1 : 0; + result = test_bit(client, q->clients_bitmap) ? 1 : 0; queuefree(q); return result; } diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h index 82139c43e9bf3230ca9880713ce6562cc668c752..1bb37e036c93e1b6c60acbdca735b95f193ed69c 100644 --- a/sound/core/seq/seq_queue.h +++ b/sound/core/seq/seq_queue.h @@ -51,7 +51,7 @@ struct _snd_seq_queue { spinlock_t check_lock; /* clients which uses this queue (bitmap) */ - unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)]; + unsigned long clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned long)]; unsigned int clients; /* users of this queue */ struct semaphore timer_mutex; diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index b399150e2a6fcac715f0807f1a6f1aed52cc3cc8..aa7b7ab62844fac5cdd1bc467f5c6497796fbade 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -29,6 +29,8 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/errno.h> #include <sound/core.h> #include <sound/mpu401.h>